make loop

This commit is contained in:
chai2010
2015-12-18 14:49:31 +08:00
parent 9fde1ff772
commit f9ac065e47
106 changed files with 725 additions and 725 deletions

View File

@@ -1,6 +1,6 @@
## 1.1. Hello, World
我們以1978年c語言史上經典的hello world案例來開始吧。C語言對Go語言的設計生了很多影響。用這個例子我們來講解一些Go語言的覈心特性
我們以1978年c語言史上經典的hello world案例來開始吧。C語言對Go語言的設計生了很多影響。用這個例子我們來講解一些Go語言的覈心特性
```go
//gopl.io/ch1/helloworld
@@ -25,11 +25,11 @@ Hello, BF
Go原生支持Unicode所以你可以用Go處理世界上的任何語言。
如果你希望自己的程序不隻是簡單的一次性實驗,那麽你一定會希望能夠編譯這個程序,且能夠將編譯結果保存下來以備將來之用。這個可以用build子命令來實現
如果你希望自己的程序不隻是簡單的一次性實驗,那麽你一定會希望能夠編譯這個程序,且能夠將編譯結果保存下來以備將來之用。這個可以用build子命令來實現
```
$ go build helloworld.go
```
這會創建一個名爲helloworld的可執行的二進製文件之後你可以在任何時間去運行這個二進製文件不需要其它的任何處理(譯註:因爲是靜態編譯,所以也不用擔心在繫統庫更新的時候突,福感滿滿)。
這會創建一個名爲helloworld的可執行的二進製文件之後你可以在任何時間去運行這個二進製文件不需要其它的任何處理(譯註:因爲是靜態編譯,所以也不用擔心在繫統庫更新的時候突,福感滿滿)。
下面是運行我們的編譯結果樣例:
```
@@ -37,33 +37,33 @@ $ ./helloworld
Hello, BF
```
本書中我們所有的例子都做了一個特殊標記你可以通過這些標記在gopl.io在網站上找到這些樣例代碼,比如這個 gopl.io/ch1/helloworld
本書中我們所有的例子都做了一個特殊標記你可以通過這些標記在gopl.io在網站上找到這些樣例代碼,比如這個 gopl.io/ch1/helloworld
如果你執行go get gopl.io/ch1/helloworldgo能夠自己從網上取到這些代碼,且將這些代碼放到對應的目録中。更詳細的介紹在2.6和10.7章節中。
如果你執行go get gopl.io/ch1/helloworldgo能夠自己從網上取到這些代碼,且將這些代碼放到對應的目録中。更詳細的介紹在2.6和10.7章節中。
我們來討論一下程序本身。Go的代碼是用package來組織的package的概和你知道的其它語言的libraries或者modules比較類似。一個package會包含一個或多個.go結束的源代碼文件。每一個源文件都是以一個package xxx的聲明開頭的比如我們的例子就是package main。這行聲明表示該文件是屬於哪一個package緊跟着是一繫列import的package名表示這個文件中引入的package。再之後是本文件本身的代碼
我們來討論一下程序本身。Go的代碼是用package來組織的package的概和你知道的其它語言的libraries或者modules比較類似。一個package會包含一個或多個.go結束的源代碼文件。每一個源文件都是以一個package xxx的聲明開頭的比如我們的例子就是package main。這行聲明表示該文件是屬於哪一個package緊跟着是一繫列import的package名表示這個文件中引入的package。再之後是本文件本身的代碼
Go的標準庫已經提供了100多個package用來完成一門程序語言的一些基本任務比如輸入、輸齣、排序或者字符串/文本處理。比如fmt這個package就包括接收輸入、格式化輸齣的各種函數。Println是其中的一個函數可以用這個函數來打印一個或多個值該函數會將這些參數用空格隔開進行輸齣在輸齣完畢之後在行末加上一個換行符。
Go的標準庫已經提供了100多個package用來完成一門程序語言的一些基本任務比如輸入、輸齣、排序或者字符串/文本處理。比如fmt這個package就包括接收輸入、格式化輸齣的各種函數。Println是其中的一個函數可以用這個函數來打印一個或多個值該函數會將這些參數用空格隔開進行輸齣在輸齣完畢之後在行末加上一個換行符。
package main比較特殊。這個package會定義一個獨立的程序這個程序是可以運行的而不是像其它package一樣的library。在main這個packagemain函數也是一個特殊的函數這是我們整個程序的入口(譯註其實c繫語言差不多都是這樣)。main函數所做的事情就是我們程序做的事情。當然了main函數一般完成的工作是調用其它packge的函數來完成自己的工作比如fmt.Println。
package main比較特殊。這個package會定義一個獨立的程序這個程序是可以運行的而不是像其它package一樣的library。在main這個packagemain函數也是一個特殊的函數這是我們整個程序的入口(譯註其實c繫語言差不多都是這樣)。main函數所做的事情就是我們程序做的事情。當然了main函數一般完成的工作是調用其它packge的函數來完成自己的工作比如fmt.Println。
我們必鬚告訴編譯器如果要正確地執行這個源文件需要用到哪些package這就是import在這個文件扮演的角色。上述的hello world隻用到了一個其它的package就是fmt。一般情況下需要import的package不隻一個。
我們必鬚告訴編譯器如果要正確地執行這個源文件需要用到哪些package這就是import在這個文件扮演的角色。上述的hello world隻用到了一個其它的package就是fmt。一般情況下需要import的package不隻一個。
也正是因爲go語言必鬚引入所有用到的package的原則假如你沒有在代碼import需要用到的package程序將無法編譯通過當你import了沒有用到的package也會無法編譯通過(譯註:爭議特性之一)。
也正是因爲go語言必鬚引入所有用到的package的原則假如你沒有在代碼import需要用到的package程序將無法編譯通過當你import了沒有用到的package也會無法編譯通過(譯註:爭議特性之一)。
import聲明必鬚跟在文件的package聲明之後。在import之後則是各種方法、變量、常量、類型的聲明(分用關鍵字func, var, const, type來進行定義)。這些內容的聲明順序沒有什麽規定,可以隨便(譯註:最好還是定一下規)。我們例子的程序比較簡單,隻包含了一個函數。且在該函數也隻調用了一個其它函數。爲了節省空間有些時候的例子我們會省略package和import聲明但是讀者需要註意這些聲明是一定要包含在源文件的。
import聲明必鬚跟在文件的package聲明之後。在import之後則是各種方法、變量、常量、類型的聲明(分用關鍵字func, var, const, type來進行定義)。這些內容的聲明順序沒有什麽規定,可以隨便(譯註:最好還是定一下規)。我們例子的程序比較簡單,隻包含了一個函數。且在該函數也隻調用了一個其它函數。爲了節省空間有些時候的例子我們會省略package和import聲明但是讀者需要註意這些聲明是一定要包含在源文件的。
一個函數的聲明包含func這個關鍵字、函數名、參數列表(我們例子的main函數是空)、返迴結果列表(這的例子也是空)以及包含在大括號的函數體。關於函數的更詳細描述在第五章。
一個函數的聲明包含func這個關鍵字、函數名、參數列表(我們例子的main函數是空)、返迴結果列表(這的例子也是空)以及包含在大括號的函數體。關於函數的更詳細描述在第五章。
Go是一門不需要分號作爲語句或者聲明結束的語言除非要在一行中將多個語句、聲明隔開。然而在編譯時編譯器會主動在一些特定的符號(譯註比如行末是一個標識符、一個整數、浮點數、虛數、字符或字符串文字、關鍵字break、continue、fallthrough或return中的一個、運算符和分隔符++、--、)、]或}中的一個) 後添加分號,所以在哪加分號合適是取決於Go的代碼的。例如在Go語言中的函數聲明和 { 必鬚在同一行而在x + y的表達式中在+號後換行可以,但是在+號前換行則會有問題。
Go是一門不需要分號作爲語句或者聲明結束的語言除非要在一行中將多個語句、聲明隔開。然而在編譯時編譯器會主動在一些特定的符號(譯註比如行末是一個標識符、一個整數、浮點數、虛數、字符或字符串文字、關鍵字break、continue、fallthrough或return中的一個、運算符和分隔符++、--、)、]或}中的一個) 後添加分號,所以在哪加分號合適是取決於Go的代碼的。例如在Go語言中的函數聲明和 { 必鬚在同一行而在x + y的表達式中在+號後換行可以,但是在+號前換行則會有問題。
Go語言在代碼格式上取了很強硬的態度。gofmt工具會將你的代碼格式化爲標準格式且go工具中的fmt子命令會自動對特定package下的所有.go源文件應用gofmt。如果不指定package則默認對當前目録下的源文件進行格式化。本書中的所有代碼已經是執行過gofmt後的標準格式代碼。你應該在自己的代碼上也執行這種格式化。規定一種標準的代碼格式可以規避掉無的無意義的撕逼。當然了,也可以避免由於代碼格式導的邏輯上的歧義。
Go語言在代碼格式上取了很強硬的態度。gofmt工具會將你的代碼格式化爲標準格式且go工具中的fmt子命令會自動對特定package下的所有.go源文件應用gofmt。如果不指定package則默認對當前目録下的源文件進行格式化。本書中的所有代碼已經是執行過gofmt後的標準格式代碼。你應該在自己的代碼上也執行這種格式化。規定一種標準的代碼格式可以規避掉無的無意義的撕逼。當然了,也可以避免由於代碼格式導的邏輯上的歧義。
很多文本編輯器都可以設置爲保存文件時自動執行gofmt所以你的源代碼應該總是會被格式化。這還有一個相關的工具goimports會自動地添加你代碼需要用到的import聲明以及需要移除的import聲明。這個工具沒有包含在標準的分發包中,然而你可以自行安裝:
很多文本編輯器都可以設置爲保存文件時自動執行gofmt所以你的源代碼應該總是會被格式化。這還有一個相關的工具goimports會自動地添加你代碼需要用到的import聲明以及需要移除的import聲明。這個工具沒有包含在標準的分發包中,然而你可以自行安裝:
```
$ go get golang.org/x/tools/cmd/goimports
```
對於大多數用戶來下載、build package、運行測試用例、顯示go的文檔等等常用功能都是可以用go的工具來實現的。這些工具的詳細介紹我們會在10.7節中提到。
對於大多數用戶來下載、build package、運行測試用例、顯示go的文檔等等常用功能都是可以用go的工具來實現的。這些工具的詳細介紹我們會在10.7節中提到。

View File

@@ -1,13 +1,13 @@
## 1.2. 命令行參數
大多數的程序都是處理輸入,生輸齣;這也正是“計算”的定義。但是一個程序要如何取輸入呢?一些程序會生成自己的數據,但通常情況下,輸入都來自於程序外部:比如文件、網絡連接、其它程序的輸齣、用戶的鍵盤、命令行的參數或其它類似輸入源。下面幾個例子會討論其中的一些輸入類型,首先是命令行參數。
大多數的程序都是處理輸入,生輸齣;這也正是“計算”的定義。但是一個程序要如何取輸入呢?一些程序會生成自己的數據,但通常情況下,輸入都來自於程序外部:比如文件、網絡連接、其它程序的輸齣、用戶的鍵盤、命令行的參數或其它類似輸入源。下面幾個例子會討論其中的一些輸入類型,首先是命令行參數。
os這個package提供了操作繫統無關(跨平)的與繫統交互的一些函數和相關的變量運行時程序的命令行參數可以用一個叫os包中的Args這個變量來在外部需要使用該變量時需要用os.Args來訪問。
os這個package提供了操作繫統無關(跨平)的與繫統交互的一些函數和相關的變量運行時程序的命令行參數可以用一個叫os包中的Args這個變量來在外部需要使用該變量時需要用os.Args來訪問。
os.Args這個變量是一個字符串(string)的sliceslice在go語言是一個基礎的數據結構之後我們很快會提到。現在可以先把slice當一個簡單的元素序列可以用類似s[i]的下標訪問形式取其內容,且可以用形如s[m:n]的形式來取到一個slice的子集(譯註和python的差不多)。其長度可以用len(s)函數來取。和其它大多數語言差不多go語言的這種索引形式也用了開區間包括m~n的第一個元素但不包括最後那個元素(譯註比如a = [1, 2, 3, 4, 5], a[0: 3] =[1, 2, 3],不包含最後一個元素)。這樣可以簡化我們的邏輯。比如s[m:n]這個slice0 ≤ m ≤ n ≤ len(s)包含n-m個元素。
os.Args這個變量是一個字符串(string)的sliceslice在go語言是一個基礎的數據結構之後我們很快會提到。現在可以先把slice當一個簡單的元素序列可以用類似s[i]的下標訪問形式取其內容,且可以用形如s[m:n]的形式來取到一個slice的子集(譯註和python的差不多)。其長度可以用len(s)函數來取。和其它大多數語言差不多go語言的這種索引形式也用了開區間包括m~n的第一個元素但不包括最後那個元素(譯註比如a = [1, 2, 3, 4, 5], a[0: 3] =[1, 2, 3],不包含最後一個元素)。這樣可以簡化我們的邏輯。比如s[m:n]這個slice0 ≤ m ≤ n ≤ len(s)包含n-m個元素。
os.Args的第一個元素卽os.Args[0]是命令行執行時的命令本身其它的元素則是執行該命令時傳給這個程序的參數。前面提到的切片表達式s[m:n]會返迴第m到第n-1個元素所以下一個例子需要用到的os.Args[1:len(os.Args)]卽是除了命令本身外的所有傳入參數。如果我們省略s[m:n]的m和n那麽默認這個表達式會填入0:len(s),所以這我們還可以省略掉n寫os.Args[1:]。
os.Args的第一個元素卽os.Args[0]是命令行執行時的命令本身其它的元素則是執行該命令時傳給這個程序的參數。前面提到的切片表達式s[m:n]會返迴第m到第n-1個元素所以下一個例子需要用到的os.Args[1:len(os.Args)]卽是除了命令本身外的所有傳入參數。如果我們省略s[m:n]的m和n那麽默認這個表達式會填入0:len(s),所以這我們還可以省略掉n寫os.Args[1:]。
下面是一個Unixecho命令的實現這個命令會在單行內打印齣命令行參數。這個程序import了兩個package且用括號把這兩個package包了起來這是分import各個package聲明的簡化寫法。當然了你分開來寫import也沒有什麽問題隻是一般爲了方便我們都會像下面這樣來導入多個package。我們自己寫的導入順序不重要因爲gofmt工具會幫助我們按照字母順序來排列好這些導入包名。(本書中如果一個例子有多種版本時,我們會用編號標記齣來)
下面是一個Unixecho命令的實現這個命令會在單行內打印齣命令行參數。這個程序import了兩個package且用括號把這兩個package包了起來這是分import各個package聲明的簡化寫法。當然了你分開來寫import也沒有什麽問題隻是一般爲了方便我們都會像下面這樣來導入多個package。我們自己寫的導入順序不重要因爲gofmt工具會幫助我們按照字母順序來排列好這些導入包名。(本書中如果一個例子有多種版本時,我們會用編號標記齣來)
```go
gopl.io/ch1/echo1
// Echo1 prints its command-line arguments.
@@ -25,11 +25,11 @@ func main() {
fmt.Println(s)
}
```
Go的註釋是以//來表示。//後的內容一直到行末都是這條註釋的一部分,且這些註釋會被編譯器忽略。
Go的註釋是以//來表示。//後的內容一直到行末都是這條註釋的一部分,且這些註釋會被編譯器忽略。
按照慣例我們會在每一個package前面放上這個package的詳的註釋對其進行對於一個main package來,一般這段評論會包含幾句話來明這個項目/程序整體是做什麽用的。
按照慣例我們會在每一個package前面放上這個package的詳的註釋對其進行對於一個main package來,一般這段評論會包含幾句話來明這個項目/程序整體是做什麽用的。
var關鍵字用來做變量聲明。這聲明了s和sep兩個string變量。變量可以在聲明期間直接進行初始化。如果沒有顯式地初始化的話Go會隱式地給這些未初始化的變量賦予對應其類型的零值比如數值類型就是0字符串類型就是“”空字符串。在這個例子的s和sep被隱式地賦值爲了空字符串。在第2章中我們會更詳細地講解變量和聲明。
var關鍵字用來做變量聲明。這聲明了s和sep兩個string變量。變量可以在聲明期間直接進行初始化。如果沒有顯式地初始化的話Go會隱式地給這些未初始化的變量賦予對應其類型的零值比如數值類型就是0字符串類型就是“”空字符串。在這個例子的s和sep被隱式地賦值爲了空字符串。在第2章中我們會更詳細地講解變量和聲明。
對於數字類型Go語言提供了常規的數值計算和邏輯運算符。而對於string類型+號表示字符串的連接(譯註和C++或者js是一樣的)。所以下面這個表達式:
@@ -37,7 +37,7 @@ var關鍵字用來做變量聲明。這裡聲明了s和sep兩個string變量。
sep + os.Args[i]
```
表示將sep字符串和os.Args[i]字符串進行連接。我們在程序用的另外一個表達式:
表示將sep字符串和os.Args[i]字符串進行連接。我們在程序用的另外一個表達式:
```go
s += sep + os.Args[i]
@@ -51,24 +51,24 @@ s = s + sep + os.Args[i]
運算符+=是一個賦值運算符(assignment operator),每一種數值和邏輯運算符,例如*或者+都有其對應的賦值運算符。
echo程序可以每循環一次輸齣一個參數不過我們這的版本是不斷地將其結果連接到一個字符串的末尾。s這個字符串在聲明的時候是一個空字符串而之後循環每次都會被在末尾添加一段字符串第一次迭代之後一個空格會被插入到字符串末尾所以每插入一個新值都會和前一個中間有一個空格隔開。這是一種非性的操作,當我們的參數數量變得龐大的時候(當然不是說這裡的echo一般echo也不會有太多參數)其運行開銷也會變得龐大。下面我們會介紹一繫列的echo改進版來應對這裡說到的運行效率低下。
echo程序可以每循環一次輸齣一個參數不過我們這的版本是不斷地將其結果連接到一個字符串的末尾。s這個字符串在聲明的時候是一個空字符串而之後循環每次都會被在末尾添加一段字符串第一次迭代之後一個空格會被插入到字符串末尾所以每插入一個新值都會和前一個中間有一個空格隔開。這是一種非性的操作,當我們的參數數量變得龐大的時候(當然不是説這里的echo一般echo也不會有太多參數)其運行開銷也會變得龐大。下面我們會介紹一繫列的echo改進版來應對這里説到的運行效率低下。
在for循環中我們用到了i來做下標索引可以看到我們用了:=符號來給i進行初始化和賦值這是var xxx=yyy的一種簡寫形式Go會根據等號右邊的值的類型自動判斷左邊的值類型下一章會對這一點進行詳細明。
在for循環中我們用到了i來做下標索引可以看到我們用了:=符號來給i進行初始化和賦值這是var xxx=yyy的一種簡寫形式Go會根據等號右邊的值的類型自動判斷左邊的值類型下一章會對這一點進行詳細明。
自增表達式i++會爲i加上1這個i += 1以及i = i + 1都是等價的。對應的還有i--是給i減去1。這些在go語言是語句而不像C繫的其它語言是表達式。所以在Go語言j = i++是非法的,而且++和--都隻能放在變量名後面,因此--i也是非法的。
自增表達式i++會爲i加上1這個i += 1以及i = i + 1都是等價的。對應的還有i--是給i減去1。這些在go語言是語句而不像C繫的其它語言是表達式。所以在Go語言j = i++是非法的,而且++和--都隻能放在變量名後面,因此--i也是非法的。
在Go語言隻有for循環一種循環。當然了爲了滿足需求Go的for循環有很多種形式下面是其中的一種
在Go語言隻有for循環一種循環。當然了爲了滿足需求Go的for循環有很多種形式下面是其中的一種
```go
for initialization; condition; post {
// zero or more statements
}
```
需要註意for循環的兩邊是不需要像其它語言一樣寫括號的。且左大括號需要和for語句在同一行。
需要註意for循環的兩邊是不需要像其它語言一樣寫括號的。且左大括號需要和for語句在同一行。
initialization部分是可選的如果你寫了這部分的話在for循環之前這部分的邏輯會被執行。需要註意的是這部分必鬚是一個簡單的語句也就是是一個簡短的變量聲明一個賦值語句或是一個函數調用。condition部分必鬚是一個結果爲boolean值的表達式在每次循環之前語言都會檢査當前是否滿足這個條件如果不滿足的話便會結束循環post部分的語句則是在每次循環結束之後被執行之後conditon部分會在下一次執行前再被執行依此往。當condition條件的判斷結果變爲false之後循環卽結束。
initialization部分是可選的如果你寫了這部分的話在for循環之前這部分的邏輯會被執行。需要註意的是這部分必鬚是一個簡單的語句也就是是一個簡短的變量聲明一個賦值語句或是一個函數調用。condition部分必鬚是一個結果爲boolean值的表達式在每次循環之前語言都會檢査當前是否滿足這個條件如果不滿足的話便會結束循環post部分的語句則是在每次循環結束之後被執行之後conditon部分會在下一次執行前再被執行依此往。當condition條件的判斷結果變爲false之後循環卽結束。
上面提到是for循環的三個部分都是可以被省略的如果你把initialization和post部分都省略的話那麽連中間隔離他們的分號也是可以被省略的比如下面這種for循環就和傳統的while循環是一樣的
上面提到是for循環的三個部分都是可以被省略的如果你把initialization和post部分都省略的話那麽連中間隔離他們的分號也是可以被省略的比如下面這種for循環就和傳統的while循環是一樣的
```go
// a traditional "while" loop
@@ -87,7 +87,7 @@ for {
在無限循環中你還是可以靠break或者return來終止掉循環。
如果你的遍對象是string或者slice的值的話還有另外一種循環的寫法我們來看看另一個版本的echo
如果你的遍對象是string或者slice的值的話還有另外一種循環的寫法我們來看看另一個版本的echo
```go
gopl.io/ch1/echo2
@@ -108,9 +108,9 @@ func main() {
}
```
每一次循環迭代range都會返迴一對結果當前迭代的下標以及在該下標處的元素的值。在這個例子我們不需要這個下標但是因爲range的處理要求我們必鬚要同時處理下標和值。我們可以在這聲明一個接收index的臨時變量來解決這個問題但是go語言又不允許隻聲明而在後續代碼不使用這個變量,如果你這樣做了編譯器會返迴一個編譯錯誤。
每一次循環迭代range都會返迴一對結果當前迭代的下標以及在該下標處的元素的值。在這個例子我們不需要這個下標但是因爲range的處理要求我們必鬚要同時處理下標和值。我們可以在這聲明一個接收index的臨時變量來解決這個問題但是go語言又不允許隻聲明而在後續代碼不使用這個變量,如果你這樣做了編譯器會返迴一個編譯錯誤。
在Go語言中應對這種情況的解決方法是用空白標識符就是上面那個下劃_。空白標識符可以在任何你接收自己不需要處理的值時使用。在這我們用他來忽略掉range返迴的那個沒用的下標值。大多數的Go程序員都會像上面這樣來寫類似的os.Args遍,可以避免錯誤的下標引用。(這可能有翻譯錯,附上原文)
在Go語言中應對這種情況的解決方法是用空白標識符就是上面那個下劃_。空白標識符可以在任何你接收自己不需要處理的值時使用。在這我們用他來忽略掉range返迴的那個沒用的下標值。大多數的Go程序員都會像上面這樣來寫類似的os.Args遍,可以避免錯誤的下標引用。(這可能有翻譯錯,附上原文)
Most Go programmers would likely use range and _ to write the echo program as above, since the indexing over os.Args is implicit, not explicit, and thus easier to get right.
上面這個版本將s和sep的聲明和初始化都放到了一起但是我們可以等價地將聲明和賦值分開來寫下面這些寫法都是等價的
@@ -122,12 +122,12 @@ var s = ""
var s string = ""
```
那麽這些等價的形式應該怎麽做選擇呢?這提供一些建議第一種形式最好隻用在一個函數內部而package級的變量請不要使用這樣的聲明方式。第二種形式依賴於string類型的內部初始化機製被初始化爲空字符串。第三種形式使用得很少除非同時聲明多個變量。第四種形式會顯式地標明變量的類型在多變量同時聲明時可以用到。實踐中你應該隻使用上面的前兩種形式顯式地指定變量的類型讓編譯器自己去初始化其值或者直接用隱式初始化表明初始值怎麽樣不重要。
那麽這些等價的形式應該怎麽做選擇呢?這提供一些建議第一種形式最好隻用在一個函數內部而package級的變量請不要使用這樣的聲明方式。第二種形式依賴於string類型的內部初始化機製被初始化爲空字符串。第三種形式使用得很少除非同時聲明多個變量。第四種形式會顯式地標明變量的類型在多變量同時聲明時可以用到。實踐中你應該隻使用上面的前兩種形式顯式地指定變量的類型讓編譯器自己去初始化其值或者直接用隱式初始化表明初始值怎麽樣不重要。
像上面提到的每次循環中字符串s都會得到一個新內容。+=語句會分配一個新的字符串,將老字符串連接起來的值賦予給它。而目標字符串的老字面值在得到新值以後就失去了用處這些臨時值會被go的垃圾收集器掉。
像上面提到的每次循環中字符串s都會得到一個新內容。+=語句會分配一個新的字符串,將老字符串連接起來的值賦予給它。而目標字符串的老字面值在得到新值以後就失去了用處這些臨時值會被go的垃圾收集器掉。
如果不斷連接的數據量很大,那麽上面這種操作就是成本非常高的操作。更簡單且有效的一種方式是使用字符串的Join函數像下面這樣
如果不斷連接的數據量很大,那麽上面這種操作就是成本非常高的操作。更簡單且有效的一種方式是使用字符串的Join函數像下面這樣
```go
gopl.io/ch1/echo3
@@ -142,7 +142,7 @@ func main() {
fmt.Println(os.Args[1:])
```
這個輸齣結果和前面的string.Join得到的結果很相似隻是被自動地放到了一個括號對slice調用Println函數都會被打印成這樣形式的結果。
這個輸齣結果和前面的string.Join得到的結果很相似隻是被自動地放到了一個括號對slice調用Println函數都會被打印成這樣形式的結果。
下面是幾道練習題:
@@ -150,5 +150,5 @@ fmt.Println(os.Args[1:])
```
Exercise 1.1:脩改echo程序使其能夠打印os.Args[0]。
Exercise 1.2:脩改echo程序使其打印value和index每個value和index顯示一行。
Exercise 1.3:上手實踐前面提到的strings.Join和直接Println觀察輸齣結果的區
Exercise 1.3:上手實踐前面提到的strings.Join和直接Println觀察輸齣結果的區
```

View File

@@ -1,6 +1,6 @@
## 1.3. 査找重的行
## 1.3. 査找重的行
文件拷貝、文件打印、文件蒐索、文件排序、文件統計類的程序一般都會有比較相似的程序結構處理輸入的一個循環在每一個輸入元素上執行計算處理在處理的同時或者處理完成之後進行結果輸齣。我們會展示一個叫dup程序的三個版本這個程序的靈感來自於linux的uniq命令我們的程序將會找到相鄰的重的行。這個程序提供的模式可以很方便地被脩改來完成不同的需求。
文件拷貝、文件打印、文件蒐索、文件排序、文件統計類的程序一般都會有比較相似的程序結構處理輸入的一個循環在每一個輸入元素上執行計算處理在處理的同時或者處理完成之後進行結果輸齣。我們會展示一個叫dup程序的三個版本這個程序的靈感來自於linux的uniq命令我們的程序將會找到相鄰的重的行。這個程序提供的模式可以很方便地被脩改來完成不同的需求。
第一個版本的dup會輸齣標準輸入流中的齣現多次的行在行內容前會有其齣現次數的計數。這個程序將引入if表達式map內置數據結果和bufio的package。
@@ -33,7 +33,7 @@ func main() {
和我們前面提到的for循環一樣在if條件的兩邊我們也不需要加括號但是if表達式後的邏輯體的花括號是不能省略的。如果需要的話像其它語言一樣這個表達式也可以有else部分這部分邏輯會在if中的條件結果爲false時被執行。
map是go語言內置的key/value數據結構這個數據結構能夠提供常數時間的存儲、取、測試操作。key可以是任意數據類型隻要該類型能夠用==來進行比較string是最常用的key類型。而value類型的圍就更大了基本上什麽類型都是可以的。這個例子中的key都是string類型value用的是int類型。我們用內置make函數來創建一個空的map當然了make方法還可以有的用處。在4.3章中我們還會對map進行更深度的討論。
map是go語言內置的key/value數據結構這個數據結構能夠提供常數時間的存儲、取、測試操作。key可以是任意數據類型隻要該類型能夠用==來進行比較string是最常用的key類型。而value類型的圍就更大了基本上什麽類型都是可以的。這個例子中的key都是string類型value用的是int類型。我們用內置make函數來創建一個空的map當然了make方法還可以有的用處。在4.3章中我們還會對map進行更深度的討論。
dup程序每次讀取輸入的一行這一行的內容會被當做一個map的key而其value值會被+1。counts[input.Text()]++這個語句和下面的兩句是等價的:
@@ -43,9 +43,9 @@ line := input.Text()
counts[line] = counts[line] + 1
```
當然了,在這個例子我們不用擔心map在沒有當前的key時就對其進行++操作會有什麽問題因爲go語言在碰到這種情況時會自動將其初始化爲0然後再進行操作。
當然了,在這個例子我們不用擔心map在沒有當前的key時就對其進行++操作會有什麽問題因爲go語言在碰到這種情況時會自動將其初始化爲0然後再進行操作。
在這我們又用了一個range的循環來打印結果這次range是被用在map這個數據結果上。這一次的情況和上次比較類型range會返迴兩個值一個key和在map對應這個key的value。對map進行range循環時其順序是不確定的從實踐來看很可能每次運行都會有不一樣的結果(譯註這是go的設計者有意爲之的因爲其底層實現不保插入順序和遍順序一,而希望程序員不要依賴遍時的順序,所以脆直接在遍的時候做了隨機化處理,醉了),來避免程序員在業務中依賴遍時的順序。
在這我們又用了一個range的循環來打印結果這次range是被用在map這個數據結果上。這一次的情況和上次比較類型range會返迴兩個值一個key和在map對應這個key的value。對map進行range循環時其順序是不確定的從實踐來看很可能每次運行都會有不一樣的結果(譯註這是go的設計者有意爲之的因爲其底層實現不保插入順序和遍順序一,而希望程序員不要依賴遍時的順序,所以脆直接在遍的時候做了隨機化處理,醉了),來避免程序員在業務中依賴遍時的順序。
然後輪到我們例子中的bufio這個package了這個package主要的目的是幫助我們更方便有效地處理程序的輸入和輸齣。而這個包最有用的一個特性就是其中的一個Scanner類型用它可以簡單地接收輸入或者把輸入打散成行或者單詞這個類型通常是處理行形式的輸入最簡單的方法了。
@@ -55,18 +55,18 @@ counts[line] = counts[line] + 1
input := bufio.NewScanner(os.Stdin)
```
scanner對象可以從程序的標準輸入中讀取內容。對input.Scanner的每一次調用都會調入一個新行且會自動將其行末的換行符去掉其結果可以用input.Text()得到。Scan方法在讀到了新行的時候會返迴true而在沒有新行被讀入時會返迴false。
scanner對象可以從程序的標準輸入中讀取內容。對input.Scanner的每一次調用都會調入一個新行且會自動將其行末的換行符去掉其結果可以用input.Text()得到。Scan方法在讀到了新行的時候會返迴true而在沒有新行被讀入時會返迴false。
例子中還有一個fmt.Printf這個函數和C繫的其它語言的那個printf函數差不多都是格式化輸齣的方法。fmt.Printf的第一個參數卽是輸齣內容的格式規約每一個參數如果格式化是取決於在格式化字符串齣現的“轉換字符”,這個字符串是跟着%號後的一個字母。比如%d表示以一個整數的形式來打印一個變量而%s則表示以string形式來打印一個變量。
例子中還有一個fmt.Printf這個函數和C繫的其它語言的那個printf函數差不多都是格式化輸齣的方法。fmt.Printf的第一個參數卽是輸齣內容的格式規約每一個參數如果格式化是取決於在格式化字符串齣現的“轉換字符”,這個字符串是跟着%號後的一個字母。比如%d表示以一個整數的形式來打印一個變量而%s則表示以string形式來打印一個變量。
Printf有一大堆這種轉換Go程序員把這些叫做verb(動詞)。下面的表格列齣了常用的動詞,當然了不是全部,但基本也夠用了。
```
%d int變量
%x, %o, %b 分爲16進製8進製2進製形式的int
%x, %o, %b 分爲16進製8進製2進製形式的int
%f, %g, %e 浮點數: 3.141593 3.141592653589793 3.141593e+00
%t 爾變量true 或 false
%c rune (Unicode code point)go語言特有的Unicode字符類型
%t 爾變量true 或 false
%c rune (Unicode code point)go語言特有的Unicode字符類型
%s string
%q quoted string "abc" or rune 'c'
%v 會將任意變量以易讀的形式打印齣來
@@ -74,9 +74,9 @@ Printf有一大堆這種轉換Go程序員把這些叫做verb(動詞)。下面
%% 字符型百分比標誌(不確定) literal percent sign (no operand)
```
dup1中的程序還包含了一個\t和\n的格式化字符串。在字符串中會以這些特殊的轉義字符來表示不可見字符。Printf默認不會在輸齣內容後加上換行符。按照慣例用來格式化的函數都會在末尾以f字母結尾比如log.Printffmt.Errorf同時還有一繫列對應以ln結尾的函數這些函數默認以%v來格式化他們的參數且會在輸齣結束後在最後自動加上一個換行符。
dup1中的程序還包含了一個\t和\n的格式化字符串。在字符串中會以這些特殊的轉義字符來表示不可見字符。Printf默認不會在輸齣內容後加上換行符。按照慣例用來格式化的函數都會在末尾以f字母結尾比如log.Printffmt.Errorf同時還有一繫列對應以ln結尾的函數這些函數默認以%v來格式化他們的參數且會在輸齣結束後在最後自動加上一個換行符。
許多程序從標準輸入中讀取數據像上面的例子那樣。除此之外還可能從一繫列的文件中讀取。下一個dup程序就是從標準輸入中讀到一些文件名用os.Open函數來打開每一個文件取內容的。
許多程序從標準輸入中讀取數據像上面的例子那樣。除此之外還可能從一繫列的文件中讀取。下一個dup程序就是從標準輸入中讀到一些文件名用os.Open函數來打開每一個文件取內容的。
```go
gopl.io/ch1/dup2
@@ -124,15 +124,15 @@ func countLines(f *os.File, counts map[string]int) {
os.Open函數會返迴兩個值。第一個值是一個打開的文件類型(*os.File)這個對象在下面的程序中被Scanner讀取。
os.Open返迴的第二個值是一個go內置的error類型。如果這個error和內置值的nil(譯註:相當於其它語言的NULL)相等的話,明文件被成功的打開了。之後文件被讀取一直到文件的最後Close函數關閉該文件釋放相應的用一切資源。另一方面如果err的值不是nil的話明在打開文件的時候齣了某種錯誤。這種情況下error類型的值會描述具體的問題。我們例子的簡單錯誤處理會在標準錯誤流中用Fprintf和%v來格式化該錯誤字符串。然後繼續處理下一個文件continue語句會直接跳過之後的語句直接開始執行下一次循環。
os.Open返迴的第二個值是一個go內置的error類型。如果這個error和內置值的nil(譯註:相當於其它語言的NULL)相等的話,明文件被成功的打開了。之後文件被讀取一直到文件的最後Close函數關閉該文件釋放相應的用一切資源。另一方面如果err的值不是nil的話明在打開文件的時候齣了某種錯誤。這種情況下error類型的值會描述具體的問題。我們例子的簡單錯誤處理會在標準錯誤流中用Fprintf和%v來格式化該錯誤字符串。然後繼續處理下一個文件continue語句會直接跳過之後的語句直接開始執行下一次循環。
我們在本書中早期的例子中做了比較詳的錯誤處理當然了在實際編碼過程中像os.Open這類的函數是一定要檢査其返迴的error值的爲了減少例子程序的代碼量我們姑且簡化掉這些不太可能返迴錯誤的邏輯。後面的例子我們會跳過錯誤檢査。在5.4節中我們會對錯誤處理做更詳細的闡述。
我們在本書中早期的例子中做了比較詳的錯誤處理當然了在實際編碼過程中像os.Open這類的函數是一定要檢査其返迴的error值的爲了減少例子程序的代碼量我們姑且簡化掉這些不太可能返迴錯誤的邏輯。後面的例子我們會跳過錯誤檢査。在5.4節中我們會對錯誤處理做更詳細的闡述。
讀者可以再觀察一下上面的例子我們的countLines函數是在其聲明之前就被調用了。在Go語言,函數和包級的變量可以以任意的順序被聲明,不影響其被調用。(譯註:最好還是遵循一定的規)
讀者可以再觀察一下上面的例子我們的countLines函數是在其聲明之前就被調用了。在Go語言,函數和包級的變量可以以任意的順序被聲明,不影響其被調用。(譯註:最好還是遵循一定的規)
再來講講map這個數據結構map是用make函數創建的數據結構的一個引用。當一個map被作爲參數傳遞給一個函數時函數接收到的是一份引用的拷貝雖然本身不是一個東西,但因爲他們指向的是同一塊數據對象(譯註類似於C艹的引用傳遞),所以你在函數對map的值進行脩改時原始的map內的值也會改變。在我們的例子中我們在countLines函數中插入到counts這個map的值,在主函數中也是看得到的。
再來講講map這個數據結構map是用make函數創建的數據結構的一個引用。當一個map被作爲參數傳遞給一個函數時函數接收到的是一份引用的拷貝雖然本身不是一個東西,但因爲他們指向的是同一塊數據對象(譯註類似於C艹的引用傳遞),所以你在函數對map的值進行脩改時原始的map內的值也會改變。在我們的例子中我們在countLines函數中插入到counts這個map的值,在主函數中也是看得到的。
上面這個版本的dup是以流的形式來處理輸入將其打散爲行。理論上這些程序也是可以以二進製形式來處理輸入的。我們也可以一次性的把整個輸入內容全部讀到內存中然後再把其分割爲多行然後再去處理這些行內的數據。下面的dup3這個例子就是以這種形式來進行操作的。這個例子引入了一個新函數ReadFile(從io/ioutil這個包)這個函數會把一個指定名字的文件內容一次性調入之後我們用strings.Split函數把文件分割爲多個子字符串存儲到slice結構中。(Split函數是strings.Join的逆函數Join函數之前提到過)
上面這個版本的dup是以流的形式來處理輸入將其打散爲行。理論上這些程序也是可以以二進製形式來處理輸入的。我們也可以一次性的把整個輸入內容全部讀到內存中然後再把其分割爲多行然後再去處理這些行內的數據。下面的dup3這個例子就是以這種形式來進行操作的。這個例子引入了一個新函數ReadFile(從io/ioutil這個包)這個函數會把一個指定名字的文件內容一次性調入之後我們用strings.Split函數把文件分割爲多個子字符串存儲到slice結構中。(Split函數是strings.Join的逆函數Join函數之前提到過)
我們簡化了dup3這個程序。首先他隻讀取命名的文件而不去讀標準輸入因爲ReadFile函數需要一個文件名參數。其次我們將行計數邏輯移迴到了main函數因爲現在這個邏輯隻有一個地方需要用到。
@@ -169,8 +169,8 @@ func main() {
ReadFile函數返迴一個byte的slice這個slice必鬚被轉換爲string之後纔能夠用string.Split方法來進行處理。我們在3.5.4節中會更詳細地講解string和byte slice(字節數組)。
在更底層一些的地方bufio.Scannerioutil.ReadFile和ioutil.WriteFile使用的是*os.File的Read和Write方法不過一般程序員不需要去直接了解到其底層實現細節在bufio和io/ioutil包中提供的方法已經足夠好用。
在更底層一些的地方bufio.Scannerioutil.ReadFile和ioutil.WriteFile使用的是*os.File的Read和Write方法不過一般程序員不需要去直接了解到其底層實現細節在bufio和io/ioutil包中提供的方法已經足夠好用。
```
Exercise 1.4: 脩改dup2使其可以打印重的行分齣現在哪些文件。
Exercise 1.4: 脩改dup2使其可以打印重的行分齣現在哪些文件。
```

View File

@@ -1,10 +1,10 @@
## 1.4. GIF動
## 1.4. GIF動
下面的程序會演示Go語言標準庫的image這個package的用法我們會用這個包來生成一繫列的bit-mapped圖然後將這些圖片編碼爲一個GIF動。我們生成的圖形名字叫利薩如圖形(Lissajous figures)這種效果是在1960年代的老電影齣現的一種視覺特效。他們是協振子在兩個緯度上振動所生的麴比如兩個sin正絃波分在x軸和y軸輸入會生的麴。圖1.1是這樣的一個例子:
下面的程序會演示Go語言標準庫的image這個package的用法我們會用這個包來生成一繫列的bit-mapped圖然後將這些圖片編碼爲一個GIF動。我們生成的圖形名字叫利薩如圖形(Lissajous figures)這種效果是在1960年代的老電影齣現的一種視覺特效。他們是協振子在兩個緯度上振動所生的麴比如兩個sin正絃波分在x軸和y軸輸入會生的麴。圖1.1是這樣的一個例子:
![](../images/ch1-01.png)
這段代碼我們用了一些新的結構包括const聲明數據struct類型合聲明。和我們舉的其它的例子不太一樣,這一個例子包含了浮點數運算。這些概我們隻在這簡單地明一下,之後的章節會更詳細地講解。
這段代碼我們用了一些新的結構包括const聲明數據struct類型合聲明。和我們舉的其它的例子不太一樣,這一個例子包含了浮點數運算。這些概我們隻在這簡單地明一下,之後的章節會更詳細地講解。
```go
gopl.io/ch1/lissajous
@@ -62,19 +62,19 @@ blackIndex)
```
當我們import了一個包路徑包含有多個單詞的package時比如image/color(image和color兩個單詞)我們隻需要用最後那個單詞表示這個包就可以。所以當我們寫color.White時這個變量指向的是image/color包的變量同理gif.GIF是屬於image/gif包的變量。
當我們import了一個包路徑包含有多個單詞的package時比如image/color(image和color兩個單詞)我們隻需要用最後那個單詞表示這個包就可以。所以當我們寫color.White時這個變量指向的是image/color包的變量同理gif.GIF是屬於image/gif包的變量。
這個程序的常量聲明給齣了一繫列的常量值,常量是指在程序編譯後運行時始終都不會變化的值,比如圈數、幀數、延遲值。常量聲明和變量聲明一般都會齣現在包級所以這些常量在整個包中都是可以共享的或者你也可以把常量聲明定義在函數體內部那麽這種常量就隻能在函數體內用。常量聲明的值必鬚是一個數字值、字符串或者一個固定的boolean值。
這個程序的常量聲明給齣了一繫列的常量值,常量是指在程序編譯後運行時始終都不會變化的值,比如圈數、幀數、延遲值。常量聲明和變量聲明一般都會齣現在包級所以這些常量在整個包中都是可以共享的或者你也可以把常量聲明定義在函數體內部那麽這種常量就隻能在函數體內用。常量聲明的值必鬚是一個數字值、字符串或者一個固定的boolean值。
[]color.Color{...}和gif.GIF{...}這兩個表達式就是我們說的復合聲明(4.2和4.4.1節有明)。這是實例化Go語言裡的復合類型的一種寫法。這的前者生成的是一個slice後者生成的是一個struct。
[]color.Color{...}和gif.GIF{...}這兩個表達式就是我們説的複合聲明(4.2和4.4.1節有明)。這是實例化Go語言里的複合類型的一種寫法。這的前者生成的是一個slice後者生成的是一個struct。
gif.GIF是一個struct類型(參考4.4節)。struct是一組值或者叫字段的集合不同的類型集合在一個struct可以讓我們以一個統一的單元進行處理。anim是一個gif.GIF類型的struct變量。這種寫法會生成一個struct變量且其內部變量LoopCount字段會被設置爲nframes而其它的字段會被設置爲各自類型默認的零值。struct內部的變量可以以一個點(.)來進行訪問就像在最後兩個賦值語句中顯式地更新了anim這個struct的Delay和Image字段。
gif.GIF是一個struct類型(參考4.4節)。struct是一組值或者叫字段的集合不同的類型集合在一個struct可以讓我們以一個統一的單元進行處理。anim是一個gif.GIF類型的struct變量。這種寫法會生成一個struct變量且其內部變量LoopCount字段會被設置爲nframes而其它的字段會被設置爲各自類型默認的零值。struct內部的變量可以以一個點(.)來進行訪問就像在最後兩個賦值語句中顯式地更新了anim這個struct的Delay和Image字段。
lissajous函數內部有兩層嵌太的for循環。外層循環會循環64次每一次都會生成一個單獨的動幀。它生成了一個包含兩種色的201&201大小的圖片白色和黑色。所有像素點都會被默認設置爲其零值(也就是palette的第0個值),這我們設置的是白色。每次經過內存循環都會通過設置像素爲黑色生成一張新圖片。其結果會append到之前結果之後。這我們用到了append(參考4.2.1)這個內置函數將結果appen到anim中的幀列表末尾會設置一個默認的80ms的延遲值。最終循環結束所有的延遲值也被編碼進了GIF圖片中將結果寫入到輸齣流。out這個變量是io.Writer類型這個類型讓我們可以可以讓我們把輸齣結果寫到很多目標很快我們就可以看到了。
lissajous函數內部有兩層嵌太的for循環。外層循環會循環64次每一次都會生成一個單獨的動幀。它生成了一個包含兩種色的201&201大小的圖片白色和黑色。所有像素點都會被默認設置爲其零值(也就是palette的第0個值),這我們設置的是白色。每次經過內存循環都會通過設置像素爲黑色生成一張新圖片。其結果會append到之前結果之後。這我們用到了append(參考4.2.1)這個內置函數將結果appen到anim中的幀列表末尾會設置一個默認的80ms的延遲值。最終循環結束所有的延遲值也被編碼進了GIF圖片中將結果寫入到輸齣流。out這個變量是io.Writer類型這個類型讓我們可以可以讓我們把輸齣結果寫到很多目標很快我們就可以看到了。
內存循環設置了兩個偏振。x軸偏振使用的是一個sin函數。y軸偏振也是一個正絃波但是其其相對x軸的偏振是一個0-3的隨機值且初始偏振值是一個零值,隨着動的每一幀逐漸增加。循環會一直跑到x軸完成五次完整的循環。每一步它都會調用SetColorIndex來爲(x, y)點來染黑色。
內存循環設置了兩個偏振。x軸偏振使用的是一個sin函數。y軸偏振也是一個正絃波但是其其相對x軸的偏振是一個0-3的隨機值且初始偏振值是一個零值,隨着動的每一幀逐漸增加。循環會一直跑到x軸完成五次完整的循環。每一步它都會調用SetColorIndex來爲(x, y)點來染黑色。
main函數調用了lissajous函數且用它來向標準輸齣中打印信息所以下面這個命令會像圖1.1中生一個GIF動
main函數調用了lissajous函數且用它來向標準輸齣中打印信息所以下面這個命令會像圖1.1中生一個GIF動
```bash
$ go build gopl.io/ch1/lissajous
@@ -82,6 +82,6 @@ $ ./lissajous >out.gif
```
```
Exercise 1.5: 脩改前面的Lissajous程序的調色闆由緑色改爲黑色。我們可以用color.RGBA{0xRR, 0xGG, 0xBB}來得到#RRGGBB這個色值三個十六進製的字符串分代表紅、緑、藍像素。
Exercise 1.6: 脩改Lissajous程序脩改其調色闆來生成更豐富的然後脩改SetColorIndex的第三個參數看看顯示結果吧。
Exercise 1.5: 脩改前面的Lissajous程序的調色闆由緑色改爲黑色。我們可以用color.RGBA{0xRR, 0xGG, 0xBB}來得到#RRGGBB這個色值三個十六進製的字符串分代表紅、緑、藍像素。
Exercise 1.6: 脩改Lissajous程序脩改其調色闆來生成更豐富的然後脩改SetColorIndex的第三個參數看看顯示結果吧。
```

View File

@@ -1,7 +1,7 @@
## 1.5 取URL
對於很多應用來訪問互聯網上的信息和訪問本地文件繫統一樣重要。Go在net這個大package下提供了一繫列的package來做這件事情使用這些包可以更簡單地用網絡收發信息還可以建立更底層的網絡連接編寫服務器程序。在這些情景下Go原生的發特性(在第八章中會介紹)就顯得尤其好用了。
## 1.5 取URL
對於很多應用來訪問互聯網上的信息和訪問本地文件繫統一樣重要。Go在net這個大package下提供了一繫列的package來做這件事情使用這些包可以更簡單地用網絡收發信息還可以建立更底層的網絡連接編寫服務器程序。在這些情景下Go原生的發特性(在第八章中會介紹)就顯得尤其好用了。
爲了最簡單地展示基於HTTP取信息的方式下面給齣一個示例程序fetch這個程序將取對應的url將其源文本打印齣來這個例子的靈感來源於curl工具(譯註unix下的一個工具)。當然了curl提供的功能更爲雜豐富,這我們隻編寫最簡單的樣例。之後我們還會在本書中經常用到這個例子。
爲了最簡單地展示基於HTTP取信息的方式下面給齣一個示例程序fetch這個程序將取對應的url將其源文本打印齣來這個例子的靈感來源於curl工具(譯註unix下的一個工具)。當然了curl提供的功能更爲雜豐富,這我們隻編寫最簡單的樣例。之後我們還會在本書中經常用到這個例子。
```go
gopl.io/ch1/fetch
@@ -32,7 +32,7 @@ func main() {
}
```
這個程序從兩個package中導入了函數net/http和io/ioutilhttp.Get函數是創建HTTP請求的函數如果取過程沒有齣錯那麽會在resp這個結構體中得到訪問的請求結果。resp的Body字段包括一個可讀的服務器響應流。這之後ioutil.ReadAll函數從response中讀取到全部內容其結果保存在變量b中。resp.Body.Close這一句會關閉resp的Body流防止資源洩露Printf函數會將結果b寫齣到標準輸齣流中。
這個程序從兩個package中導入了函數net/http和io/ioutilhttp.Get函數是創建HTTP請求的函數如果取過程沒有齣錯那麽會在resp這個結構體中得到訪問的請求結果。resp的Body字段包括一個可讀的服務器響應流。這之後ioutil.ReadAll函數從response中讀取到全部內容其結果保存在變量b中。resp.Body.Close這一句會關閉resp的Body流防止資源洩露Printf函數會將結果b寫齣到標準輸齣流中。
```bash
$ go build gopl.io/ch1/fetch
@@ -50,10 +50,10 @@ $ ./fetch http://bad.gopl.io
fetch: Get http://bad.gopl.io: dial tcp: lookup bad.gopl.io: no such host
```
無論哪種失敗原因我們的程序都用了os.Exit函數來終止進程且返迴一個status錯誤碼其值爲1。
無論哪種失敗原因我們的程序都用了os.Exit函數來終止進程且返迴一個status錯誤碼其值爲1。
```
Exercise1.7: 函數調用io.Copy(dst, src)會從src中讀取內容將讀到的結果寫入到dst中使用這個函數替代掉例子中的ioutil.ReadAll來拷貝響應結構體到os.Stdout避免申請一個緩區(例子中的b)來存儲。記得處理io.Copy返迴結果中的錯誤。
Exercise 1.8: 脩改fetch這個如果輸入的url參數沒有http://前綴的話爲這個url加上該前綴。你可能會用到strings.HasPrefix這個函數。
Exercise1.7: 函數調用io.Copy(dst, src)會從src中讀取內容將讀到的結果寫入到dst中使用這個函數替代掉例子中的ioutil.ReadAll來拷貝響應結構體到os.Stdout避免申請一個緩區(例子中的b)來存儲。記得處理io.Copy返迴結果中的錯誤。
Exercise 1.8: 脩改fetch這個如果輸入的url參數沒有http://前綴的話爲這個url加上該前綴。你可能會用到strings.HasPrefix這個函數。
Exercise 1.9: 脩改fetch打印齣HTTP協議的狀態碼可以從resp.Status變量得到該狀態碼。
```

View File

@@ -1,8 +1,8 @@
## 1.6 並發獲取多個URL
## 1.6 併發穫取多個URL
Go語言最有意思且最新奇的特性就是其對發編程的支持了。發編程是一個大話題,在第八章和第九章中會講到。這我們隻淺輒止地來體驗一下Go語言的goroutine和channel。
Go語言最有意思且最新奇的特性就是其對發編程的支持了。發編程是一個大話題,在第八章和第九章中會講到。這我們隻淺輒止地來體驗一下Go語言的goroutine和channel。
下面的例子fetchall和上面的fetch程序所要做的工作是一但是這個fetchall的特之處在於它會同時去取所有的URL所以這個程序的取時間不會超過執行時間最長的那一個任務而不會像前面的fetch程序一樣執行時間是所有任務執行時間之和。這次的fetchall程序隻會打印取的內容大小和經過的時間,不會像上面那樣打印齣取的內容。
下面的例子fetchall和上面的fetch程序所要做的工作是一但是這個fetchall的特之處在於它會同時去取所有的URL所以這個程序的取時間不會超過執行時間最長的那一個任務而不會像前面的fetch程序一樣執行時間是所有任務執行時間之和。這次的fetchall程序隻會打印取的內容大小和經過的時間,不會像上面那樣打印齣取的內容。
```go
gopl.io/ch1/fetchall
@@ -58,10 +58,10 @@ $ ./fetchall https://golang.org http://gopl.io https://godoc.org
0.48s 2475 http://gopl.io
0.48s elapsed
```
goroutine是一種函數的行執行方式而channel是用來在goroutine之間進行參數傳遞。main函數卽運行在一個goroutine中而go function則表示創建一個新的goroutine讓這個函數去這個新的goroutine執行。
goroutine是一種函數的行執行方式而channel是用來在goroutine之間進行參數傳遞。main函數卽運行在一個goroutine中而go function則表示創建一個新的goroutine讓這個函數去這個新的goroutine執行。
main函數中用make函數創建了一個傳遞string類型參數的channel對每一個命令行參數我們都用go這個關鍵字來創建一個goroutine且讓函數在這個goroutine異步執行http.Get方法。這個程序的io.Copy會把響應的Body內容拷貝到ioutil.Discard輸齣流中因爲我們需要這個方法返迴的字節數但是又不想要其內容。每當請求返迴內容時fetch函數都會往ch這個channel寫入一個字符串由main函數的第二個for循環來處理打印channel的這個字符串。
main函數中用make函數創建了一個傳遞string類型參數的channel對每一個命令行參數我們都用go這個關鍵字來創建一個goroutine且讓函數在這個goroutine異步執行http.Get方法。這個程序的io.Copy會把響應的Body內容拷貝到ioutil.Discard輸齣流中因爲我們需要這個方法返迴的字節數但是又不想要其內容。每當請求返迴內容時fetch函數都會往ch這個channel寫入一個字符串由main函數的第二個for循環來處理打印channel的這個字符串。
當一個goroutine試在一個channel上做send或者receive操作時這個goroutine會阻塞在調用處直到另一個goroutine往這個channel寫入、或者接收了值這樣兩個goroutine纔會繼續執行操作channel完成之後的邏輯。在這個例子中每一個fetch函數在執行時都會往channel發送一個值(ch <- expression)主函數接收這些值(<-ch)。這個程序中我們用main函數來所有fetch函數傳迴的字符串可以避免在goroutine異步執行時同時結束
當一個goroutine試在一個channel上做send或者receive操作時這個goroutine會阻塞在調用處直到另一個goroutine往這個channel寫入、或者接收了值這樣兩個goroutine纔會繼續執行操作channel完成之後的邏輯。在這個例子中每一個fetch函數在執行時都會往channel發送一個值(ch <- expression)主函數接收這些值(<-ch)。這個程序中我們用main函數來所有fetch函數傳迴的字符串可以避免在goroutine異步執行時同時結束
Exercise 1.10: 找一個數據量比較大的網站用本小節中的程序調研網站的緩存策略對每個URL執行兩遍請求査看兩次時間是否有較大的差且每次取到的響應內容是否一脩改本節中的程序將響應結果輸齣以便於進行對比
Exercise 1.10: 找一個數據量比較大的網站用本小節中的程序調研網站的緩存策略對每個URL執行兩遍請求査看兩次時間是否有較大的差且每次取到的響應內容是否一脩改本節中的程序將響應結果輸齣以便於進行對比

View File

@@ -1,6 +1,6 @@
## 1.7. Web服務
Go的內置庫讓我們寫一個像fetch這樣例子的web服務器變得異常地簡單。在本節中我們會展示一個微型服務器這個服務的功能是返迴當前用戶正在訪問的URL。也就是比如用戶訪問的是http://localhost:8000/hello那麽響應是URL.Path = "hello"。
Go的內置庫讓我們寫一個像fetch這樣例子的web服務器變得異常地簡單。在本節中我們會展示一個微型服務器這個服務的功能是返迴當前用戶正在訪問的URL。也就是比如用戶訪問的是http://localhost:8000/hello那麽響應是URL.Path = "hello"。
```go
gopl.io/ch1/server1
// Server1 is a minimal "echo" server.
@@ -23,9 +23,9 @@ func handler(w http.ResponseWriter, r *http.Request) {
}
```
我們隻用了八九行就實現了這個程序,這都是多虧了標準庫的方法已經幫我們處理了大多數的工作。main函數會將所有發送到/目録下的請求和handler函數關聯起來/開頭的請求其實就是所有發送到當前站點上的請求我們的服務跑在了8000端口上。發送到這個服務的“請求”是一個http.Request類型的對象這個對象中包含了請求中的一繫列相關字段其中就包括我們需要的URL。當請求到達服務器時這個請求會被傳給handler函數來處理這個函數會將/hello這個路徑從請求的URL中解析齣來然後把其發送到響應中我們用的是標準輸齣流的fmt.Fprintf。Web服務會在第7.7節中詳細闡述。
我們隻用了八九行就實現了這個程序,這都是多虧了標準庫的方法已經幫我們處理了大多數的工作。main函數會將所有發送到/目録下的請求和handler函數關聯起來/開頭的請求其實就是所有發送到當前站點上的請求我們的服務跑在了8000端口上。發送到這個服務的“請求”是一個http.Request類型的對象這個對象中包含了請求中的一繫列相關字段其中就包括我們需要的URL。當請求到達服務器時這個請求會被傳給handler函數來處理這個函數會將/hello這個路徑從請求的URL中解析齣來然後把其發送到響應中我們用的是標準輸齣流的fmt.Fprintf。Web服務會在第7.7節中詳細闡述。
讓我們在後運行這個服務程序。如果你的操作繫統是Mac OS X或者Linux那麽在運行命令的末尾加上一個&符號,卽可讓程序簡單地跑在後而在windows下你需要在另外一個命令行窗口去運行這個程序了。
讓我們在後運行這個服務程序。如果你的操作繫統是Mac OS X或者Linux那麽在運行命令的末尾加上一個&符號,卽可讓程序簡單地跑在後而在windows下你需要在另外一個命令行窗口去運行這個程序了。
```
$ go run src/gopl.io/ch1/server1/main.go &
@@ -41,7 +41,7 @@ $ ./fetch http://localhost:8000/help
URL.Path = "/help"
```
另外我們還可以直接在瀏覽器訪問這個URL然後得到返迴結果如圖1.2
另外我們還可以直接在瀏覽器訪問這個URL然後得到返迴結果如圖1.2
![](../images/ch1-02.png)
在這個服務的基礎上疊加特性是很容易的。一種比較實用的脩改是爲訪問的url添加某種狀態。比如下面這個版本輸齣了同樣的內容但是會對請求的次數進行計算對URL的請求結果會包含各種URL被訪問的總次數直接對/count這個URL的訪問要除外。
@@ -83,7 +83,7 @@ func counter(w http.ResponseWriter, r *http.Request) {
}
```
這個服務器有兩個請求處理函數請求的url會決定具體調用哪一個對/count這個url的請求會調用到count這個函數其它所有的url都會調用默認的處理函數。如果你的請求pattern是以/結尾那麽所有以該url爲前綴的url都會被這條規則匹配。在這些代碼的背後服務器每一次接收請求處理時都會另起一個goroutine這樣服務器就可以同一時間處理多數請求。然而在發情況下假如眞的有兩個請求同一時刻去更新count那麽這個值可能不會被正確地增加這個程序可能會被引發一個嚴重的bug競態條件(參見9.1)。爲了避免這個問題,我們必鬚保每次脩改變量的最多隻能有一個goroutine這也就是代碼的mu.Lock()和mu.Unlock()調用將脩改count的所有行爲包在中間的目的。第九章中我們會進一步講解共享變量。
這個服務器有兩個請求處理函數請求的url會決定具體調用哪一個對/count這個url的請求會調用到count這個函數其它所有的url都會調用默認的處理函數。如果你的請求pattern是以/結尾那麽所有以該url爲前綴的url都會被這條規則匹配。在這些代碼的背後服務器每一次接收請求處理時都會另起一個goroutine這樣服務器就可以同一時間處理多數請求。然而在發情況下假如眞的有兩個請求同一時刻去更新count那麽這個值可能不會被正確地增加這個程序可能會被引發一個嚴重的bug競態條件(參見9.1)。爲了避免這個問題,我們必鬚保每次脩改變量的最多隻能有一個goroutine這也就是代碼的mu.Lock()和mu.Unlock()調用將脩改count的所有行爲包在中間的目的。第九章中我們會進一步講解共享變量。
下面是一個更爲豐富的例子handler函數會把請求的http頭和請求的form數據都打印齣來這樣可以讓檢査和調試這個服務更爲方便
@@ -106,7 +106,7 @@ func handler(w http.ResponseWriter, r *http.Request) {
}
```
我們用http.Request這個struct的字段來輸齣下面這樣的內容:
我們用http.Request這個struct的字段來輸齣下面這樣的內容:
```
GET /?q=query HTTP/1.1
@@ -117,7 +117,7 @@ RemoteAddr = "127.0.0.1:59911"
Form["q"] = ["query"]
```
可以看到這的ParseForm被嵌套在了if語句中。Go語言允許這樣的一個簡單的語句結果作爲循環的變量聲明齣現在if語句的最前面這一點對錯誤處理很有用處。我們還可以像下面這樣寫(當然看起來就長了一些)
可以看到這的ParseForm被嵌套在了if語句中。Go語言允許這樣的一個簡單的語句結果作爲循環的變量聲明齣現在if語句的最前面這一點對錯誤處理很有用處。我們還可以像下面這樣寫(當然看起來就長了一些)
```go
err := r.ParseForm()
@@ -125,13 +125,13 @@ if err != nil {
log.Print(err)
}
```
用if和ParseForm結合可以讓代碼更加簡單且可以限製err這個變量的作用域這麽做是很不錯的。我們會在2.7節中講解作用域。
用if和ParseForm結合可以讓代碼更加簡單且可以限製err這個變量的作用域這麽做是很不錯的。我們會在2.7節中講解作用域。
在這些程序中我們看到了很多不同的類型被輸齣到標準輸齣流中。比如前面的fetch程序就把HTTP的響應數據拷貝到了os.Stdout或者在lissajous程序我們輸齣的是一個文件。fetchall程序則完全忽略到了HTTP的響應體隻是計算了一下響應體的大小這個程序中把響應體拷貝到了ioutil.Discard。在本節的web服務器程序中則是用fmt.Fprintf直接寫到了http.ResponseWriter中。
在這些程序中我們看到了很多不同的類型被輸齣到標準輸齣流中。比如前面的fetch程序就把HTTP的響應數據拷貝到了os.Stdout或者在lissajous程序我們輸齣的是一個文件。fetchall程序則完全忽略到了HTTP的響應體隻是計算了一下響應體的大小這個程序中把響應體拷貝到了ioutil.Discard。在本節的web服務器程序中則是用fmt.Fprintf直接寫到了http.ResponseWriter中。
管這三種具體的實現流程不太一樣他們都實現一個共同的接口卽當它們被調用需要一個標準流輸齣時都可以滿足。這個接口叫作io.Writer在7.1節中會詳細討論。
管這三種具體的實現流程不太一樣他們都實現一個共同的接口卽當它們被調用需要一個標準流輸齣時都可以滿足。這個接口叫作io.Writer在7.1節中會詳細討論。
Go的接口機製會在第7章中講解爲了在這簡單明接口能做什麽,讓我們簡單地將這的web服務器和之前寫的lissajous函數結合起來這樣GIF動可以被寫到HTTP的客戶端而不是之前的標準輸齣流。隻要在web服務器的代碼加入下面這幾行。
Go的接口機製會在第7章中講解爲了在這簡單明接口能做什麽,讓我們簡單地將這的web服務器和之前寫的lissajous函數結合起來這樣GIF動可以被寫到HTTP的客戶端而不是之前的標準輸齣流。隻要在web服務器的代碼加入下面這幾行。
```
handler := func(w http.ResponseWriter, r *http.Request) {
@@ -150,10 +150,10 @@ http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
HandleFunc函數的第二個參數是一個函數的字面值也就是一個在使用時定義的匿名函數。這些內容我們會在5.6節中講解。
做完這些脩改之後,在瀏覽器訪問http://localhost:8000。每次你載入這個頁面都可以看到一個像圖1.3那樣的動
做完這些脩改之後,在瀏覽器訪問http://localhost:8000。每次你載入這個頁面都可以看到一個像圖1.3那樣的動
```
Exercise 1.12:脩改Lissajour服務從URL讀取變量比如你可以訪問http://localhost:8000/?cycles=20這個URL這樣訪問可以將程序的cycles默認的5脩改爲20。字符串轉換爲數字可以調用strconv.Atoi函數。你可以在dodoc査看strconv.Atoi的詳細明。
Exercise 1.12:脩改Lissajour服務從URL讀取變量比如你可以訪問http://localhost:8000/?cycles=20這個URL這樣訪問可以將程序的cycles默認的5脩改爲20。字符串轉換爲數字可以調用strconv.Atoi函數。你可以在dodoc査看strconv.Atoi的詳細明。
```
![](../images/ch1-03.png)

View File

@@ -1,8 +1,8 @@
## 1.8. 本章要點
本章中對Go語言做了一些介紹實際上Go語言還有很多方面在這有限的篇幅中還沒有覆蓋到。這我們會把沒有講到的內容也做一些簡單的介紹,這樣讀者在之後看到完整的內容之前,也可以簡單有個印象。
本章中對Go語言做了一些介紹實際上Go語言還有很多方面在這有限的篇幅中還沒有覆蓋到。這我們會把沒有講到的內容也做一些簡單的介紹,這樣讀者在之後看到完整的內容之前,也可以簡單有個印象。
控製流在本章我們隻介紹了if控製和for但是沒有提到switch多路選擇。這是一個簡單的switch的例子
控製流在本章我們隻介紹了if控製和for但是沒有提到switch多路選擇。這是一個簡單的switch的例子
```go
switch coinflip() {
@@ -15,9 +15,9 @@ switch coinflip() {
}
```
在翻轉硬幣的時候,例子的coinflip函數返迴幾種不同的結果每一個case都會對應個返迴結果需要註意Go語言不需要顯式地去在每一個case後寫break語言默認執行完case後的邏輯語句會自動退齣。當然了如果你想要相鄰的幾個case都執行同一邏輯的話需要自己顯式地寫上一個fallthrough語句來覆蓋這種默認行爲。不過fallthrough語句在一般的編程中用到得很少。
在翻轉硬幣的時候,例子的coinflip函數返迴幾種不同的結果每一個case都會對應個返迴結果需要註意Go語言不需要顯式地去在每一個case後寫break語言默認執行完case後的邏輯語句會自動退齣。當然了如果你想要相鄰的幾個case都執行同一邏輯的話需要自己顯式地寫上一個fallthrough語句來覆蓋這種默認行爲。不過fallthrough語句在一般的編程中用到得很少。
go的switch還可以不帶操作對象可以直接羅列多種條件像其它語言面的多個if else一樣下面是一個例子
go的switch還可以不帶操作對象可以直接羅列多種條件像其它語言面的多個if else一樣下面是一個例子
```go
func Signum(x int) int {
@@ -36,7 +36,7 @@ func Signum(x int) int {
像for和if控製語句一樣switch也可以緊跟一個簡短的變量聲明一個自增表達式、賦值語句或者一個函數調用。
break和continue語句會改變控製流。和其它語言中的break和continue一樣break會中斷當前的循環開始執行循環之後的內容而continue會中跳過當前循環開始執行下一次循環。這兩個語句除了可以控製for循環還可以用來控製switch和select語句(之後會講到)在1.3節中我們看到continue會跳過是內層的循環如果我們想跳過的是更外層的循環的話我們可以在相應的位置加上label這樣break和continue就可以根據我們的想法來continue和break任意循環。這看起來甚至有點像goto語句的作用了。當然一般程序員也不會用到這種操作。這兩種行爲更多地被用到機器生成的代碼中。
break和continue語句會改變控製流。和其它語言中的break和continue一樣break會中斷當前的循環開始執行循環之後的內容而continue會中跳過當前循環開始執行下一次循環。這兩個語句除了可以控製for循環還可以用來控製switch和select語句(之後會講到)在1.3節中我們看到continue會跳過是內層的循環如果我們想跳過的是更外層的循環的話我們可以在相應的位置加上label這樣break和continue就可以根據我們的想法來continue和break任意循環。這看起來甚至有點像goto語句的作用了。當然一般程序員也不會用到這種操作。這兩種行爲更多地被用到機器生成的代碼中。
命名類型類型聲明使得我們可以很方便地給一個特殊類型一個名字。因爲struct類型聲明通常非常地長所以我們總要給這種struct取一個名字。本章中就有這樣一個例子2d點類型
```go
@@ -48,13 +48,13 @@ var p Point
類型聲明和命名類型會在第二章中介紹。
Go語言提供了指。指是一種直接存儲了變量的內存地址的數據結構。在其它語言中比如C語言是完全不受約束的。在另外一些語言中,指一般被稱爲“引用”,除了到處傳遞這些指之外,不能對這些指做太多事情。go在這兩種圍中取得了一個平衡。指是可見的內存地址,&操作符可以返迴一個變量的內存地址,且*操作符可以取指指向的變量內容但是在go語言沒有指運算也就是不像c語言可以對指進行加或減操作。我們會在2.3.2中進行詳細介紹。
Go語言提供了指。指是一種直接存儲了變量的內存地址的數據結構。在其它語言中比如C語言是完全不受約束的。在另外一些語言中,指一般被稱爲“引用”,除了到處傳遞這些指之外,不能對這些指做太多事情。go在這兩種圍中取得了一個平衡。指是可見的內存地址,&操作符可以返迴一個變量的內存地址,且*操作符可以取指指向的變量內容但是在go語言沒有指運算也就是不像c語言可以對指進行加或減操作。我們會在2.3.2中進行詳細介紹。
方法和接口方法是和命名類型關聯的一類函數。Go語言比較特殊的是方法可以被關聯到任意一種命名類型。在第六章我們會詳細地講方法。接口是一種抽象類型,這種類型可以讓我們以同樣的方式來處理不同的固有類型,不用關心它們的具體實現,而隻需要關註它們提供的方法。第七章中會詳細明這些內容。
方法和接口方法是和命名類型關聯的一類函數。Go語言比較特殊的是方法可以被關聯到任意一種命名類型。在第六章我們會詳細地講方法。接口是一種抽象類型,這種類型可以讓我們以同樣的方式來處理不同的固有類型,不用關心它們的具體實現,而隻需要關註它們提供的方法。第七章中會詳細明這些內容。
包(packages)Go語言提供了一些很好用的package且這些package是可以擴展的。Go語言社區已經創造且分享了很多很多。所以Go語言編程大多數情況下就是用已有的package來寫我們自己的代碼。通過這本書我們會講解一些重要的標準庫內的package但是還是有很多我們沒有篇幅去明,因爲我們沒法在這樣的厚度的書去做一部代碼大全。
包(packages)Go語言提供了一些很好用的package且這些package是可以擴展的。Go語言社區已經創造且分享了很多很多。所以Go語言編程大多數情況下就是用已有的package來寫我們自己的代碼。通過這本書我們會講解一些重要的標準庫內的package但是還是有很多我們沒有篇幅去明,因爲我們沒法在這樣的厚度的書去做一部代碼大全。
在你開始寫一個新程序之前最好先去檢査一下是不是已經有了現成的庫可以幫助你更高效地完成這件事情。你可以在https://golang.org/pkg 和 https://godoc.org 中找到標準庫和社區寫的package。godoc這個工具可以讓你直接在本地命令行讀標準庫的文檔。比如下面這個例子。
在你開始寫一個新程序之前最好先去檢査一下是不是已經有了現成的庫可以幫助你更高效地完成這件事情。你可以在https://golang.org/pkg 和 https://godoc.org 中找到標準庫和社區寫的package。godoc這個工具可以讓你直接在本地命令行讀標準庫的文檔。比如下面這個例子。
```
$ go doc http.ListenAndServe
@@ -64,7 +64,7 @@ func ListenAndServe(addr string, handler Handler) error
calls Serve with handler to handle requests on incoming connections.
...
```
註釋:我們之前已經提到過了在源文件的開頭寫的註釋是這個源文件的文檔。在每一個函數之前寫一個明函數行爲的註釋也是一個好習慣。這些慣例很重要因爲這些內容會被像godoc這樣的工具檢測到且在執行命令時顯示這些註釋。具體可以參考10.7.4。
註釋:我們之前已經提到過了在源文件的開頭寫的註釋是這個源文件的文檔。在每一個函數之前寫一個明函數行爲的註釋也是一個好習慣。這些慣例很重要因爲這些內容會被像godoc這樣的工具檢測到且在執行命令時顯示這些註釋。具體可以參考10.7.4。
多行註釋可以用/* ... */來包裹,和其它大多數語言一樣。在文件一開頭的註釋一般都是這種形式,或者一大段的解釋性的註釋文字也會被這符號包住,來避免每一行都需要加//。在註釋中//和/*是沒什麽意義的,所以不要在註釋中再嵌入註釋。

View File

@@ -1,4 +1,4 @@
# 第1章 入門
本章會介紹Go語言的一些基本組件。我們希望用信息和例子快帶你入門。本章和之後章節的例子都是對眞實的開發案例給齣。本章我們隻是簡單地爲你介紹一些Go的入門例子從簡單的文件處理、圖像處理到互聯網發客戶端和服務端程序。當然,在第一章我們不會詳地一一去明細枝末節,不過用這些程序來學習一門新語言肯定是很有效的。
當你學習一門新語言時你會有去用這門新語言去重寫自己以前熟悉語言例子的傾向。在學習Go的過程中量避免這麽做。我們會向你演示如何纔能寫齣好的Go程序所以請使用這的代碼作爲你寫自己的Go程序時的指南。
本章會介紹Go語言的一些基本組件。我們希望用信息和例子快帶你入門。本章和之後章節的例子都是對眞實的開發案例給齣。本章我們隻是簡單地爲你介紹一些Go的入門例子從簡單的文件處理、圖像處理到互聯網發客戶端和服務端程序。當然,在第一章我們不會詳地一一去明細枝末節,不過用這些程序來學習一門新語言肯定是很有效的。
當你學習一門新語言時你會有去用這門新語言去重寫自己以前熟悉語言例子的傾向。在學習Go的過程中量避免這麽做。我們會向你演示如何纔能寫齣好的Go程序所以請使用這的代碼作爲你寫自己的Go程序時的指南。