mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2024-12-01 02:28:56 +00:00
5b4d690a48
Update ch2-06-2.md var pc [256]byte = func() (pc [256]byte) { for i := range pc { pc[i] = pc[i/2] + byte(i&1) } return }()
78 lines
3.5 KiB
Markdown
78 lines
3.5 KiB
Markdown
### 2.6.2. 包的初始化
|
||
|
||
包的初始化首先是解決包級變量的依賴順序,然後安照包級變量聲明出現的順序依次初始化:
|
||
|
||
```Go
|
||
var a = b + c // a 第三個初始化, 爲 3
|
||
var b = f() // b 第二個初始化, 爲 2, 通過調用 f (依賴c)
|
||
var c = 1 // c 第一個初始化, 爲 1
|
||
|
||
func f() int { return c + 1 }
|
||
```
|
||
|
||
如果包中含有多個.go源文件,它們將按照發給編譯器的順序進行初始化,Go語言的構建工具首先會將.go文件根據文件名排序,然後依次調用編譯器編譯。
|
||
|
||
對於在包級别聲明的變量,如果有初始化表達式則用表達式初始化,還有一些沒有初始化表達式的,例如某些表格數據初始化併不是一個簡單的賦值過程。在這種情況下,我們可以用一個特殊的init初始化函數來簡化初始化工作。每個文件都可以包含多個init初始化函數
|
||
|
||
```Go
|
||
func init() { /* ... */ }
|
||
```
|
||
|
||
這樣的init初始化函數除了不能被調用或引用外,其他行爲和普通函數類似。在每個文件中的init初始化函數,在程序開始執行時按照它們聲明的順序被自動調用。
|
||
|
||
每個包在解決依賴的前提下,以導入聲明的順序初始化,每個包隻會被初始化一次。因此,如果一個p包導入了q包,那麽在p包初始化的時候可以認爲q包必然已經初始化過了。初始化工作是自下而上進行的,main包最後被初始化。以這種方式,可以確保在main函數執行之前,所有依然的包都已經完成初始化工作了。
|
||
|
||
下面的代碼定義了一個PopCount函數,用於返迴一個數字中含二進製1bit的個數。它使用init初始化函數來生成輔助表格pc,pc表格用於處理每個8bit寬度的數字含二進製的1bit的bit個數,這樣的話在處理64bit寬度的數字時就沒有必要循環64次,隻需要8次査表就可以了。(這併不是最快的統計1bit數目的算法,但是它可以方便演示init函數的用法,併且演示了如果預生成輔助表格,這是編程中常用的技術)。
|
||
|
||
<u><i>gopl.io/ch2/popcount</i></u>
|
||
```Go
|
||
package popcount
|
||
|
||
// pc[i] is the population count of i.
|
||
var pc [256]byte
|
||
|
||
func init() {
|
||
for i := range pc {
|
||
pc[i] = pc[i/2] + byte(i&1)
|
||
}
|
||
}
|
||
|
||
// PopCount returns the population count (number of set bits) of x.
|
||
func PopCount(x uint64) int {
|
||
return int(pc[byte(x>>(0*8))] +
|
||
pc[byte(x>>(1*8))] +
|
||
pc[byte(x>>(2*8))] +
|
||
pc[byte(x>>(3*8))] +
|
||
pc[byte(x>>(4*8))] +
|
||
pc[byte(x>>(5*8))] +
|
||
pc[byte(x>>(6*8))] +
|
||
pc[byte(x>>(7*8))])
|
||
}
|
||
```
|
||
|
||
譯註:對於pc這類需要複雜處理的初始化,可以通過將初始化邏輯包裝爲一個匿名函數處理,像下面這樣:
|
||
|
||
```Go
|
||
// pc[i] is the population count of i.
|
||
var pc [256]byte = func() (pc [256]byte) {
|
||
for i := range pc {
|
||
pc[i] = pc[i/2] + byte(i&1)
|
||
}
|
||
return
|
||
}()
|
||
```
|
||
|
||
要註意的是在init函數中,range循環隻使用了索引,省略了沒有用到的值部分。循環也可以這樣寫:
|
||
|
||
```Go
|
||
for i, _ := range pc {
|
||
```
|
||
|
||
我們在下一節和10.5節還將看到其它使用init函數的地方。
|
||
|
||
**練習 2.3:** 重寫PopCount函數,用一個循環代替單一的表達式。比較兩個版本的性能。(11.4節將展示如何繫統地比較兩個不同實現的性能。)
|
||
|
||
**練習 2.4:** 用移位算法重寫PopCount函數,每次測試最右邊的1bit,然後統計總數。比較和査表算法的性能差異。
|
||
|
||
**練習 2.5:** 表達式`x&(x-1)`用於將x的最低的一個非零的bit位清零。使用這個算法重寫PopCount函數,然後比較性能。
|