05-Arrays, Slices, and Maps、 In memory、 Array、 Slice、 fence post error、 Compare Array and Slice 、 Map、 Built in functi... ...
課程地址 go-class-slides/xmas-2020 at trunk · matt4biz/go-class-slides (github.com)
主講老師 Matt Holiday
05-Arrays, Slices, and Maps
In memory
string、array、slice 在記憶體中是連續存儲的,map不是連續存儲的。
Array
在創建數組的時候需要指定大小,如果不指定需要使用 ... ,圖中 a、b 將是固定的 24 位元組對象(int在64位操作系統上預設為int64),一旦設定不能改變。
d=b
中,由於數組只是一塊記憶體,並不是像字元串那樣的描述符,我們只是物理地複製了位元組。當數組大小不一致時,無法進行拷貝複製。
Slice
切片有描述符,指向一個特定的記憶體地址。它的工作方式類似於字元串的工作方式。
切片描述符包含 data、len、capacity。
append
方法需要把返回值重新賦給 a,假設 a 指向的記憶體區域已經滿了,再添加元素就要開闢新的更大的記憶體區域存放。
a=b
表示 b 描述符的內容被拷貝到 a 描述符中。
e:=a
新建一個描述符,內容與 a 描述符內的一致。
切片可以被切片(截取)操作,就像從字元串(前面的os.Args[1:])中取出切片,從切片數組切片等。
package main
import "fmt"
func main() {
t := []byte("string")
fmt.Println(len(t), t)
fmt.Println(t[2])
fmt.Println(t[:2])
fmt.Println(t[2:])
fmt.Println(t[3:5], len(t[3:5]))
}
6 [115 116 114 105 110 103]
114
[115 116]
[114 105 110 103]
[105 110] 2
fence post error
柵欄柱錯誤:假設我有三個柵欄部分,我必須有四個柵欄在他們旁邊將它們固定住。(不懂直接看圖)
Compare Array、Slice
切片可以是任意長度,而且大部分 Go 的標準庫使用切片作為參數。
切片是不能進行比較的,想進行比較可以使用數組。這也導致切片不能作為 Map Key。
數組可以作為一些演算法必備的數組。大小固定,值不改變。近似於偽常量。註意,不能添加 const 常量關鍵字,只有數字,字元串,布爾值可以作為常量。
Example
a[0]=4
因為 a 只是 w 的值拷貝(數組),所以修改後 w 並沒有被修改。
b[0]=3
將會使 x 修改,因為兩者 data 都指向同一個記憶體地址。(但是要註意,這是值拷貝,如果添加元素過多,會導致 b 的 data 指針使用新的記憶體地址而 x 還是指向原來的)
copy(c, b)
函數不會因為切片大小不同出錯,會儘可能把 b 切片中的元素拷貝到 c 中。
我們可以對數組切片如 z := a[0:2]
z 將是一個切片,指向 a 的前兩個元素,go 會自動提供數組來保存。
Map
假設要計算一個文件中不同單詞出現的次數,就可以使用 Maps。是一個 Hash table。
m 是一個描述符,但是整體為空。 p 的 data 指針指向一個哈希表。
map 與 map 間不能進行比較,只能進行 nil 比較。
可以查看 map 的長度,不能查看 map 的容量。
可以通過獲取第二個參數判斷鍵值對是否存在。
Built in functions
Make nil useful
由於 len、cap、range 這些內建函數是安全的,我們不需要 if 判斷 nil 就可以直接使用。
range 將會跳過 nil、empty 的迴圈對象。
Quote
一種不影響你思考編程的方式的語言是不值得瞭解的
Practice
編寫一個段落單詞計數器,輸出前三個出現次數最多的單詞。
main.go
package main
import (
"bufio"
"fmt"
"os"
"sort"
)
func main() {
scan := bufio.NewScanner(os.Stdin)
words := make(map[string]int)
// ^ 預設是按行讀取,所以手動指定按單詞讀取
scan.Split(bufio.ScanWords)
for scan.Scan() {
words[scan.Text()]++
}
fmt.Println(len(words), "unique words")
type kv struct {
key string
val int
}
var ss []kv
for k, v := range words {
ss = append(ss, kv{k, v})
}
// ^ 直接修改原切片
sort.Slice(ss, func(i, j int) bool {
return ss[i].val > ss[j].val
})
for _, s := range ss[:3] {
fmt.Println(s.key, "appears", s.val, "times")
}
}
scan.Split(bufio.ScanWords)
Scanner 預設是按行讀取,所以手動指定按單詞讀取。
kv{k, v}
結構體的初始化
sort.Slice
函數直接修改原切片,傳入的函數在 return
前面的元素排在切片的前面。如左>右,則大的元素在切片最前面,屬於降序排序。
test.txt
matt went to greece
where did matt go
alan went to rome
matt didn't go there
第一行是空行是有原因的,這是 BOM頭(Byte Order Mark) 導致的,具體請看另一篇文章
重定向管道流讀取TXT文本第一次讀取為""空字元串 - 小能日記 - 博客園 (cnblogs.com)
result
cat test.txt | go run .
12 unique words
matt appears 3 times
to appears 2 times
go appears 2 times