Fixes #198
This commit is contained in:
chai2010
2016-01-18 11:22:04 +08:00
parent 884ada9cd0
commit 9666211cd7
71 changed files with 107 additions and 105 deletions

View File

@@ -43,7 +43,7 @@ io.Writer類型定義了函數Fprintf和這個函數調用者之間的約定。
因爲fmt.Fprintf函數沒有對具體操作的值做任何假設而是僅僅通過io.Writer接口的約定來保證行爲所以第一個參數可以安全地傳入一個任何具體類型的值隻需要滿足io.Writer接口。一個類型可以自由的使用另一個滿足相同接口的類型來進行替換被稱作可替換性(LSP里氏替換)。這是一個面向對象的特徵。
讓我們通過一個新的類型來進行校驗,下面\*ByteCounter類型里的Write方法僅僅在丟失寫向它的字節前統計它們的長度。(在這個+=賦值語句中讓len(p)的類型和\*c的類型匹配的轉換是必的。)
讓我們通過一個新的類型來進行校驗,下面\*ByteCounter類型里的Write方法僅僅在丟失寫向它的字節前統計它們的長度。(在這個+=賦值語句中讓len(p)的類型和\*c的類型匹配的轉換是必的。)
```go
// gopl.io/ch7/bytecounter

View File

@@ -23,7 +23,7 @@ rwc = w // compile error: io.Writer lacks Close method
因爲ReadWriter和ReadWriteCloser包含所有Writer的方法所以任何實現了ReadWriter和ReadWriteCloser的類型必定也實現了Writer接口
在進一步學習前,必先解釋表示一個類型持有一個方法當中的細節。迴想在6.2章中對於每一個命名過的具體類型T它一些方法的接收者是類型T本身然而另一些則是一個*T的指針。還記得在T類型的參數上調用一個*T的方法是合法的隻要這個參數是一個變量編譯器隱式的獲取了它的地址。但這僅僅是一個語法T類型的值不擁有所有*T指針的方法那這樣它就可能隻實現更少的接口。
在進一步學習前,必先解釋表示一個類型持有一個方法當中的細節。迴想在6.2章中對於每一個命名過的具體類型T它一些方法的接收者是類型T本身然而另一些則是一個*T的指針。還記得在T類型的參數上調用一個*T的方法是合法的隻要這個參數是一個變量編譯器隱式的獲取了它的地址。但這僅僅是一個語法T類型的值不擁有所有*T指針的方法那這樣它就可能隻實現更少的接口。
舉個例子可能會更清晰一點。在第6.5章中IntSet類型的String方法的接收者是一個指針類型所以我們不能在一個不能尋址的IntSet值上調用這個方法

View File

@@ -72,9 +72,9 @@ func (f *celsiusFlag) Set(s string) error {
}
```
調用fmt.Sscanf函數從輸入s中解析一個浮點數value和一個字符串unit。雖然通常必檢査Sscanf的錯誤返迴但是在這個例子中我們不需要因爲如果有錯誤發生就沒有switch case會匹配到。
調用fmt.Sscanf函數從輸入s中解析一個浮點數value和一個字符串unit。雖然通常必檢査Sscanf的錯誤返迴但是在這個例子中我們不需要因爲如果有錯誤發生就沒有switch case會匹配到。
下面的CelsiusFlag函數將所有邏輯都封裝在一起。它返迴一個內嵌在celsiusFlag變量f中的Celsius指針給調用者。Celsius字段是一個會通過Set方法在標記處理的過程中更新的變量。調用Var方法將標記加入應用的命令行標記集合中有異常複雜命令行接口的全局變量flag.CommandLine.Programs可能有幾個這個類型的變量。調用Var方法將一個*celsiusFlag參數賦值給一個flag.Value參數,導致編譯器去檢査*celsiusFlag是否有必的方法。
下面的CelsiusFlag函數將所有邏輯都封裝在一起。它返迴一個內嵌在celsiusFlag變量f中的Celsius指針給調用者。Celsius字段是一個會通過Set方法在標記處理的過程中更新的變量。調用Var方法將標記加入應用的命令行標記集合中有異常複雜命令行接口的全局變量flag.CommandLine.Programs可能有幾個這個類型的變量。調用Var方法將一個*celsiusFlag參數賦值給一個flag.Value參數,導致編譯器去檢査*celsiusFlag是否有必的方法。
```go
// CelsiusFlag defines a Celsius flag with the specified name,

View File

@@ -42,7 +42,7 @@ w = os.Stdout
w.Write([]byte("hello")) // "hello"
```
通常在編譯期,我們不知道接口值的動態類型是什麽,所以一個接口上的調用必使用動態分配。因爲不是直接進行調用,所以編譯器必把代碼生成在類型描述符的方法Write上然後間接調用那個地址。這個調用的接收者是一個接口動態值的拷貝os.Stdout。效果和下面這個直接調用一樣
通常在編譯期,我們不知道接口值的動態類型是什麽,所以一個接口上的調用必使用動態分配。因爲不是直接進行調用,所以編譯器必把代碼生成在類型描述符的方法Write上然後間接調用那個地址。這個調用的接收者是一個接口動態值的拷貝os.Stdout。效果和下面這個直接調用一樣
```go
os.Stdout.Write([]byte("hello")) // "hello"
@@ -93,7 +93,7 @@ var x interface{} = []int{1, 2, 3}
fmt.Println(x == x) // panic: comparing uncomparable type []int
```
考慮到這點,接口類型是非常與衆不同的。其它類型要麽是安全的可比較類型(如基本類型和指針)要麽是完全不可比較的類型(如切片,映射類型,和函數),但是在比較接口值或者包含了接口值的聚合類型時,我們必要意識到潛在的panic。同樣的風險也存在於使用接口作爲map的鍵或者switch的操作數。隻能比較你非常確定它們的動態值是可比較類型的接口值。
考慮到這點,接口類型是非常與衆不同的。其它類型要麽是安全的可比較類型(如基本類型和指針)要麽是完全不可比較的類型(如切片,映射類型,和函數),但是在比較接口值或者包含了接口值的聚合類型時,我們必要意識到潛在的panic。同樣的風險也存在於使用接口作爲map的鍵或者switch的操作數。隻能比較你非常確定它們的動態值是可比較類型的接口值。
當我們處理錯誤或者調試的過程中得知接口值的動態類型是非常有幫助的。所以我們使用fmt包的%T動作:

