ch4-1 review

This commit is contained in:
chai2010 2016-01-03 20:55:55 +08:00
parent 9787892cb0
commit 469d4a9db0

View File

@ -1,8 +1,8 @@
## 4.1. 數組 ## 4.1. 數組
數組是一個固定長度的特定類型元素組成的序列,有零個或多個元素。因爲數組的長度是固定的因此在Go語言中很少直接使用數組。Slice切片是可以增長和收縮動態序列slice功能也更靈活但是要理解slice工作原理的話需要先理解數組。 數組是一個固定長度的特定類型元素組成的序列,一個數組可以由零個或多個元素組成。因爲數組的長度是固定的因此在Go語言中很少直接使用數組。Slice切片是可以增長和收縮動態序列slice功能也更靈活但是要理解slice工作原理的話需要先理解數組。
數組的每個元素可以通過下標來訪問下標的范圍是從0開始到數組長度減1的位置。內置的len函數將返迴數組中元素的個數。 數組的每個元素可以通過索引下標來訪問,索引下標的范圍是從0開始到數組長度減1的位置。內置的len函數將返迴數組中元素的個數。
```Go ```Go
var a [3]int // array of 3 integers var a [3]int // array of 3 integers
@ -20,7 +20,7 @@ for _, v := range a {
} }
``` ```
默認情況下數組的每個元素都被初始化爲元素類型對應的零值對於數字類型來説就是0。我們可以使用數組字面值語法用一組值來初始化數組 默認情況下數組的每個元素都被初始化爲元素類型對應的零值對於數字類型來説就是0。我們可以使用數組字面值語法用一組值來初始化數組:
```Go ```Go
var q [3]int = [3]int{1, 2, 3} var q [3]int = [3]int{1, 2, 3}
@ -28,21 +28,21 @@ var r [3]int = [3]int{1, 2}
fmt.Println(r[2]) // "0" fmt.Println(r[2]) // "0"
``` ```
在數組字面值中,如果數組的長度位置出現的是“...”省略號則表示數組的長度是根據初始化值的個數來計算的。上面q數組的定義可以簡化爲 在數組字面值中,如果數組的長度位置出現的是“...”省略號,則表示數組的長度是根據初始化值的個數來計算的。因此,上面q數組的定義可以簡化爲
```Go ```Go
q := [...]int{1, 2, 3} q := [...]int{1, 2, 3}
fmt.Printf("%T\n", q) // "[3]int" fmt.Printf("%T\n", q) // "[3]int"
``` ```
數組的長度是數組類型的一個組成部分,因此[3]int和[4]int是兩種不同的數組類型。數組的長度必鬚是常量表達式因爲數組的長度需要在程序的編譯階段確定。 數組的長度是數組類型的一個組成部分,因此[3]int和[4]int是兩種不同的數組類型。數組的長度必鬚是常量表達式因爲數組的長度需要在編譯階段確定。
```Go ```Go
q := [3]int{1, 2, 3} q := [3]int{1, 2, 3}
q = [4]int{1, 2, 3, 4} // compile error: cannot assign [4]int to [3]int q = [4]int{1, 2, 3, 4} // compile error: cannot assign [4]int to [3]int
``` ```
我們將會發現數組、slice、map和結構體字面值的寫法都很相似。上面的形式是直接提順序供初始化值序列但是也可以指定一個索引和對應值列表初始化,像下面這樣: 我們將會發現數組、slice、map和結構體字面值的寫法都很相似。上面的形式是直接提順序供初始化值序列但是也可以指定一個索引和對應值列表的方式初始化,像下面這樣:
```Go ```Go
type Currency int type Currency int
@ -56,18 +56,18 @@ const (
symbol := [...]string{USD: "$", EUR: "€", GBP: "£", RMB: "¥"} symbol := [...]string{USD: "$", EUR: "€", GBP: "£", RMB: "¥"}
fmt.Println(RMB, symbol[RMB]) // "3 "" fmt.Println(RMB, symbol[RMB]) // "3 "
``` ```
這種形式的數組字面值形式中,初始化索引的順序是無關緊要的,而且一些索引可以省略,和前面提到的規則一樣,未知道初始值的元素將用零值初始化。例如, 這種形式的數組字面值形式中,初始化索引的順序是無關緊要的,而且沒用到的索引可以省略,和前面提到的規則一樣,未指定初始值的元素將用零值初始化。例如,
```Go ```Go
r := [...]int{99: -1} r := [...]int{99: -1}
``` ```
定義了一個含有100個原始的數組r最後一個元素初始化爲-1其它元素都是用0初始化。 定義了一個含有100個元素的數組r最後一個元素被初始化爲-1其它元素都是用0初始化。
如果一個數組的元素類型是可以相互比較的,那麽數組類型也是可以相互比較的,因此我們可以直接通過==比較運算符來比較兩個數組,隻有當兩個數組的所有元素都是相等的時候數組才是相等的。相對的是不相等比較運算符!=。 如果一個數組的元素類型是可以相互比較的,那麽數組類型也是可以相互比較的,這時候我們可以直接通過==比較運算符來比較兩個數組,隻有當兩個數組的所有元素都是相等的時候數組才是相等的。是不相等比較運算符!=遵循同樣的規則
```Go ```Go
a := [2]int{1, 2} a := [2]int{1, 2}
@ -78,7 +78,7 @@ d := [3]int{1, 2}
fmt.Println(a == d) // compile error: cannot compare [2]int == [3]int fmt.Println(a == d) // compile error: cannot compare [2]int == [3]int
``` ```
作爲一個更可信的例子crypto/sha256包的Sum256函數用於生成一個針對任意的字節類型的slice消息的摘要。消息摘要有256bit大小因此對應[32]byte數組類型。如果兩個消息摘要是相同的那麽可以認爲兩個消息本身也是相同譯註理論上有HASH碼碰撞的清空,但是實際應用可以基本忽略);如果消息摘要不同,那麽消息本身比如也是不同的。下面的例子用SHA256算法分别生成“x”和“X”兩個信息的摘要 作爲一個眞實的例子crypto/sha256包的Sum256函數對一個任意的字節slice類型的數據生成一個對應的消息摘要。消息摘要有256bit大小因此對應[32]byte數組類型。如果兩個消息摘要是相同的那麽可以認爲兩個消息本身也是相同譯註理論上有HASH碼碰撞的情況,但是實際應用可以基本忽略);如果消息摘要不同,那麽消息本身必然也是不同的。下面的例子用SHA256算法分别生成“x”和“X”兩個信息的摘要
```Go ```Go
gopl.io/ch4/sha256 gopl.io/ch4/sha256
@ -97,11 +97,11 @@ func main() {
} }
``` ```
兩個消息雖然隻有一個字符的差異但是生成的消息摘要則幾乎有一半的bit位是不同的。需要註意Printf函數的%x參數它用於指定以十六進製的格式打印全部的數組或slice的的元素,%t參數是用於打印布爾型數據%T參數是用於顯示一個值對應的數據類型。 上面例子中,兩個消息雖然隻有一個字符的差異但是生成的消息摘要則幾乎有一半的bit位是不同的。需要註意Printf函數的%x參數它用於指定以十六進製的格式打印數組或slice全部的元素,%t參數是用於打印布爾型數據%T參數是用於顯示一個值對應的數據類型。
當調用一個函數的時候,函數的每個調用參數將會被賦值給函數本身的參數變量,所以函數參數變量接收的是一個複製的副本,併不是原始調用的參數。因爲函數參數傳遞的機製導致傳遞大的數組類型將是低效的,併且對數組參數的任何的脩改都是發生在複製的數組上,併不直接脩改調用時原始的數組變量。在這個方面Go語言對待數組的方式和其它很多編程語言不同其它編程語言可能會隱式地將數組作爲引用或指針對象傳入被調用的函數。 當調用一個函數的時候,函數的每個調用參數將會被賦值給函數內部的參數變量,所以函數參數變量接收的是一個複製的副本,併不是原始調用的參數。因爲函數參數傳遞的機製導致傳遞大的數組類型將是低效的,併且對數組參數的任何的脩改都是發生在複製的數組上,併不直接脩改調用時原始的數組變量。在這個方面Go語言對待數組的方式和其它很多編程語言不同其它編程語言可能會隱式地將數組作爲引用或指針對象傳入被調用的函數。
當然,我們可以顯式地傳入一個數組指針,那樣的話函數對數組的任何脩改都可以直接反饋到調用者。下面的函數用於給[32]byte類型的數組清零 當然,我們可以顯式地傳入一個數組指針,那樣的話函數通過指針對數組的任何脩改都可以直接反饋到調用者。下面的函數用於給[32]byte類型的數組清零
```Go ```Go
func zero(ptr *[32]byte) { func zero(ptr *[32]byte) {
@ -111,7 +111,7 @@ func zero(ptr *[32]byte) {
} }
``` ```
其實數組字面值[32]byte{}就可以生成一個32字節的數組。而且每個數組的元素都是零值初始化也就是0。我們可以將上面的zero函數寫的更簡潔一點 其實數組字面值[32]byte{}就可以生成一個32字節的數組。而且每個數組的元素都是零值初始化也就是0。因此,我們可以將上面的zero函數寫的更簡潔一點
```Go ```Go
func zero(ptr *[32]byte) { func zero(ptr *[32]byte) {
@ -119,10 +119,8 @@ func zero(ptr *[32]byte) {
} }
``` ```
雖然通過指針來傳遞數組參數是高效的,而且也允許在函數內部脩改數組的值,但是因爲數組依然是殭化的類型因爲數組的類型包含長度信息。zero函數併不能接收指向[16]byte類型數組的指針而且也沒有任何添加或刪除數組元素的方法。由於這些原因除了像SHA256這類需要處理特定大小數組的函數數組依然很少用作函數參數相反我們一般使用slice來替代數組。 雖然通過指針來傳遞數組參數是高效的,而且也允許在函數內部脩改數組的值,但是數組依然是殭化的類型,因爲數組的類型包含了殭化的長度信息。上面的zero函數併不能接收指向[16]byte類型數組的指針而且也沒有任何添加或刪除數組元素的方法。由於這些原因除了像SHA256這類需要處理特定大小數組的特例數組依然很少用作函數參數相反我們一般使用slice來替代數組。
**練習 4.1** 編寫一個函數計算兩個SHA256碼中不同bit的數目。參考2.6.2節的PopCount函數。) **練習 4.1** 編寫一個函數計算兩個SHA256哈希碼中不同bit的數目。參考2.6.2節的PopCount函數。)
**練習 4.2** 編寫一個程序默認打印標準輸入的以SHA256哈希碼也可以通過命令行標準參數選擇SHA384或SHA512哈希算法。 **練習 4.2** 編寫一個程序默認打印標準輸入的以SHA256哈希碼也可以通過命令行標準參數選擇SHA384或SHA512哈希算法。