回到简体

This commit is contained in:
chai2010
2016-02-15 11:06:34 +08:00
parent 9e878f9944
commit 2b37b23285
177 changed files with 2354 additions and 2354 deletions

View File

@@ -1,6 +1,6 @@
## 1.1. Hello, World
們以現已成爲傳統的“hello world”案例來開始吧, 這個例子首次出現於1978年出版的C語言聖經[《The C Programming Language》](http://s3-us-west-2.amazonaws.com/belllabs-microsite-dritchie/cbook/index.html)[^1]。C言是直接影Go語言設計的語言之一。這個例子體現了Go言一些核心理念。
们以现已成为传统的“hello world”案例来开始吧, 这个例子首次出现于1978年出版的C语言圣经[《The C Programming Language》](http://s3-us-west-2.amazonaws.com/belllabs-microsite-dritchie/cbook/index.html)[^1]。C言是直接影Go语言设计的语言之一。这个例子体现了Go言一些核心理念。
<u><i>gopl.io/ch1/helloworld</i></u>
```go
@@ -13,76 +13,76 @@ func main() {
}
```
Go是一門編譯型語Go言的工具鏈將源代及其依賴轉換成計算機的機器指令[^2]。Go言提供的工具都通過一個單獨的命令`go`調用,`go`命令有一列子命令。最簡單的一子命令就是run。這個命令編譯一個或多以.go尾的源文件,鏈接庫文件,併運行最生成的可行文件。(本使用$表示命令行提示符。)
Go是一门编译型语Go言的工具链将源代及其依赖转换成计算机的机器指令[^2]。Go言提供的工具都通过一个单独的命令`go`用,`go`命令有一列子命令。最简单的一子命令就是run。这个命令编译一个或多以.go尾的源文件,链接库文件,并运行最生成的可行文件。(本使用$表示命令行提示符。)
```
$ go run helloworld.go
```
意外,這個命令會輸出:
意外,这个命令会输出:
```
Hello, 世界
```
Go言原生支持Unicode它可以理全世界任何言的文本。
Go言原生支持Unicode它可以理全世界任何言的文本。
如果不是一次性實驗,你肯定希望能夠編譯這個程序,保存編譯結果以備將來之用。可以用build子命令
如果不是一次性实验,你肯定希望能够编译这个程序,保存编译结果以备将来之用。可以用build子命令
```
$ go build helloworld.go
```
這個命令生成一個名爲helloworld的可行的二進製文件[^3],之你可以隨時運行它[^4],不需任何理[^5]。
这个命令生成一个名为helloworld的可行的二进制文件[^3],之你可以随时运行它[^4],不需任何理[^5]。
```
$ ./helloworld
Hello, 世界
```
中, 所有的示例代上都有一行標記,利用這些標記, 可以[gopl.io](http://gopl.io)站上本書源碼倉庫里獲取代
中, 所有的示例代上都有一行标记,利用这些标记, 可以[gopl.io](http://gopl.io)站上本书源码仓库里获取代
```
gopl.io/ch1/helloworld
```
`go get gopl.io/ch1/helloworld` 命令,就會從網上獲取代碼,併放到對應目録中[^6]。2.6和10.7節有這方面更詳細的介
`go get gopl.io/ch1/helloworld` 命令,就会从网上获取代码,并放到对应目录中[^6]。2.6和10.7节有这方面更详细的介
來討論下程序本身。Go言的代碼通過**包**package組織,包類似於其它言里的libraries或者模modules。一包由位於單個目録下的一或多.go源代文件成, 目録定義包的作用。每源文件都以一`package`聲明語句開始,這個例子里就是`package main`, 表示文件屬於哪個包,跟着一繫列導import的包是存儲在這個文件里的程序句。
来讨论下程序本身。Go言的代码通过**包**package组织,包类似于其它言里的libraries或者模modules。一包由位于单个目录下的一或多.go源代文件成, 目录定义包的作用。每源文件都以一`package`声明语句开始,这个例子里就是`package main`, 表示文件属于哪个包,跟着一系列导import的包是存储在这个文件里的程序句。
Go的標準庫提供了100多包,以支持常功能,如入、出、排序以及文本理。比如`fmt`包,就含有格式化出、接收入的函`Println`是其中一個基礎函數,可以打印以空格隔的一或多值,在最添加一個換行符,從而輸出一整行。
Go的标准库提供了100多包,以支持常功能,如入、出、排序以及文本理。比如`fmt`包,就含有格式化出、接收入的函`Println`是其中一个基础函数,可以打印以空格隔的一或多值,在最添加一个换行符,从而输出一整行。
`main`包比特殊。它定了一個獨立可行的程序,而不是一個庫。在`main`里的`main` *函* 也很特殊,它是整程序執行時的入口[^7]。`main`所做的事情就是程序做的。然了,`main`一般調用其它包里的函完成很多工作, 比如`fmt.Println`
`main`包比特殊。它定了一个独立可行的程序,而不是一个库。在`main`里的`main` *函* 也很特殊,它是整程序执行时的入口[^7]。`main`所做的事情就是程序做的。然了,`main`一般用其它包里的函完成很多工作, 比如`fmt.Println`
須告訴編譯器源文件需要哪些包,就是`import`明以及隨後`package`明扮演的角色。hello world例子用到了一包,大多程序需要入多包。
须告诉编译器源文件需要哪些包,就是`import`明以及随后`package`明扮演的角色。hello world例子用到了一包,大多程序需要入多包。
須恰當導入需要的包,缺少了必要的包或者入了不需要的包,程序都無法編譯通過。這項嚴格要求避免了程序開發過程中引入未使用的包[^8]。
须恰当导入需要的包,缺少了必要的包或者入了不需要的包,程序都无法编译通过。这项严格要求避免了程序开发过程中引入未使用的包[^8]。
`import`明必跟在文件的`package`明之後。隨後,則是組成程序的函數、變量、常量、型的聲明語句(分别由關鍵`func`, `var`, `const`, `type`)。這些內容的聲明順序併不重要[^9]。這個例子的程序已經盡可能短了,隻聲明了一個函數, 其中隻調用了一其他函數。爲了節省篇幅,有些候, 示例程序省略`package``import`明,但是,這些聲明在源代里有,且必得有才能編譯
`import`明必跟在文件的`package`明之后。随后,则是组成程序的函数、变量、常量、型的声明语句(分别由关键`func`, `var`, `const`, `type`)。这些内容的声明顺序并不重要[^9]。这个例子的程序已经尽可能短了,只声明了一个函数, 其中只调用了一其他函数。为了节省篇幅,有些候, 示例程序省略`package``import`明,但是,这些声明在源代里有,且必得有才能编译
個函數的聲明由`func`關鍵字、函名、參數列表、返值列表(這個例子里的`main`數參數列表和返值都是空的)以及包含在大括里的函數體組成。第五章一步考察函
个函数的声明由`func`关键字、函名、参数列表、返值列表(这个例子里的`main`数参数列表和返值都是空的)以及包含在大括里的函数体组成。第五章一步考察函
Go言不需要在句或者明的末尾添加分,除非一行上有多條語句。實際上,編譯器會主動把特定符號後的換行符轉換爲分號, 因此行符添加的位置會影響Go代的正解析[^10]。。舉個例子, 函的左括`{``func`數聲明在同一行上, 且位末尾,不能占一行,而在表`x + y`中,可在`+`後換行,不能在`+`行。
Go言不需要在句或者明的末尾添加分,除非一行上有多条语句。实际上,编译器会主动把特定符号后的换行符转换为分号, 因此行符添加的位置会影响Go代的正解析[^10]。。举个例子, 函的左括`{``func`数声明在同一行上, 且位末尾,不能占一行,而在表`x + y`中,可在`+`后换行,不能在`+`行。
Go言在代格式上采取了很硬的度。`gofmt`工具把代格式化爲標準格式[^12]`go`工具中的`fmt`子命令會對指定包, 否則默認爲當前目, 中所有.go源文件`gofmt`命令。本中的所有代都被gofmt。你也應該養成格式化自己的代碼的習慣。以法令方式規定標準的代格式可以避免無盡的無意義的瑣碎爭執[^13]。更重要的是,這樣可以做多種自動源碼轉換如果放任Go言代格式,這些轉換就不大可能了。
Go言在代格式上采取了很硬的度。`gofmt`工具把代格式化为标准格式[^12]`go`工具中的`fmt`子命令会对指定包, 否则默认为当前目, 中所有.go源文件`gofmt`命令。本中的所有代都被gofmt。你也应该养成格式化自己的代码的习惯。以法令方式规定标准的代格式可以避免无尽的无意义的琐碎争执[^13]。更重要的是,这样可以做多种自动源码转换如果放任Go言代格式,这些转换就不大可能了。
很多文本編輯器都可以配置保存文件時自動執`gofmt`這樣你的源代碼總會被恰地格式化。還有個相關的工具,`goimports`,可以根據代碼需要, 自地添加或`import`明。這個工具併沒有包含在標準的分包中,可以用下面的命令安
很多文本编辑器都可以配置保存文件时自动执`gofmt`这样你的源代码总会被恰地格式化。还有个相关的工具,`goimports`,可以根据代码需要, 自地添加或`import`明。这个工具并没有包含在标准的分包中,可以用下面的命令安
```
$ go get golang.org/x/tools/cmd/goimports
```
對於大多數用戶來説,下載、編譯包、運行測試用例、察看Go言的文等等常用功能都可以用go的工具完成。10.7節詳細介紹這些知
对于大多数用户来说,下载、编译包、运行测试用例、察看Go言的文等等常用功能都可以用go的工具完成。10.7节详细介绍这些知
[^1]: 本作者之一Brian W. Kernighan也是《The C Programming Language》一的作者。
[^2]: 靜態編譯
[^3]: Windows繫統下生成的可行文件是helloworld.exe增加了.exe後綴名。
[^4]: 在Windows繫統下在命令行直接入helloworld.exe命令行。
[^5]: 因爲靜態編譯,所以不用心在繫統庫更新的時候衝突,幸福感滿滿
[^6]: 需要先安Git或Hg之的版本管理工具,併將對應的命令添加到PATH環境變量中。序言已提及,需要先置好GOPATH環境變量,下的代碼會放在`$GOPATH/src/gopl.io/ch1/helloworld`
[^7]: C繫語言差不多都這樣
[^8]: Go語言編譯過程沒有警告信息,爭議特性之一。
[^9]: 最好是定一下范。
[^10]: 比如行末是標識符、整、浮點數、虛數、字符或字符串文字、關鍵`break``continue``fallthrough``return`中的一個、運算符和分隔符`++``--``)``]``}`中的一
[^11]: 以+尾的話不會被插入分分隔符但是以x尾的話則會被分分隔符,從而導致編譯錯誤
[^12]: 這個格式化工具有任何可以調整代格式的參數Go言就是這麽任性。
[^13]: 也致了Go言的TIOBE排名低,因缺少撕逼的話題
[^1]: 本作者之一Brian W. Kernighan也是《The C Programming Language》一的作者。
[^2]: 静态编译
[^3]: Windows系统下生成的可行文件是helloworld.exe增加了.exe后缀名。
[^4]: 在Windows系统下在命令行直接入helloworld.exe命令行。
[^5]: 因为静态编译,所以不用心在系统库更新的时候冲突,幸福感满满
[^6]: 需要先安Git或Hg之的版本管理工具,并将对应的命令添加到PATH环境变量中。序言已提及,需要先置好GOPATH环境变量,下的代码会放在`$GOPATH/src/gopl.io/ch1/helloworld`
[^7]: C系语言差不多都这样
[^8]: Go语言编译过程没有警告信息,争议特性之一。
[^9]: 最好是定一下范。
[^10]: 比如行末是标识符、整、浮点数、虚数、字符或字符串文字、关键`break``continue``fallthrough``return`中的一个、运算符和分隔符`++``--``)``]``}`中的一
[^11]: 以+尾的话不会被插入分分隔符但是以x尾的话则会被分分隔符,从而导致编译错误
[^12]: 这个格式化工具有任何可以整代格式的参数Go言就是这么任性。
[^13]: 也致了Go言的TIOBE排名低,因缺少撕逼的话题

View File

@@ -1,14 +1,14 @@
## 1.2. 命令行參數
## 1.2. 命令行参数
大多的程序都是處理輸入,産生輸出;也正是“算”的定。但是, 程序如何取要理的輸入數據呢?一些程序生成自己的數據,但通常情下,輸入來自於程序外部:文件、網絡連接、其它程序的出、敲鍵盤的用、命令行參數或其它類似輸入源。下面幾個例子會討論其中幾個輸入源,首先是命令行參數
大多的程序都是处理输入,产生输出;也正是“算”的定。但是, 程序如何取要理的输入数据呢?一些程序生成自己的数据,但通常情下,输入来自于程序外部:文件、网络连接、其它程序的出、敲键盘的用、命令行参数或其它类似输入源。下面几个例子会讨论其中几个输入源,首先是命令行参数
`os`包以跨平台的方式,提供了一些操作繫統交互的函數和變量。程序的命令行參數可從os包的Args變量獲os包外部使用os.Args訪問該變量。
`os`包以跨平台的方式,提供了一些操作系统交互的函数和变量。程序的命令行参数可从os包的Args变量获os包外部使用os.Args访问该变量。
os.Args量是一字符串string的*切片*slice譯註slice和Python言中的切片似,是一個簡版的動態數組切片是Go言的基概念,稍後詳細介紹。現在先把切片s當作數組元素序列, 序列的成長度動態變化, 用`s[i]`訪問單個元素,用`s[m:n]`取子序列(譯註和python里的法差不多)。序列的元素數目爲len(s)。和大多數編程語言類似,區間索引Go言里也采用左閉右開形式, 卽,區間包括第一索引元素,不包括最後一個, 因爲這樣可以簡化邏輯。(譯註比如a = [1, 2, 3, 4, 5], a[0:3] = [1, 2, 3],不包含最後一個元素。比如s[m:n]這個切片0 ≤ m ≤ n ≤ len(s)包含n-m元素。
os.Args量是一字符串string的*切片*slice译注slice和Python言中的切片似,是一个简版的动态数组切片是Go言的基概念,稍后详细介绍。现在先把切片s当作数组元素序列, 序列的成长度动态变化, 用`s[i]`访问单个元素,用`s[m:n]`取子序列(译注和python里的法差不多)。序列的元素数目为len(s)。和大多数编程语言类似,区间索引Go言里也采用左闭右开形式, 即,区间包括第一索引元素,不包括最后一个, 因为这样可以简化逻辑。(译注比如a = [1, 2, 3, 4, 5], a[0:3] = [1, 2, 3],不包含最后一个元素。比如s[m:n]这个切片0 ≤ m ≤ n ≤ len(s)包含n-m元素。
os.Args的第一元素os.Args[0], 是命令本身的名字;其它的元素是程序啟動時傳給它的參數。s[m:n]形式的切片表式,産生從第m元素到第n-1元素的切片,下例子用到的元素包含在os.Args[1:len(os.Args)]切片中。如果省略切片表式的m或n會默認傳入0或len(s),因此前面的切片可以簡寫成os.Args[1:]。
os.Args的第一元素os.Args[0], 是命令本身的名字;其它的元素是程序启动时传给它的参数。s[m:n]形式的切片表式,产生从第m元素到第n-1元素的切片,下例子用到的元素包含在os.Args[1:len(os.Args)]切片中。如果省略切片表式的m或n会默认传入0或len(s),因此前面的切片可以简写成os.Args[1:]。
下面是Unix里echo命令的一份實現echo把它的命令行參數打印成一行。程序入了兩個包,用括把它括起來寫成列表形式, 而有分開寫成獨立的`import`明。兩種形式都合法,列表形式習慣上用得多。包導入順序併不重要gofmt工具格式化按照字母順序對包名排序。(示例有多版本,我們會對示例編號, 這樣可以明確當前正在討論的是哪。)
下面是Unix里echo命令的一份实现echo把它的命令行参数打印成一行。程序入了两个包,用括把它括起来写成列表形式, 而有分开写成独立的`import`明。两种形式都合法,列表形式习惯上用得多。包导入顺序并不重要gofmt工具格式化按照字母顺序对包名排序。(示例有多版本,我们会对示例编号, 这样可以明确当前正在讨论的是哪。)
<u><i>gopl.io/ch1/echo1</i></u>
```go
@@ -30,37 +30,37 @@ func main() {
}
```
註釋語句以`//`開頭。對於程序員來説//之到行末之所有的容都是註釋,被編譯器忽略。按照例,我在每包的包明前添加註釋;對於`main package`註釋包含一句或幾句話,從整體角度程序做描述。
注释语句以`//`开头。对于程序员来说//之到行末之所有的容都是注释,被编译器忽略。按照例,我在每包的包明前添加注释;对于`main package`注释包含一句或几句话,从整体角度程序做描述。
var明定義了兩個string型的量s和sep。變量會在聲明時直接初始化。如果變量沒有顯式初始化,則被隱式地予其型的*零值*zero value數值類型是0字符串型是空字符串""。這個例子里,明把s和sep式地初始化成空字符串。第2章再來詳細地講解變量和明。
var明定义了两个string型的量s和sep。变量会在声明时直接初始化。如果变量没有显式初始化,则被隐式地予其型的*零值*zero value数值类型是0字符串型是空字符串""。这个例子里,明把s和sep式地初始化成空字符串。第2章再来详细地讲解变量和明。
對數值類Go言提供了常規的數值和邏輯運算符。而string型,`+`算符接字符串(譯註和C++或者js是一的)。所以表式:
对数值类Go言提供了常规的数值和逻辑运算符。而string型,`+`算符接字符串(译注和C++或者js是一的)。所以表式:
```go
sep + os.Args[i]
```
表示接字符串sep和os.Args。程序中使用的句:
表示接字符串sep和os.Args。程序中使用的句:
```go
s += sep + os.Args[i]
```
是一條*賦值語句*, s的值跟sepos.Args[i]連接後賦值迴s價於
是一条*赋值语句*, s的值跟sepos.Args[i]连接后赋值回s价于
```go
s = s + sep + os.Args[i]
```
算符`+=`賦值運算符assignment operator種數值運算符或邏輯運算符,如`+``*`,都有對應的賦值運算符。
算符`+=`赋值运算符assignment operator种数值运算符或逻辑运算符,如`+``*`,都有对应的赋值运算符。
echo程序可以每循一次出一個參數,這個版本是不地把新文本追加到末尾來構造字符串。字符串s開始爲空,卽值爲"",每次循環會添加一些文本;第一次迭代之後,還會再插入一空格,因此循環結束時每個參數中間都有一空格。是一二次加工quadratic process當參數數量龐大時,開銷很大,但是對於echo這種情形不大可能出。本章會介紹echo的若榦改進版,下一章解低效問題
echo程序可以每循一次出一个参数,这个版本是不地把新文本追加到末尾来构造字符串。字符串s开始为空,即值为"",每次循环会添加一些文本;第一次迭代之后,还会再插入一空格,因此循环结束时每个参数中间都有一空格。是一二次加工quadratic process当参数数量庞大时,开销很大,但是对于echo这种情形不大可能出。本章会介绍echo的若干改进版,下一章解低效问题
索引量i在for循的第一部分中定。符`:=`是*短變量聲明*short variable declaration的一部分, 是定義一個或多個變量併根據它們的初始值爲這些變量賦予適當類型的句。下一章有方面更多明。
索引量i在for循的第一部分中定。符`:=`是*短变量声明*short variable declaration的一部分, 是定义一个或多个变量并根据它们的初始值为这些变量赋予适当类型的句。下一章有方面更多明。
自增`i++``i`加1`i += 1`以及`i = i + 1`都是等的。對應的還`i--``i`1。它們是語而不像C的其它言那是表式。所以`j = i++`非法,而且++和--都能放在量名面,因此`--i`也非法。
自增`i++``i`加1`i += 1`以及`i = i + 1`都是等的。对应的还`i--``i`1。它们是语而不像C的其它言那是表式。所以`j = i++`非法,而且++和--都能放在量名面,因此`--i`也非法。
Go語言隻有for循環這一種循環語句。for循有多形式,其中一如下所示:
Go语言只有for循环这一种循环语句。for循有多形式,其中一如下所示:
```go
for initialization; condition; post {
@@ -68,11 +68,11 @@ for initialization; condition; post {
}
```
for循環三個部分不需括號包圍。大括號強製要求, 左大括號必須和*post*句在同一行。
for循环三个部分不需括号包围。大括号强制要求, 左大括号必须和*post*句在同一行。
*initialization*句是可的,在循環開始前行。*initalization*如果存在,必是一條*簡單語句*simple statement,短變量聲明、自增句、賦值語句或函數調用。`condition`是一個布爾表達boolean expression其值在每次循迭代開始時計算。如果`true`則執行循環體語句。`post`句在循環體執行結束後執行,之再次`conditon`求值。`condition``false`,循環結束。
*initialization*句是可的,在循环开始前行。*initalization*如果存在,必是一条*简单语句*simple statement,短变量声明、自增句、赋值语句或函数调用。`condition`是一个布尔表达boolean expression其值在每次循迭代开始时计算。如果`true`则执行循环体语句。`post`句在循环体执行结束后执行,之再次`conditon`求值。`condition``false`,循环结束。
for循環的這三個部分每都可以省略,如果省略`initialization``post`,分也可以省略:
for循环的这三个部分每都可以省略,如果省略`initialization``post`,分也可以省略:
```go
// a traditional "while" loop
@@ -81,7 +81,7 @@ for condition {
}
```
如果`condition`也省略了,像下面這樣
如果`condition`也省略了,像下面这样
```go
// a traditional infinite loop
@@ -90,9 +90,9 @@ for {
}
```
這就變成一個無限循環,盡管如此,可以用其他方式止循, 如一`break``return`句。
这就变成一个无限循环,尽管如此,可以用其他方式止循, 如一`break``return`句。
`for`的另一形式, 在某種數據類型的區間range上遍,如字符串或切片。`echo`的第二版本展示了這種形式:
`for`的另一形式, 在某种数据类型的区间range上遍,如字符串或切片。`echo`的第二版本展示了这种形式:
<u><i>gopl.io/ch1/echo2</i></u>
```go
@@ -113,11 +113,11 @@ func main() {
}
```
每次循迭代,`range`生一值;索引以及在索引的元素值。這個例子不需要索引,但`range`法要求, 要理元素, 必須處理索引。一思路是把索引賦值給一個臨時變量, 如`temp`, 然忽略它的值但Go言不允使用用的局部local variables爲這會導致編譯錯誤
每次循迭代,`range`生一值;索引以及在索引的元素值。这个例子不需要索引,但`range`法要求, 要理元素, 必须处理索引。一思路是把索引赋值给一个临时变量, 如`temp`, 然忽略它的值但Go言不允使用用的局部local variables为这会导致编译错误
Go言中這種情況的解方法是用`空標識符`blank identifier`_`(也就是下劃線)。空標識符可用任何法需要量名但程序邏輯不需要的候, 例如, 在循里,丟棄不需要的循索引, 保留元素值。大多的Go程序員都會像上面這樣使用`range``_``echo`程序,因爲隱式地而非示地索引os.Args容易寫對
Go言中这种情况的解方法是用`空标识符`blank identifier`_`(也就是下划线)。空标识符可用任何法需要量名但程序逻辑不需要的候, 例如, 在循里,丢弃不需要的循索引, 保留元素值。大多的Go程序员都会像上面这样使用`range``_``echo`程序,因为隐式地而非示地索引os.Args容易写对
`echo`這個版本使用一條短變量聲明來聲明併初始化`s``seps`,也可以將這兩個變量分開聲明,明一個變量有好幾種方式,下面些都等
`echo`这个版本使用一条短变量声明来声明并初始化`s``seps`,也可以将这两个变量分开声明,明一个变量有好几种方式,下面些都等
```go
s := ""
@@ -126,11 +126,11 @@ var s = ""
var s string = ""
```
用哪不用哪種,爲什麽呢?第一形式,是一條短變量聲明,最簡潔,但能用在函數內部,而不能用於包變量。第二形式依賴於字符串的默初始化零值機製,被初始化""。第三形式用得很少,除非同時聲明多個變量。第四形式式地標明變量的型,當變量類型與初值型相同時,類型冗,但如果兩者類型不同,變量類型就必了。實踐中一般使用前兩種形式中的某,初始值重要的話就顯式地指定量的型,否使用式初始化。
用哪不用哪种,为什么呢?第一形式,是一条短变量声明,最简洁,但能用在函数内部,而不能用于包变量。第二形式依赖于字符串的默初始化零值机制,被初始化""。第三形式用得很少,除非同时声明多个变量。第四形式式地标明变量的型,当变量类型与初值型相同时,类型冗,但如果两者类型不同,变量类型就必了。实践中一般使用前两种形式中的某,初始值重要的话就显式地指定量的型,否使用式初始化。
如前文所述,每次循迭代字符串s的容都更新。`+=`接原字符串、空格和下個參數,産生新字符串, 把它賦值給`s``s`來的內容已不再使用,將在適當時機對它進行垃圾收。
如前文所述,每次循迭代字符串s的容都更新。`+=`接原字符串、空格和下个参数,产生新字符串, 把它赋值给`s``s`来的内容已不再使用,将在适当时机对它进行垃圾收。
如果接涉及的數據量很大,這種方式代高昂。一種簡單且高效的解方案是使用`strings`包的`Join`
如果接涉及的数据量很大,这种方式代高昂。一种简单且高效的解方案是使用`strings`包的`Join`
<u><i>gopl.io/ch1/echo3</i></u>
```go
@@ -139,16 +139,16 @@ func main() {
}
```
,如果不關心輸出格式,想看看出值,或許隻是爲了調試,可以用`Println`爲我們格式化出。
,如果不关心输出格式,想看看出值,或许只是为了调试,可以用`Println`为我们格式化出。
```go
fmt.Println(os.Args[1:])
```
這條語句的輸出結果跟`strings.Join`得到的果很像,是被放到了一方括里。切片都被打印成這種格式。
这条语句的输出结果跟`strings.Join`得到的果很像,是被放到了一方括里。切片都被打印成这种格式。
**練習 1.1** `echo`程序,使其能打印`os.Args[0]`卽被執行命令本身的名字。
**练习 1.1** `echo`程序,使其能打印`os.Args[0]`即被执行命令本身的名字。
**練習 1.2** `echo`程序,使其打印每個參數的索引和值,每一行。
**练习 1.2** `echo`程序,使其打印每个参数的索引和值,每一行。
**練習 1.3**實驗測量潛在低效的版本和使用了`strings.Join`的版本的運行時間差異1.6節講解了部分`time`11.4展示了如何寫標準測試程序,以得到繫統性的性能評測。)
**练习 1.3**实验测量潜在低效的版本和使用了`strings.Join`的版本的运行时间差异1.6节讲解了部分`time`11.4展示了如何写标准测试程序,以得到系统性的性能评测。)

View File

@@ -1,8 +1,8 @@
## 1.3. 找重的行
## 1.3. 找重的行
文件做拷、打印、索、排序、統計或類似事情的程序都有一差不多的程序結構:一個處理輸入的循,在每元素上執行計算處理,在理的同或最後産生輸出。我們會展示一個名爲`dup`的程序的三版本;靈感來自於Unix的`uniq`命令,其找相的重行。程序使用的結構和包是個參考范例,可以方便地改。
文件做拷、打印、索、排序、统计或类似事情的程序都有一差不多的程序结构:一个处理输入的循,在每元素上执行计算处理,在理的同或最后产生输出。我们会展示一个名为`dup`的程序的三版本;灵感来自于Unix的`uniq`命令,其找相的重行。程序使用的结构和包是个参考范例,可以方便地改。
`dup`的第一版本打印標準輸入中多次出的行,以重複次數開頭。該程序引入`if`句,`map`數據類型以及`bufio`包。
`dup`的第一版本打印标准输入中多次出的行,以重复次数开头。该程序引入`if`句,`map`数据类型以及`bufio`包。
<u><i>gopl.io/ch1/dup1</i></u>
```go
@@ -31,53 +31,53 @@ func main() {
}
```
正如`for`環一樣`if`語句條件兩邊也不加括,但是主部分需要加。`if`句的`else`部分是可的,在`if`條件爲`false`時執行。
正如`for`环一样`if`语句条件两边也不加括,但是主部分需要加。`if`句的`else`部分是可的,在`if`条件为`false`时执行。
**map**存儲了鍵/值key/value的集合集合元素,提供常數時間的存、取或測試操作。可以是任意型,要其值能用`==`算符比,最常的例子是字符串;值可以是任意型。這個例子中的是字符串,值是整數。內置函`make`建空`map`,此外,它有别的作用。4.3節討論`map`
**map**存储了键/值key/value的集合集合元素,提供常数时间的存、取或测试操作。可以是任意型,要其值能用`==`算符比,最常的例子是字符串;值可以是任意型。这个例子中的是字符串,值是整数。内置函`make`建空`map`,此外,它有别的作用。4.3节讨论`map`
譯註:從功能和實現上説`Go``map`類似於`Java`言中的`HashMap`Python言中的`dict``Lua`言中的`table`,通常使用`hash`實現。遺憾的是,對於該詞的翻譯併不統一,數學界術語爲`映射`,而計算機界衆説紛紜莫衷一是。了防止對讀者造成解,保留不。)
译注:从功能和实现上说`Go``map`类似于`Java`言中的`HashMap`Python言中的`dict``Lua`言中的`table`,通常使用`hash`实现。遗憾的是,对于该词的翻译并不统一,数学界术语为`映射`,而计算机界众说纷纭莫衷一是。了防止对读者造成解,保留不。)
每次`dup`取一行入,行被`map`,其對應的值增。`counts[input.Text()]++`句等下面句:
每次`dup`取一行入,行被`map`,其对应的值增。`counts[input.Text()]++`句等下面句:
```go
line := input.Text()
counts[line] = counts[line] + 1
```
`map`中不含某個鍵時不用心,首次到新行,等號右邊的表`counts[line]`的值將被計算爲其類型的零值,對於int`0。
`map`中不含某个键时不用心,首次到新行,等号右边的表`counts[line]`的值将被计算为其类型的零值,对于int`0。
了打印果,我使用了基`range`的循環,併在`counts`這個`map`上迭代。跟之前似,每次迭代得到兩個結果,和其在`map`中對應的值。`map`的迭代順序併不確定,從實踐來看,該順序隨機,每次行都會變化。這種設計是有意之的,因能防止程序依特定遍歷順序,而這是無法保的。
了打印果,我使用了基`range`的循环,并在`counts`这个`map`上迭代。跟之前似,每次迭代得到两个结果,和其在`map`中对应的值。`map`的迭代顺序并不确定,从实践来看,该顺序随机,每次行都会变化。这种设计是有意之的,因能防止程序依特定遍历顺序,而这是无法保的。
繼續來看`bufio`包,它使處理輸入和出方便又高效。`Scanner`型是包最有用的特性之一,它讀取輸入併將其拆成行或單詞;通常是理行形式的入最簡單的方法。
继续来看`bufio`包,它使处理输入和出方便又高效。`Scanner`型是包最有用的特性之一,它读取输入并将其拆成行或单词;通常是理行形式的入最简单的方法。
程序使用短變量聲明創建`bufio.Scanner`型的量`input`。
程序使用短变量声明创建`bufio.Scanner`型的量`input`。
```
input := bufio.NewScanner(os.Stdin)
```
該變量從程序的標準輸入中讀取內容。每次調用`input.Scanner`卽讀入下一行,移除行末的行符;取的容可以調用`input.Text()`得到。`Scan`函數在讀到一行時返迴`true`,在無輸入時返迴`false`。
该变量从程序的标准输入中读取内容。每次用`input.Scanner`即读入下一行,移除行末的行符;取的容可以用`input.Text()`得到。`Scan`函数在读到一行时返回`true`,在无输入时返回`false`。
類似於C或其它言里的`printf`函`fmt.Printf`函數對一些表達式産生格式化出。該函數的首個參數是個格式字符串,指定後續參數被如何格式化。各個參數的格式取決於“轉換字符”conversion character形式百分號後跟一字母。舉個例子,`%d`表示以十進製形式打印一整型操作,而`%s`表示把字符串型操作的值展
类似于C或其它言里的`printf`函`fmt.Printf`函数对一些表达式产生格式化出。该函数的首个参数是个格式字符串,指定后续参数被如何格式化。各个参数的格式取决于“转换字符”conversion character形式百分号后跟一字母。举个例子,`%d`表示以十进制形式打印一整型操作,而`%s`表示把字符串型操作的值展
`Printf`有一大堆這種轉換Go程序員稱之爲*動詞verb*。下面的表格雖然遠不是完整的范,但展示了可用的很多特性:
`Printf`有一大堆这种转换Go程序员称之为*动词verb*。下面的表格虽然远不是完整的范,但展示了可用的很多特性:
```
%d 十進製整數
%x, %o, %b 十六進製,八進製,二進製整數
%f, %g, %e 浮點數 3.141593 3.141592653589793 3.141593e+00
%t 布true或false
%c 字符rune (Unicode碼點)
%d 十进制整数
%x, %o, %b 十六进制,八进制,二进制整数
%f, %g, %e 浮点数 3.141593 3.141592653589793 3.141593e+00
%t 布true或false
%c 字符rune (Unicode码点)
%s 字符串
%q 帶雙引號的字符串"abc"或帶單引號的字符'c'
%v 量的自然形式natural format
%T 量的
%% 字面上的百分號標誌(無操作
%q 带双引号的字符串"abc"或带单引号的字符'c'
%v 量的自然形式natural format
%T 量的
%% 字面上的百分号标志(无操作
```
`dup1`的格式字符串中含有表符`\t`和行符`\n`。字符串字面上可能含有些代表不可字符的**轉義字符escap sequences**。默認情況下,`Printf`不會換行。按照例,以字母`f`尾的格式化函,如`log.Printf`和`fmt.Errorf`,都采用`fmt.Printf`的格式化準則。而以`ln`尾的格式化函數,則遵循`Println`的方式,以跟`%v`差不多的方式格式化參數,併在最添加一個換行符。(譯註:後綴`f`指`fomart``ln`指`line`。)
`dup1`的格式字符串中含有表符`\t`和行符`\n`。字符串字面上可能含有些代表不可字符的**转义字符escap sequences**。默认情况下,`Printf`不会换行。按照例,以字母`f`尾的格式化函,如`log.Printf`和`fmt.Errorf`,都采用`fmt.Printf`的格式化准则。而以`ln`尾的格式化函数,则遵循`Println`的方式,以跟`%v`差不多的方式格式化参数,并在最添加一个换行符。(译注:后缀`f`指`fomart``ln`指`line`。)
很多程序要麽從標準輸入中讀取數據,如上面的例子所示,要麽從一繫列具名文件中讀取數據。`dup`程序的下版本讀取標準輸入或是使用`os.Open`打開各個具名文件,操作它
很多程序要么从标准输入中读取数据,如上面的例子所示,要么从一系列具名文件中读取数据。`dup`程序的下版本读取标准输入或是使用`os.Open`打开各个具名文件,操作它
<u><i>gopl.io/ch1/dup2</i></u>
```go
@@ -123,19 +123,19 @@ func countLines(f *os.File, counts map[string]int) {
}
```
`os.Open`函數返迴兩個值。第一值是被打的文件(`*os.File`),其被`Scanner`取。
`os.Open`函数返回两个值。第一值是被打的文件(`*os.File`),其被`Scanner`取。
`os.Open`返的第二值是置`error`型的值。如果`err`等於內置值`nil`譯註:相當於其它言里的NULL文件被成功打開。讀取文件,直到文件束,然後調用`Close`關閉該文件,併釋放占用的所有源。相反的,如果`err`的值不是`nil`明打文件時出錯了。這種情況下,錯誤值描述了所遇到的問題。我們的錯誤處理非常簡單,隻是使用`Fprintf`表示任意型默格式值的動詞`%v`,向標準錯誤流打印一信息,然`dup`繼續處理下一文件;`continue`句直接跳到`for`循的下迭代開始執行。
`os.Open`返的第二值是置`error`型的值。如果`err`等于内置值`nil`译注:相当于其它言里的NULL文件被成功打开。读取文件,直到文件束,然后调用`Close`关闭该文件,并释放占用的所有源。相反的,如果`err`的值不是`nil`明打文件时出错了。这种情况下,错误值描述了所遇到的问题。我们的错误处理非常简单,只是使用`Fprintf`表示任意型默格式值的动词`%v`,向标准错误流打印一信息,然`dup`继续处理下一文件;`continue`句直接跳到`for`循的下迭代开始执行。
了使示例代保持合理的大小,本書開始的一些示例有意化了錯誤處理,而易的是,應該檢査`os.Open`返迴的錯誤值,然而,使用`input.Scan`取文件程中,不大可能出現錯誤,因此我忽略了錯誤處理。我們會在跳過錯誤檢査的地方做明。5.4中深入介紹錯誤處理。
了使示例代保持合理的大小,本书开始的一些示例有意化了错误处理,而易的是,应该检查`os.Open`返回的错误值,然而,使用`input.Scan`取文件程中,不大可能出现错误,因此我忽略了错误处理。我们会在跳过错误检查的地方做明。5.4中深入介绍错误处理。
意`countLines`函在其明前被調用。函和包别的package-level entities可以任意順序聲明,不影其被調用。(譯註:最好是遵循一定的范)
意`countLines`函在其明前被用。函和包别的package-level entities可以任意顺序声明,不影其被用。(译注:最好是遵循一定的范)
`map`是一由`make`函數創建的數據結構的引用。`map`作爲爲參數傳遞給某函數時,該函數接收這個引用的一份拷copy譯爲副本),被調用函數對`map`底層數據結構的任何改,調用者函都可以通持有的`map`引用看到。在我的例子中,`countLines`函向`counts`插入的值,也被`main`函看到。(譯註:類似於C++里的引用傳遞,實際上指是另一個指針了,但部存的值指向同一塊內存)
`map`是一由`make`函数创建的数据结构的引用。`map`作为为参数传递给某函数时,该函数接收这个引用的一份拷copy译为副本),被用函数对`map`底层数据结构的任何改,用者函都可以通持有的`map`引用看到。在我的例子中,`countLines`函向`counts`插入的值,也被`main`函看到。(译注:类似于C++里的引用传递,实际上指是另一个指针了,但部存的值指向同一块内存)
`dup`的前兩個版本以"流”模式讀取輸入,併根據需要拆分成多行。理上,些程序可以理任意量的輸入數據。還有另一方法,就是一口把全部輸入數據讀到內存中,一次分割多行,然後處理它。下面這個版本,`dup3`,就是這麽操作的。這個例子引入了`ReadFile`函數(來自於`io/ioutil`包),其取指定文件的全部容,`strings.Split`函把字符串分割成子串的切片。(`Split`的作用前文提到的`strings.Join`相反。)
`dup`的前两个版本以"流”模式读取输入,并根据需要拆分成多行。理上,些程序可以理任意量的输入数据。还有另一方法,就是一口把全部输入数据读到内存中,一次分割多行,然后处理它。下面这个版本,`dup3`,就是这么操作的。这个例子引入了`ReadFile`函数(来自于`io/ioutil`包),其取指定文件的全部容,`strings.Split`函把字符串分割成子串的切片。(`Split`的作用前文提到的`strings.Join`相反。)
略微化了`dup3`。首先,由`ReadFile`函需要文件名作爲參數,因此隻讀指定文件,不讀標準輸入。其次,由於行計數代碼隻在一用到,故其移`main`函
略微化了`dup3`。首先,由`ReadFile`函需要文件名作为参数,因此只读指定文件,不读标准输入。其次,由于行计数代码只在一用到,故其移`main`函
<u><i>gopl.io/ch1/dup3</i></u>
```go
@@ -168,8 +168,8 @@ func main() {
}
```
`ReadFile`函數返迴一個字節切片byte slice把它轉換爲`string`,才能用`strings.Split`分割。我們會在3.5.4節詳細講解字符串和字切片。
`ReadFile`函数返回一个字节切片byte slice把它转换为`string`,才能用`strings.Split`分割。我们会在3.5.4节详细讲解字符串和字切片。
實現上,`bufio.Scanner`、`outil.ReadFile`和`ioutil.WriteFile`都使用`*os.File`的`Read`和`Write`方法,但是,大多程序很少需要直接調用那些低lower-level。高higher-level,像`bufio`和`io/ioutil`包中所提供的那些,用起要容易
实现上,`bufio.Scanner`、`outil.ReadFile`和`ioutil.WriteFile`都使用`*os.File`的`Read`和`Write`方法,但是,大多程序很少需要直接用那些低lower-level。高higher-level,像`bufio`和`io/ioutil`包中所提供的那些,用起要容易
**練習 1.4** 改`dup2`,出現重複的行打印文件名
**练习 1.4** 改`dup2`,出现重复的行打印文件名