View File

@@ -80,7 +80,7 @@ func printTracks(tracks []*Track) {
}
```
爲了能按照Artist字段對播放列表進行排序我們會像對StringSlice那樣定義一個新的帶有必LenLess和Swap方法的切片類型。
爲了能按照Artist字段對播放列表進行排序我們會像對StringSlice那樣定義一個新的帶有必LenLess和Swap方法的切片類型。
```go
type byArtist []*Track
@@ -89,7 +89,7 @@ func (x byArtist) Less(i, j int) bool { return x[i].Artist < x[j].Artist }
func (x byArtist) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
```
爲了調用通用的排序程序,我們必先將tracks轉換爲新的byArtist類型它定義了具體的排序
爲了調用通用的排序程序,我們必先將tracks轉換爲新的byArtist類型它定義了具體的排序
```go
sort.Sort(byArtist(tracks))
@@ -137,7 +137,7 @@ func Reverse(data Interface) Interface { return reverse{data} }
reverse的另外兩個方法Len和Swap隱式地由原有內嵌的sort.Interface提供。因爲reverse是一個不公開的類型所以導出函數Reverse函數返迴一個包含原有sort.Interface值的reverse類型實例。
爲了可以按照不同的列進行排序,我們必定義一個新的類型例如byYear
爲了可以按照不同的列進行排序,我們必定義一個新的類型例如byYear
```go
type byYear []*Track

View File

@@ -175,7 +175,7 @@ mux.HandleFunc("/list", db.list)
mux.HandleFunc("/price", db.price)
```
從上面的代碼很容易看出應該怎麽構建一個程序它有兩個不同的web服務器監聽不同的端口的併且定義不同的URL將它們指派到不同的handler。我們隻要構建另外一個ServeMux併且在調用一次ListenAndServe可能併行的。但是在大多數程序中一個web服務器就足夠了。此外在一個應用程序的多個文件中定義HTTP handler也是非常典型的如果它們必全部都顯示的註冊到這個應用的ServeMux實例上會比較麻煩。
從上面的代碼很容易看出應該怎麽構建一個程序它有兩個不同的web服務器監聽不同的端口的併且定義不同的URL將它們指派到不同的handler。我們隻要構建另外一個ServeMux併且在調用一次ListenAndServe可能併行的。但是在大多數程序中一個web服務器就足夠了。此外在一個應用程序的多個文件中定義HTTP handler也是非常典型的如果它們必全部都顯示的註冊到這個應用的ServeMux實例上會比較麻煩。
所以爲了方便net/http包提供了一個全局的ServeMux實例DefaultServerMux和包級别的http.Handle和http.HandleFunc函數。現在爲了使用DefaultServeMux作爲服務器的主handler我們不需要將它傳給ListenAndServe函數nil值就可以工作。

View File

@@ -50,7 +50,7 @@ type call struct {
type Env map[Var]float64
```
我們也需要每個表示式去定義一個Eval方法這個方法會根據給定的environment變量返迴表達式的值。因爲每個表達式都必提供這個方法我們將它加入到Expr接口中。這個包隻會對外公開ExprEnv和Var類型。調用方不需要獲取其它的表達式類型就可以使用這個求值器。
我們也需要每個表示式去定義一個Eval方法這個方法會根據給定的environment變量返迴表達式的值。因爲每個表達式都必提供這個方法我們將它加入到Expr接口中。這個包隻會對外公開ExprEnv和Var類型。調用方不需要獲取其它的表達式類型就可以使用這個求值器。
```go
type Expr interface {
@@ -248,7 +248,7 @@ log(10) unknown function "log"
sqrt(1, 2) call to sqrt has 2 args, want 1
```
Check方法的參數是一個Var類型的集合這個集合聚集從表達式中找到的變量名爲了保證成功的計算這些變量中的每一個都必出現在環境變量中從邏輯上講這個集合就是調用Check方法返迴的結果但是因爲這個方法是遞歸調用的所以對於Check方法填充結果到一個作爲參數傳入的集合中會更加的方便調用方在初始調用時必提供一個空的集合
Check方法的參數是一個Var類型的集合這個集合聚集從表達式中找到的變量名爲了保證成功的計算這些變量中的每一個都必出現在環境變量中從邏輯上講這個集合就是調用Check方法返迴的結果但是因爲這個方法是遞歸調用的所以對於Check方法填充結果到一個作爲參數傳入的集合中會更加的方便調用方在初始調用時必提供一個空的集合
在第3.2節中我們繪製了一個在編譯器才確定的函數f(x,y)。現在我們可以解析檢査和計算在字符串中的表達式我們可以構建一個在運行時從客戶端接收表達式的web應用併且它會繪製這個函數的表示的麴面我們可以使用集合vars來檢査表達式是否是一個隻有兩個變量,x和y的函數——實際上是3個因爲我們爲了方便會提供半徑大小r併且我們會在計算前使用Check方法拒絶有格式問題的表達式這樣我們就不會在下面函數的40000個計算過程100x100個柵格每一個有4個角重複這些檢査