mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-12-18 19:54:21 +08:00
make loop
This commit is contained in:
@@ -48,7 +48,7 @@ Hello, 世界
|
||||
gopl.io/ch1/helloworld
|
||||
```
|
||||
|
||||
如果你執行 `go get gopl.io/ch1/helloworld` 命令,go命令能夠自己從網上穫取到這些代碼(譯註:需要先安裝Git或Hg之類的版本管理工具,併將對應的命令添加到PATH環境變量中),併且將這些代碼放到對應的目録中(譯註:序言已經提及,需要先設置好GOPATH環境變量,下載的代碼會放在 $GOPATH/src/gopl.io/ch1/helloworld 目録)。更詳細的介紹在2.6和10.7章節中。
|
||||
如果你執行 `go get gopl.io/ch1/helloworld` 命令,go命令能夠自己從網上獲取到這些代碼(譯註:需要先安裝Git或Hg之類的版本管理工具,併將對應的命令添加到PATH環境變量中),併且將這些代碼放到對應的目録中(譯註:序言已經提及,需要先設置好GOPATH環境變量,下載的代碼會放在 $GOPATH/src/gopl.io/ch1/helloworld 目録)。更詳細的介紹在2.6和10.7章節中。
|
||||
|
||||
我們來討論一下程序本身。Go語言的代碼是通過package來組織的,package的概念和你知道的其它語言里的libraries或者modules概念比較類似。一個package會包含一個或多個.go結束的源代碼文件。每一個源文件都是以一個package xxx的聲明語句開頭的,比如我們的例子里就是package main。這行聲明語句表示該文件是屬於哪一個package,緊跟着是一繫列import的package名,表示這個文件中引入的package。再之後是本文件本身的代碼。
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
## 1.2. 命令行參數
|
||||
|
||||
大多數的程序都是處理輸入,産生輸出;這也正是“計算”的定義。但是一個程序要如何穫取輸入呢?一些程序會生成自己的數據,但通常情況下,輸入都來自於程序外部:比如文件、網絡連接、其它程序的輸出、用戶的鍵盤、命令行的參數或其它類似輸入源。下面幾個例子會討論其中的一些輸入類型,首先是命令行參數。
|
||||
大多數的程序都是處理輸入,産生輸出;這也正是“計算”的定義。但是一個程序要如何獲取輸入呢?一些程序會生成自己的數據,但通常情況下,輸入都來自於程序外部:比如文件、網絡連接、其它程序的輸出、用戶的鍵盤、命令行的參數或其它類似輸入源。下面幾個例子會討論其中的一些輸入類型,首先是命令行參數。
|
||||
|
||||
os這個package提供了操作繫統無關(跨平台)的,與繫統交互的一些函數和相關的變量,運行時程序的命令行參數可以通過os包中一個叫Args的這個變量來穫取;當在os包外部使用該變量時,需要用os.Args來訪問。
|
||||
os這個package提供了操作繫統無關(跨平台)的,與繫統交互的一些函數和相關的變量,運行時程序的命令行參數可以通過os包中一個叫Args的這個變量來獲取;當在os包外部使用該變量時,需要用os.Args來訪問。
|
||||
|
||||
os.Args這個變量是一個字符串(string)的slice(譯註:slice和Python語言中的切片類似,是一個簡版的動態數組),slice在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]這個slice,0 ≤ m ≤ n ≤ len(s),包含n-m個元素。
|
||||
os.Args這個變量是一個字符串(string)的slice(譯註:slice和Python語言中的切片類似,是一個簡版的動態數組),slice在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]這個slice,0 ≤ 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:]。
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ func main() {
|
||||
|
||||
和我們前面提到的for循環一樣,在if條件的兩邊,我們也不需要加括號,但是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()]++這個語句和下面的兩句是等價的:
|
||||
@@ -76,7 +76,7 @@ Printf有一大堆這種轉換,Go語言程序員把這些叫做verb(動詞
|
||||
|
||||
dup1中的程序還包含了一個\t和\n的格式化字符串。在字符串中會以這些特殊的轉義字符來表示不可見字符。Printf默認不會在輸出內容後加上換行符。按照慣例,用來格式化的函數都會在末尾以f字母結尾(譯註:f後綴對應format或fmt縮寫),比如log.Printf,fmt.Errorf,同時還有一繫列對應以ln結尾的函數(譯註:ln後綴對應line縮寫),這些函數默認以%v來格式化他們的參數,併且會在輸出結束後在最後自動加上一個換行符。
|
||||
|
||||
許多程序從標準輸入中讀取數據,像上面的例子那樣。除此之外,還可能從一繫列的文件中讀取。下一個dup程序就是從標準輸入中讀到一些文件名,用os.Open函數來打開每一個文件穫取內容的。
|
||||
許多程序從標準輸入中讀取數據,像上面的例子那樣。除此之外,還可能從一繫列的文件中讀取。下一個dup程序就是從標準輸入中讀到一些文件名,用os.Open函數來打開每一個文件獲取內容的。
|
||||
|
||||
```go
|
||||
gopl.io/ch1/dup2
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
## 1.5. 穫取URL
|
||||
## 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
|
||||
@@ -34,7 +34,7 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
這個程序從兩個package中導入了函數,net/http和io/ioutil包,http.Get函數是創建HTTP請求的函數,如果穫取過程沒有出錯,那麽會在resp這個結構體中得到訪問的請求結果。resp的Body字段包括一個可讀的服務器響應流。這之後ioutil.ReadAll函數從response中讀取到全部內容;其結果保存在變量b中。resp.Body.Close這一句會關閉resp的Body流,防止資源洩露,Printf函數會將結果b寫出到標準輸出流中。
|
||||
這個程序從兩個package中導入了函數,net/http和io/ioutil包,http.Get函數是創建HTTP請求的函數,如果獲取過程沒有出錯,那麽會在resp這個結構體中得到訪問的請求結果。resp的Body字段包括一個可讀的服務器響應流。這之後ioutil.ReadAll函數從response中讀取到全部內容;其結果保存在變量b中。resp.Body.Close這一句會關閉resp的Body流,防止資源洩露,Printf函數會將結果b寫出到標準輸出流中。
|
||||
|
||||
```
|
||||
$ go build gopl.io/ch1/fetch
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
## 1.6. 併發穫取多個URL
|
||||
## 1.6. 併發獲取多個URL
|
||||
|
||||
Go語言最有意思併且最新奇的特性就是其對併發編程的支持了。併發編程是一個大話題,在第八章和第九章中會專門講到。這里我們隻淺嚐輒止地來體驗一下Go語言里的goroutine和channel。
|
||||
|
||||
下面的例子fetchall,和上面的fetch程序所要做的工作是一致的,但是這個fetchall的特别之處在於它會同時去穫取所有的URL,所以這個程序的穫取時間不會超過執行時間最長的那一個任務,而不會像前面的fetch程序一樣,執行時間是所有任務執行時間之和。這次的fetchall程序隻會打印穫取的內容大小和經過的時間,不會像上面那樣打印出穫取的內容。
|
||||
下面的例子fetchall,和上面的fetch程序所要做的工作是一致的,但是這個fetchall的特别之處在於它會同時去獲取所有的URL,所以這個程序的獲取時間不會超過執行時間最長的那一個任務,而不會像前面的fetch程序一樣,執行時間是所有任務執行時間之和。這次的fetchall程序隻會打印獲取的內容大小和經過的時間,不會像上面那樣打印出獲取的內容。
|
||||
|
||||
```go
|
||||
gopl.io/ch1/fetchall
|
||||
@@ -65,4 +65,4 @@ main函數中用make函數創建了一個傳遞string類型參數的channel,
|
||||
|
||||
當一個goroutine嚐試在一個channel上做send或者receive操作時,這個goroutine會阻塞在調用處,直到另一個goroutine往這個channel里寫入、或者接收了值,這樣兩個goroutine才會繼續執行操作channel完成之後的邏輯。在這個例子中,每一個fetch函數在執行時都會往channel里發送一個值(ch <- expression),主函數接收這些值(<-ch)。這個程序中我們用main函數來所有fetch函數傳迴的字符串,可以避免在goroutine異步執行時同時結束。
|
||||
|
||||
**練習 1.10:** 找一個數據量比較大的網站,用本小節中的程序調研網站的緩存策略,對每個URL執行兩遍請求,査看兩次時間是否有較大的差别,併且每次穫取到的響應內容是否一致,脩改本節中的程序,將響應結果輸出,以便於進行對比。
|
||||
**練習 1.10:** 找一個數據量比較大的網站,用本小節中的程序調研網站的緩存策略,對每個URL執行兩遍請求,査看兩次時間是否有較大的差别,併且每次獲取到的響應內容是否一致,脩改本節中的程序,將響應結果輸出,以便於進行對比。
|
||||
|
||||
@@ -49,7 +49,7 @@ var p Point
|
||||
|
||||
類型聲明和命名類型會在第二章中介紹。
|
||||
|
||||
**指針:** Go語言提供了指針。指針是一種直接存儲了變量的內存地址的數據類型。在其它語言中,比如C語言,指針操作是完全不受約束的。在另外一些語言中,指針一般被處理爲“引用”,除了到處傳遞這些指針之外,併不能對這些指針做太多事情。Go語言在這兩種范圍中取了一種平衡。指針是可見的內存地址,&操作符可以返迴一個變量的內存地址,併且*操作符可以穫取指針指向的變量內容,但是在Go語言里沒有指針運算,也就是不能像c語言里可以對指針進行加或減操作。我們會在2.3.2中進行詳細介紹。
|
||||
**指針:** Go語言提供了指針。指針是一種直接存儲了變量的內存地址的數據類型。在其它語言中,比如C語言,指針操作是完全不受約束的。在另外一些語言中,指針一般被處理爲“引用”,除了到處傳遞這些指針之外,併不能對這些指針做太多事情。Go語言在這兩種范圍中取了一種平衡。指針是可見的內存地址,&操作符可以返迴一個變量的內存地址,併且*操作符可以獲取指針指向的變量內容,但是在Go語言里沒有指針運算,也就是不能像c語言里可以對指針進行加或減操作。我們會在2.3.2中進行詳細介紹。
|
||||
|
||||
**方法和接口:** 方法是和命名類型關聯的一類函數。Go語言里比較特殊的是方法可以被關聯到任意一種命名類型。在第六章我們會詳細地講方法。接口是一種抽象類型,這種類型可以讓我們以同樣的方式來處理不同的固有類型,不用關心它們的具體實現,而隻需要關註它們提供的方法。第七章中會詳細説明這些內容。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user