View File

@@ -1,14 +1,14 @@
## 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)
譯註:要看這個程序的果,需要將標準輸出重定向到一GIF像文件(使用 `./lissajous > output.gif` 命令。下面是GIF圖像動畵效果:
译注:要看这个程序的果,需要将标准输出重定向到一GIF像文件(使用 `./lissajous > output.gif` 命令。下面是GIF图像动画效果:
![](../images/ch1-01.gif)
段代里我用了一些新的結構包括conststruct結構體類型,複合聲明。和我們舉的其它的例子不太一樣,這一個例子包含了浮點數運算。些概念我們隻在這里簡單地説明一下,之的章節會更詳細地講解。
段代里我用了一些新的结构包括conststruct结构体类型,复合声明。和我们举的其它的例子不太一样,这一个例子包含了浮点数运算。些概念我们只在这里简单地说明一下,之的章节会更详细地讲解。
<u><i>gopl.io/ch1/lissajous</i></u>
```go
@@ -66,25 +66,25 @@ func lissajous(out io.Writer) {
```
當我們import了一包路包含有多個單詞的package比如image/colorimage和color兩個單詞),通常我們隻需要用最後那個單詞表示這個包就可以。所以當我們寫color.White時,這個變量指向的是image/color包里的同理gif.GIF是屬於image/gif包里的量。
当我们import了一包路包含有多个单词的package比如image/colorimage和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)置函數,將結果append到anim中的列表末尾,併設置一個默認的80ms的延值。循環結束後所有的延值被編碼進了GIF片中,併將結果寫入到出流。out這個變量是io.Writer型,這個類型支持把輸出結果寫到很多目,很快我就可以看到例子。
lissajous函数内部有两层嵌套的for循。外层循环会循环64次每一次都生成一个单独的动画帧。它生成了一包含两种颜色的201&201大小的片,白色和黑色。所有像素点都会被默认设置为其零值(也就是调色板palette里的第0值),里我们设置的是白色。每次外层循环都会生成一张新图片,并将一些像素设置为黑色。其结果会append到之前果之后。这里我用到了append(考4.2.1)置函数,将结果append到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动画
```
$ go build gopl.io/ch1/lissajous
$ ./lissajous >out.gif
```
**練習 1.5** 改前面的Lissajous程序里的調色闆,由黑色改爲緑色。我可以用`color.RGBA{0xRR, 0xGG, 0xBB, 0xff}`得到`#RRGGBB`這個色值,三十六進製的字符串分别代表紅、緑、藍像素。
**练习 1.5** 改前面的Lissajous程序里的调色板,由黑色改为绿色。我可以用`color.RGBA{0xRR, 0xGG, 0xBB, 0xff}`得到`#RRGGBB`这个色值,三十六进制的字符串分别代表红、绿、蓝像素。
**練習 1.6** 改Lissajous程序改其調色闆來生成更富的色,然後脩改SetColorIndex的第三個參數,看看顯示結果吧。
**练习 1.6** 改Lissajous程序改其调色板来生成更富的色,然后修改SetColorIndex的第三个参数,看看显示结果吧。

View File

@@ -1,8 +1,8 @@
## 1.5. 取URL
## 1.5. 取URL
對於很多現代應用來説,訪問互聯網上的信息和訪問本地文件繫統一樣重要。Go言在net這個強大package的助下提供了一列的package來做這件事情,使用些包可以更簡單地用網絡收發信息,可以建立更底層的網絡連接,編寫服務器程序。在些情景下Go言原生的併發特性(在第八章中會介紹)顯得尤其好用。
对于很多现代应用来说,访问互联网上的信息和访问本地文件系统一样重要。Go言在net这个强大package的助下提供了一列的package来做这件事情,使用些包可以更简单地用网络收发信息,可以建立更底层的网络连接,编写服务器程序。在些情景下Go言原生的并发特性(在第八章中会介绍)显得尤其好用。
了最簡單地展示基HTTP取信息的方式,下面出一示例程序fetch這個程序將獲取對應的url併將其源文本打印出來;這個例子的靈感來源於curl工具譯註unix下的一個用來發http求的工具,具可以man curlcurl提供的功能更爲複雜豐富,這里隻編寫最簡單的樣例。這個樣例之後還會多次被用到。
了最简单地展示基HTTP取信息的方式,下面出一示例程序fetch这个程序将获取对应的url并将其源文本打印出来;这个例子的灵感来源于curl工具译注unix下的一个用来发http求的工具,具可以man curlcurl提供的功能更为复杂丰富,这里只编写最简单的样例。这个样例之后还会多次被用到。
<u><i>gopl.io/ch1/fetch</i></u>
```go
@@ -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
@@ -45,24 +45,24 @@ $ ./fetch http://gopl.io
...
```
HTTP求如果失了的話,會得到下面這樣的結果:
HTTP求如果失了的话,会得到下面这样的结果:
```
$ ./fetch http://bad.gopl.io
fetch: Get http://bad.gopl.io: dial tcp: lookup bad.gopl.io: no such host
```
譯註:在大天朝的網絡環境下很容易重現這種錯誤下面是Windows下行得到的錯誤信息:
译注:在大天朝的网络环境下很容易重现这种错误下面是Windows下行得到的错误信息:
```
$ go run main.go http://gopl.io
fetch: Get http://gopl.io: dial tcp: lookup gopl.io: getaddrinfow: No such host is known.
```
無論哪種失敗原因,我的程序都用了os.Exit函數來終止進程,且返迴一個status錯誤碼,其值1。
无论哪种失败原因,我的程序都用了os.Exit函数来终止进程,且返回一个status错误码,其值1。
**練習 1.7**數調用io.Copy(dst, src)會從src中讀取內容,併將讀到的結果寫入到dst中使用這個函數替代掉例子中的ioutil.ReadAll來拷貝響應結構體到os.Stdout避免申請一個緩衝區例子中的b來存儲。記得處理io.Copy返迴結果中的錯誤
**练习 1.7**数调用io.Copy(dst, src)会从src中读取内容,并将读到的结果写入到dst中使用这个函数替代掉例子中的ioutil.ReadAll来拷贝响应结构体到os.Stdout避免申请一个缓冲区例子中的b来存储。记得处理io.Copy返回结果中的错误
**練習 1.8** 改fetch這個范例,如果入的url參數沒`http://`綴的話,爲這個url加上該前綴。你可能用到strings.HasPrefix這個函數
**练习 1.8** 改fetch这个范例,如果入的url参数没`http://`缀的话,为这个url加上该前缀。你可能用到strings.HasPrefix这个函数
**練習 1.9** 改fetch打印出HTTP協議的狀態碼,可以resp.Status量得到該狀態碼
**练习 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程序只会打印取的容大小和经过的时间,不像之前那打印取的容。
<u><i>gopl.io/ch1/fetchall</i></u>
```go
@@ -48,7 +48,7 @@ func fetch(url string, ch chan<- string) {
}
```
下面使用fetchall來請求幾個地址:
下面使用fetchall来请求几个地址:
```
$ go build gopl.io/ch1/fetchall
@@ -59,10 +59,10 @@ $ ./fetchall https://golang.org http://gopl.io https://godoc.org
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異步執行還沒有完成main函提前退出
当一个goroutine尝试在一channel上做send或者receive操作时,这个goroutine阻塞在调用处,直到另一goroutine往这个channel里入、或者接收值,这样两个goroutine才会继续执行channel操作之后的逻辑。在这个例子中,每一fetch函数在执行时都会往channel里送一值(ch <- expression)主函数负责接收些值(<-ch)。这个程序中我用main函数来接收所有fetch函数传回的字符串可以避免在goroutine异步执行还没有完成main函提前退出
**練習 1.10** 找一個數據量比大的用本小中的程序調研網站的存策略對每個URL執行兩遍請求査看兩次時間是否有大的差别且每次取到的響應內容是否一致改本中的程序將響應結果輸以便於進行對
**练习 1.10** 找一个数据量比大的用本小中的程序调研网站的存策略对每个URL执行两遍请求查看两次时间是否有大的差别且每次取到的响应内容是否一致改本中的程序将响应结果输以便于进行对

View File

@@ -1,6 +1,6 @@
## 1.7. Web服
## 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"。
<u><i>gopl.io/ch1/server1</i></u>
```go
@@ -24,15 +24,15 @@ func handler(w http.ResponseWriter, r *http.Request) {
}
```
們隻用了八九行代碼就實現了一Web服程序,都是多虧了標準庫里的方法已經幫我們完成了大量工作。main函數將所有送到/路下的求和handler函數關聯起來,/開頭的請求其就是所有送到前站上的求,服務監聽8000端口。送到這個服務的“求”是一http.Request型的象,這個對象中包含了求中的一列相字段,其中就包括我需要的URL。當請求到達服務器時這個請求會被傳給handler函數來處理這個函數會將/hello這個路徑從請求的URL中解析出,然把其送到響應中,里我用的是標準輸出流的fmt.Fprintf。Web服務會在第7.7中做更詳細的闡述。
们只用了八九行代码就实现了一Web服程序,都是多亏了标准库里的方法已经帮我们完成了大量工作。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 &
```
在可以通命令行來發送客戶端請求了:
在可以通命令行来发送客户端请求了:
```
$ go build gopl.io/ch1/fetch
@@ -42,11 +42,11 @@ $ ./fetch http://localhost:8000/help
URL.Path = "/help"
```
可以直接在瀏覽器里訪問這個URL得到返迴結果,如1.2
可以直接在浏览器里访问这个URL得到返回结果,如1.2
![](../images/ch1-02.png)
這個服務的基礎上疊加特性是很容易的。一種比較實用的改是爲訪問的url添加某種狀態。比如,下面這個版本出了同樣的內容,但是會對請求的次數進行計算;URL的請求結果會包含各URL被訪問的總次數,直接/count這個URL的訪問要除外。
这个服务的基础上叠加特性是很容易的。一种比较实用的改是为访问的url添加某种状态。比如,下面这个版本出了同样的内容,但是会对请求的次数进行计算;URL的请求结果会包含各URL被访问的总次数,直接/count这个URL的访问要除外。
<u><i>gopl.io/ch1/server2</i></u>
```go
@@ -85,9 +85,9 @@ 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數據都打印出來,這樣可以使檢査和調試這個服務更爲方便:
下面是一个更为丰富的例子handler函数会把请求的http头和请求的form数据都打印出来,这样可以使检查和调试这个服务更为方便:
<u><i>gopl.io/ch1/server3</i></u>
```go
@@ -108,7 +108,7 @@ func handler(w http.ResponseWriter, r *http.Request) {
}
```
用http.Request這個struct里的字段來輸出下面這樣的內容:
用http.Request这个struct里的字段来输出下面这样的内容:
```
GET /?q=query HTTP/1.1
@@ -119,7 +119,7 @@ RemoteAddr = "127.0.0.1:59911"
Form["q"] = ["query"]
```
可以看到里的ParseForm被嵌套在了if句中。Go言允許這樣的一個簡單的語句結果作爲循環的變量聲明出在if句的最前面,這一點對錯誤處理很有用。我們還可以像下面這樣寫(當然看起來就長了一些):
可以看到里的ParseForm被嵌套在了if句中。Go言允许这样的一个简单的语句结果作为循环的变量声明出在if句的最前面,这一点对错误处理很有用。我们还可以像下面这样写(当然看起来就长了一些):
```go
err := r.ParseForm()
@@ -128,13 +128,13 @@ if err != nil {
}
```
用if和ParseForm合可以讓代碼更加簡單,併且可以限err這個變量的作用域,這麽做是很不的。我們會在2.7節中講解作用域。
用if和ParseForm合可以让代码更加简单,并且可以限err这个变量的作用域,这么做是很不的。我们会在2.7节中讲解作用域。
些程序中,我看到了很多不同的型被出到標準輸出流中。比如前面的fetch程序把HTTP的響應數據拷貝到了os.Stdoutlissajous程序里我們輸出的是一文件。fetchall程序完全忽略到了HTTP的響應Body隻是計算了一下響應Body的大小這個程序中把響應Body拷到了ioutil.Discard。在本的web服器程序中是用fmt.Fprintf直接到了http.ResponseWriter中。
些程序中,我看到了很多不同的型被出到标准输出流中。比如前面的fetch程序把HTTP的响应数据拷贝到了os.Stdoutlissajous程序里我们输出的是一文件。fetchall程序完全忽略到了HTTP的响应Body只是计算了一下响应Body的大小这个程序中把响应Body拷到了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服器的代里加入下面这几行。
```Go
handler := func(w http.ResponseWriter, r *http.Request) {
@@ -143,7 +143,7 @@ handler := func(w http.ResponseWriter, r *http.Request) {
http.HandleFunc("/", handler)
```
或者另一種等價形式:
或者另一种等价形式:
```Go
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
@@ -151,11 +151,11 @@ http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
})
```
HandleFunc函的第二個參數是一個函數的字面值,也就是一在使用時定義的匿名函數。這些內容我們會在5.6節中講解。
HandleFunc函的第二个参数是一个函数的字面值,也就是一在使用时定义的匿名函数。这些内容我们会在5.6节中讲解。
做完這些脩改之,在瀏覽器里訪問 http://localhost:8000 。每次你載入這個頁面都可以看到一個像圖1.3那樣的動畵
做完这些修改之,在浏览器里访问 http://localhost:8000 。每次你载入这个页面都可以看到一个像图1.3那样的动画
![](../images/ch1-03.png)
**練習 1.12** 改Lissajour服從URL讀取變量,比如你可以訪問 http://localhost:8000/?cycles=20 這個URL這樣訪問可以程序里的cycles默的5脩改爲20。字符串轉換爲數字可以調用strconv.Atoi函。你可以在godoc里看strconv.Atoi的詳細説明。
**练习 1.12** 改Lissajour服从URL读取变量,比如你可以访问 http://localhost:8000/?cycles=20 这个URL这样访问可以程序里的cycles默的5修改为20。字符串转换为数字可以用strconv.Atoi函。你可以在godoc里看strconv.Atoi的详细说明。

View File

@@ -1,8 +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 @@ default:
}
```
在翻轉硬幣的時例子里的coinflip函數返迴幾種不同的果,每一case都會對應一個返迴結果,里需要Go語言併不需要式地在每一case後寫break言默認執行完case後的邏輯語句會自動退出。然了,如果你想要相鄰的幾個case都行同一邏輯的話,需要自己式地上一fallthrough語句來覆蓋這種默認行爲。不fallthrough句在一般的程序中很少用到。
在翻转硬币的时例子里的coinflip函数返回几种不同的果,每一case都会对应一个返回结果,里需要Go语言并不需要式地在每一case后写break言默认执行完case后的逻辑语句会自动退出。然了,如果你想要相邻的几个case都行同一逻辑的话,需要自己式地上一fallthrough语句来覆盖这种默认行为。不fallthrough句在一般的程序中很少用到。
Go言里的switch可以不操作象(譯註switch不操作對象時默認用true值代替後將每個case的表式和true值行比);可以直接列多種條件,像其它言里面的多if else一,下面是一例子:
Go言里的switch可以不操作象(译注switch不操作对象时默认用true值代替后将每个case的表式和true值行比);可以直接列多种条件,像其它言里面的多if else一,下面是一例子:
```go
func Signum(x int) int {
@@ -32,13 +32,13 @@ func Signum(x int) int {
}
```
這種形式叫做tag switch(tagless switch)和switch true是等的。
这种形式叫做tag switch(tagless switch)和switch true是等的。
像for和if控製語句一switch也可以跟一個簡短的變量聲明,一自增表式、賦值語句,或者一個函數調用(譯註:比其它語言豐富)。
像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取一名字。本章中就有這樣一個例子,二維點類型:
**命名型:** 类型声明使得我可以很方便地给一个特殊型一名字。因struct类型声明通常非常地,所以我们总要给这种struct取一名字。本章中就有这样一个例子,二维点类型:
```go
type Point struct {
@@ -47,15 +47,15 @@ type Point struct {
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
@@ -66,7 +66,7 @@ func ListenAndServe(addr string, handler Handler) error
...
```
**註釋**之前已提到了在源文件的開頭寫的註釋是這個源文件的文。在每一個函數之前寫一個説明函數行爲的註釋也是一個好習慣。這些慣例很重要,因爲這些內容會被像godoc這樣的工具檢測到,且在行命令時顯示這些註釋。具可以考10.7.4。
**注释**之前已提到了在源文件的开头写的注释是这个源文件的文。在每一个函数之前写一个说明函数行为的注释也是一个好习惯。这些惯例很重要,因为这些内容会被像godoc这样的工具检测到,且在行命令时显示这些注释。具可以考10.7.4。
多行註釋可以用 `/* ... */` 包裹,和其它大多數語言一。在文件一開頭的註釋一般都是這種形式,或者一大段的解性的註釋文字也會被這符號包住,避免每一行都需要加//。在註釋中//和/*是沒什麽意義的,所以不要在註釋中再嵌入註釋
多行注释可以用 `/* ... */` 包裹,和其它大多数语言一。在文件一开头的注释一般都是这种形式,或者一大段的解性的注释文字也会被这符号包住,避免每一行都需要加//。在注释中//和/*是没什么意义的,所以不要在注释中再嵌入注释

View File

@@ -1,5 +1,5 @@
# 第1章 入
# 第1章 入
本章介Go言的基礎組件。本章提供了足的信息和示例程序,希望可以幫你盡快入, 出有用的程序。本章和之後章節的示例程序都針對你可能遇到的現實案例。先了解幾個Go程序涉及的主題從簡單的文件理、圖像處理到互聯網客戶端和服務端併發。當然,第一章不會解釋細枝末,但用些程序來學習一門新語言還是很有效的。
本章介Go言的基础组件。本章提供了足的信息和示例程序,希望可以帮你尽快入, 出有用的程序。本章和之后章节的示例程序都针对你可能遇到的现实案例。先了解几个Go程序涉及的主题从简单的文件理、图像处理到互联网客户端和服务端并发。当然,第一章不会解释细枝末,但用些程序来学习一门新语言还是很有效的。
學習一門新語言時,會有一自然的向, 按照自己熟悉的言的套路寫新語言程序。學習Go言的程中,警惕這種想法,量别這麽做。我們會演示怎麽寫好Go言程序,所以使用本的代碼作爲你自己程序的指南。
学习一门新语言时,会有一自然的向, 按照自己熟悉的言的套路写新语言程序。学习Go言的程中,警惕这种想法,量别这么做。我们会演示怎么写好Go言程序,所以使用本的代码作为你自己程序的指南。