mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2026-03-11 04:14:29 +00:00
Compare commits
52 Commits
e77f5af7b4
...
revert-213
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
687fb92030 | ||
|
|
feac38bcf2 | ||
|
|
8df9c1211c | ||
|
|
b8d7c32434 | ||
|
|
d35ce39441 | ||
|
|
b1b9274c8c | ||
|
|
233eef7844 | ||
|
|
0bf3867967 | ||
|
|
c7f58f8991 | ||
|
|
a9f4ae0bf7 | ||
|
|
bb175c69f5 | ||
|
|
d948a26c96 | ||
|
|
213fe7cbaa | ||
|
|
279919079a | ||
|
|
2decff99fd | ||
|
|
c19fb7aff1 | ||
|
|
f4a7cd48b4 | ||
|
|
0984caea57 | ||
|
|
dba2f4c2f1 | ||
|
|
a10ff75618 | ||
|
|
b7cdabf84a | ||
|
|
023499ad54 | ||
|
|
863c1951ea | ||
|
|
cd809c6adc | ||
|
|
4139bfabf3 | ||
|
|
92199cf1ff | ||
|
|
81eda26917 | ||
|
|
145304b099 | ||
|
|
2310a57796 | ||
|
|
9b5c4ea466 | ||
|
|
a1ad719a03 | ||
|
|
9490a5390d | ||
|
|
1597ed7bcd | ||
|
|
b4c6e3e234 | ||
|
|
eaef4fd12d | ||
|
|
380ff85241 | ||
|
|
414ff02cea | ||
|
|
561b2d100a | ||
|
|
bc82ee8489 | ||
|
|
325cf079b7 | ||
|
|
67f8003c62 | ||
|
|
096ef23f97 | ||
|
|
d1c55a2703 | ||
|
|
4ec6d09fd4 | ||
|
|
453cf62e38 | ||
|
|
f6bb64ce09 | ||
|
|
fc4e5fdd3e | ||
|
|
f9ccbec80e | ||
|
|
0c05463c23 | ||
|
|
df88d26247 | ||
|
|
86e9ec1d28 | ||
|
|
6c7b1eb574 |
@@ -1,12 +1,11 @@
|
||||
# Go语言圣经(中文版)
|
||||
|
||||
- *KusonStack一站式可编程配置技术栈(Go): https://github.com/KusionStack/kusion*
|
||||
- *KCL 配置编程语言(Rust): https://github.com/KusionStack/KCLVM*
|
||||
- *凹语言™: https://github.com/wa-lang/wa*
|
||||
- *凹语言(专为 WebAssembly 设计): https://github.com/wa-lang/wa*
|
||||
- *KCL 配置语言(Rust): https://github.com/kcl-lang/kcl*
|
||||
|
||||
----
|
||||
|
||||
Go语言圣经 [《The Go Programming Language》](http://gopl.io) 中文版本,仅供学习交流之用。对于希望学习CGO、Go汇编语言等高级用法的同学,我们推荐[《Go语言高级编程》](https://github.com/chai2010/advanced-go-programming-book)开源图书。如果希望深入学习Go语言语法树结构,可以参考[《Go语法树入门——开启自制编程语言和编译器之旅》](https://github.com/chai2010/go-ast-book)。如果想从头实现一个玩具Go语言可以参考[《从头实现µGo语言》](https://github.com/chai2010/ugo-compiler-book)。
|
||||
Go语言圣经 [《The Go Programming Language》](http://gopl.io) 中文版本,仅供学习交流之用。对于希望学习CGO、Go汇编语言等高级用法的同学,我们推荐[《Go语言高级编程》](https://github.com/chai2010/advanced-go-programming-book)开源图书。如果希望深入学习Go语言语法树结构,可以参考[《Go语法树入门——开启自制编程语言和编译器之旅》](https://github.com/chai2010/go-ast-book)。如果想从头实现一个玩具Go语言可以参考[《从头实现µGo语言》](https://github.com/wa-lang/ugo-compiler-book)(µGo 是 [凹语言](https://wa-lang.org/) 阶段的产物)。
|
||||
|
||||
|
||||

|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Summary
|
||||
|
||||
[Go语言圣经](index.md)
|
||||
[译者序](preface-zh.md)
|
||||
[前言](preface.md)
|
||||
|
||||
* [入门](ch1/ch1.md)
|
||||
@@ -76,7 +77,7 @@
|
||||
* [并发的循环](ch8/ch8-05.md)
|
||||
* [示例: 并发的Web爬虫](ch8/ch8-06.md)
|
||||
* [基于select的多路复用](ch8/ch8-07.md)
|
||||
* [示例: 并发的目录遍历](ch8/ch8-08.md)
|
||||
* [示例: 并发的目录遍历](ch8/ch8-08.md)
|
||||
* [并发的退出](ch8/ch8-09.md)
|
||||
* [示例: 聊天服务](ch8/ch8-10.md)
|
||||
* [基于共享变量的并发](ch9/ch9.md)
|
||||
@@ -84,7 +85,7 @@
|
||||
* [sync.Mutex互斥锁](ch9/ch9-02.md)
|
||||
* [sync.RWMutex读写锁](ch9/ch9-03.md)
|
||||
* [内存同步](ch9/ch9-04.md)
|
||||
* [sync.Once惰性初始化](ch9/ch9-05.md)
|
||||
* [sync.Once惰性初始化](ch9/ch9-05.md)
|
||||
* [竞争条件检测](ch9/ch9-06.md)
|
||||
* [示例: 并发的非阻塞缓存](ch9/ch9-07.md)
|
||||
* [Goroutines和线程](ch9/ch9-08.md)
|
||||
@@ -110,7 +111,7 @@
|
||||
* [示例: 编码S表达式](ch12/ch12-04.md)
|
||||
* [通过reflect.Value修改值](ch12/ch12-05.md)
|
||||
* [示例: 解码S表达式](ch12/ch12-06.md)
|
||||
* [获取结构体字段标签](ch12/ch12-07.md)
|
||||
* [获取结构体字段标签](ch12/ch12-07.md)
|
||||
* [显示一个类型的方法集](ch12/ch12-08.md)
|
||||
* [几点忠告](ch12/ch12-09.md)
|
||||
* [底层编程](ch13/ch13.md)
|
||||
|
||||
@@ -65,7 +65,7 @@ Go 的标准库提供了 100 多个包,以支持常见功能,如输入、输
|
||||
|
||||
Go 语言不需要在语句或者声明的末尾添加分号,除非一行上有多条语句。实际上,编译器会主动把特定符号后的换行符转换为分号,因此换行符添加的位置会影响 Go 代码的正确解析(译注:比如行末是标识符、整数、浮点数、虚数、字符或字符串文字、关键字 `break`、`continue`、`fallthrough`或 `return` 中的一个、运算符和分隔符 `++`、`--`、`)`、`]` 或 `}` 中的一个)。举个例子,函数的左括号 `{` 必须和 `func` 函数声明在同一行上,且位于末尾,不能独占一行,而在表达式 `x+y` 中,可在 `+` 后换行,不能在 `+` 前换行(译注:以+结尾的话不会被插入分号分隔符,但是以 x 结尾的话则会被分号分隔符,从而导致编译错误)。
|
||||
|
||||
Go 语言在代码格式上采取了很强硬的态度。`gofmt`工具把代码格式化为标准格式(译注:这个格式化工具没有任何可以调整代码格式的参数,Go 语言就是这么任性),并且 `go` 工具中的 `fmt` 子命令会对指定包,否则默认为当前目录中所有。go 源文件应用 `gofmt` 命令。本书中的所有代码都被 gofmt 过。你也应该养成格式化自己的代码的习惯。以法令方式规定标准的代码格式可以避免无尽的无意义的琐碎争执(译注:也导致了 Go 语言的 TIOBE 排名较低,因为缺少撕逼的话题)。更重要的是,这样可以做多种自动源码转换,如果放任 Go 语言代码格式,这些转换就不大可能了。
|
||||
Go 语言在代码格式上采取了很强硬的态度。`gofmt`工具把代码格式化为标准格式(译注:这个格式化工具没有任何可以调整代码格式的参数,Go 语言就是这么任性),并且 `go` 工具中的 `fmt` 子命令会对指定包,否则默认为当前目录中所有go 源文件应用 `gofmt` 命令。本书中的所有代码都被 gofmt 过。你也应该养成格式化自己的代码的习惯。以法令方式规定标准的代码格式可以避免无尽的无意义的琐碎争执(译注:也导致了 Go 语言的 TIOBE 排名较低,因为缺少撕逼的话题)。更重要的是,这样可以做多种自动源码转换,如果放任 Go 语言代码格式,这些转换就不大可能了。
|
||||
|
||||
很多文本编辑器都可以配置为保存文件时自动执行 `gofmt`,这样你的源代码总会被恰当地格式化。还有个相关的工具:`goimports`,可以根据代码需要,自动地添加或删除 `import` 声明。这个工具并没有包含在标准的分发包中,可以用下面的命令安装:
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ func main() {
|
||||
|
||||
`var` 声明定义了两个 `string` 类型的变量 `s` 和 `sep`。变量会在声明时直接初始化。如果变量没有显式初始化,则被隐式地赋予其类型的 *零值*(zero value),数值类型是 `0`,字符串类型是空字符串 `""`。这个例子里,声明把 `s` 和 `sep` 隐式地初始化成空字符串。第 2 章再来详细地讲解变量和声明。
|
||||
|
||||
对数值类型,Go 语言提供了常规的数值和逻辑运算符。而对 `string` 类型,`+` 运算符连接字符串(译注:和 C++ 或者 JavaScript 是一样的)。所以表达式:`sep + os.Args[i]` 表示连接字符串 `sep` 和 `os.Args`。程序中使用的语句:`s+=sep+os.Args[i]` 是一条 *赋值语句*,将 `s` 的旧值跟 `sep` 与 `os.Args[i]` 连接后赋值回 `s`,等价于:`s=s+sep+os.Args[i]`。
|
||||
对数值类型,Go 语言提供了常规的数值和逻辑运算符。而对 `string` 类型,`+` 运算符连接字符串(译注:和 C++ 或者 JavaScript 是一样的)。所以表达式:`sep + os.Args[i]` 表示连接字符串 `sep` 和 `os.Args[i]`。程序中使用的语句:`s+=sep+os.Args[i]` 是一条 *赋值语句*,将 `s` 的旧值跟 `sep` 与 `os.Args[i]` 连接后赋值回 `s`,等价于:`s=s+sep+os.Args[i]`。
|
||||
|
||||
运算符 `+=` 是赋值运算符(assignment operator),每种数值运算符或逻辑运算符,如 `+` 或 `*`,都有对应的赋值运算符。
|
||||
|
||||
@@ -113,7 +113,7 @@ var s = ""
|
||||
var s string = ""
|
||||
```
|
||||
|
||||
用哪种不用哪种,为什么呢?第一种形式,是一条短变量声明,最简洁,但只能用在函数内部,而不能用于包变量。第二种形式依赖于字符串的默认初始化零值机制,被初始化为 `""`。第三种形式用得很少,除非同时声明多个变量。第四种形式显式地标明变量的类型,当变量类型与初值类型相同时,类型冗余,但如果两者类型不同,变量类型就必须了。实践中一般使用前两种形式中的某个,初始值重要的话就显式地指定变量的类型,否则使用隐式初始化。
|
||||
用哪种不用哪种,为什么呢?第一种形式,是一条短变量声明,最简洁,但只能用在函数内部,而不能用于包变量。第二种形式依赖于字符串的默认初始化零值机制,被初始化为 `""`。第三种形式用得很少,除非同时声明多个变量。第四种形式显式地标明变量的类型,当变量类型与初值类型相同时,类型冗余,但如果两者类型不同,变量类型就必须了。实践中一般使用前两种形式中的某个,初始值重要的话就显式地指定变量的值,否则指定类型使用隐式初始化。
|
||||
|
||||
如前文所述,每次循环迭代字符串 `s` 的内容都会更新。`+=` 连接原字符串、空格和下个参数,产生新字符串,并把它赋值给 `s`。`s` 原来的内容已经不再使用,将在适当时机对它进行垃圾回收。
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ func main() {
|
||||
fmt.Fprintf(os.Stderr, "fetch: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
b, err := io.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\n", url, err)
|
||||
@@ -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,http.Get函数是创建HTTP请求的函数,如果获取过程没有出错,那么会在resp这个结构体中得到访问的请求结果。resp的Body字段包括一个可读的服务器响应流。io.ReadAll函数从response中读取到全部内容;将其结果保存在变量b中。resp.Body.Close关闭resp的Body流,防止资源泄露,Printf函数会将结果b写出到标准输出流中。
|
||||
|
||||
```
|
||||
$ go build gopl.io/ch1/fetch
|
||||
|
||||
@@ -12,7 +12,7 @@ Unicode字符rune类型是和int32等价的类型,通常用于表示一个Unic
|
||||
|
||||
不管它们的具体大小,int、uint和uintptr是不同类型的兄弟类型。其中int和int32也是不同的类型,即使int的大小也是32bit,在需要将int当作int32类型的地方需要一个显式的类型转换操作,反之亦然。
|
||||
|
||||
其中有符号整数采用2的补码形式表示,也就是最高bit位用来表示符号位,一个n-bit的有符号数的值域是从$-2^{n-1}$到$2^{n-1}-1$。无符号整数的所有bit位都用于表示非负数,值域是0到$2^n-1$。例如,int8类型整数的值域是从-128到127,而uint8类型整数的值域是从0到255。
|
||||
其中有符号整数采用2的补码形式表示,也就是最高bit位用来表示符号位,一个n-bit的有符号数的值域是从-2<sup>n-1</sup>到2<sup>n-1</sup>-1。无符号整数的所有bit位都用于表示非负数,值域是0到2<sup>n</sup>-1。例如,int8类型整数的值域是从-128到127,而uint8类型整数的值域是从0到255。
|
||||
|
||||
下面是Go语言中关于算术运算、逻辑运算和比较运算的二元运算符,它们按照优先级递减的顺序排列:
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ s[0] = 'L' // compile error: cannot assign to s[0]
|
||||
字符串值也可以用字符串面值方式编写,只要将一系列字节序列包含在双引号内即可:
|
||||
|
||||
```
|
||||
"Hello, 世界"
|
||||
"Hello, world"
|
||||
```
|
||||
|
||||

|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# 第3章 基础数据类型
|
||||
|
||||
虽然从底层而言,所有的数据都是由比特组成,但计算机一般操作的是固定大小的数,如整数、浮点数、比特数组、内存地址等。进一步将这些数组织在一起,就可表达更多的对象,例如数据包、像素点、诗歌,甚至其他任何对象。Go语言提供了丰富的数据组织形式,这依赖于Go语言内置的数据类型。这些内置的数据类型,兼顾了硬件的特性和表达复杂数据结构的便捷性。
|
||||
虽然从底层而言,所有的数据都是由比特组成,但计算机一般操作的是固定大小的字,如整数、浮点数、比特数组、内存地址等。进一步将这些数组织在一起,就可表达更多的对象,例如数据包、像素点、诗歌,甚至其他任何对象。Go语言提供了丰富的数据组织形式,这依赖于Go语言内置的数据类型。这些内置的数据类型,兼顾了硬件的特性和表达复杂数据结构的便捷性。
|
||||
|
||||
Go语言将数据类型分为四类:基础类型、复合类型、引用类型和接口类型。本章介绍基础类型,包括:数字、字符串和布尔型。复合数据类型——数组(§4.1)和结构体(§4.2)——是通过组合简单类型,来表达更加复杂的数据结构。引用类型包括指针(§2.3.2)、切片(§4.2))、字典(§4.3)、函数(§5)、通道(§8),虽然数据种类很多,但它们都是对程序中一个变量或状态的间接引用。这意味着对任一引用类型数据的修改都会影响所有该引用的拷贝。我们将在第7章介绍接口类型。
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
下文的示例代码使用了非标准包 golang.org/x/net/html ,解析HTML。golang.org/x/... 目录下存储了一些由Go团队设计、维护,对网络编程、国际化文件处理、移动平台、图像处理、加密解密、开发者工具提供支持的扩展包。未将这些扩展包加入到标准库原因有二,一是部分包仍在开发中,二是对大多数Go语言的开发者而言,扩展包提供的功能很少被使用。
|
||||
|
||||
例子中调用golang.org/x/net/html的部分api如下所示。html.Parse函数读入一组bytes解析后,返回html.Node类型的HTML页面树状结构根节点。HTML拥有很多类型的结点如text(文本)、commnets(注释)类型,在下面的例子中,我们 只关注< name key='value' >形式的结点。
|
||||
例子中调用golang.org/x/net/html的部分api如下所示。html.Parse函数读入一组bytes解析后,返回html.Node类型的HTML页面树状结构根节点。HTML拥有很多类型的结点如text(文本)、comments(注释)类型,在下面的例子中,我们 只关注< name key='value' >形式的结点。
|
||||
|
||||
<u><i>golang.org/x/net/html</i></u>
|
||||
```Go
|
||||
|
||||
@@ -55,9 +55,9 @@ interface{}表示函数的最后一个参数可以接收任意类型,我们会
|
||||
|
||||
**练习5.15:** 编写类似sum的可变参数函数max和min。考虑不传参时,max和min该如何处理,再编写至少接收1个参数的版本。
|
||||
|
||||
**练习5.16:**编写多参数版本的strings.Join。
|
||||
**练习5.16:** 编写多参数版本的strings.Join。
|
||||
|
||||
**练习5.17:**编写多参数版本的ElementsByTagName,函数接收一个HTML结点树以及任意数量的标签名,返回与这些标签名匹配的所有元素。下面给出了2个例子:
|
||||
**练习5.17:** 编写多参数版本的ElementsByTagName,函数接收一个HTML结点树以及任意数量的标签名,返回与这些标签名匹配的所有元素。下面给出了2个例子:
|
||||
|
||||
```Go
|
||||
func ElementsByTagName(doc *html.Node, name...string) []*html.Node
|
||||
|
||||
@@ -221,4 +221,4 @@ func fetch(url string) (filename string, n int64, err error) {
|
||||
|
||||
对resp.Body.Close延迟调用我们已经见过了,在此不做解释。上例中,通过os.Create打开文件进行写入,在关闭文件时,我们没有对f.close采用defer机制,因为这会产生一些微妙的错误。许多文件系统,尤其是NFS,写入文件时发生的错误会被延迟到文件关闭时反馈。如果没有检查文件关闭时的反馈信息,可能会导致数据丢失,而我们还误以为写入操作成功。如果io.Copy和f.close都失败了,我们倾向于将io.Copy的错误信息反馈给调用者,因为它先于f.close发生,更有可能接近问题的本质。
|
||||
|
||||
**练习5.18:**不修改fetch的行为,重写fetch函数,要求使用defer机制关闭文件。
|
||||
**练习5.18:** 不修改fetch的行为,重写fetch函数,要求使用defer机制关闭文件。
|
||||
|
||||
@@ -17,7 +17,7 @@ default:
|
||||
}
|
||||
```
|
||||
|
||||
断言函数必须满足的前置条件是明智的做法,但这很容易被滥用。除非你能提供更多的错误信息,或者能更快速的发现错误,否则不需要使用断言,编译器在运行时会帮你检查代码。
|
||||
断言函数必须满足的前置条件是明智的做法,但这很容易被滥用。除非你能提供更多的错误信息,或者能更快速的发现错误,否则不需要断言那些运行时会检查的条件。
|
||||
|
||||
```Go
|
||||
func Reset(x *Buffer) {
|
||||
|
||||
@@ -17,7 +17,7 @@ func Parse(input string) (s *Syntax, err error) {
|
||||
}
|
||||
```
|
||||
|
||||
deferred函数帮助Parse从panic中恢复。在deferred函数内部,panic value被附加到错误信息中;并用err变量接收错误信息,返回给调用者。我们也可以通过调用runtime.Stack往错误信息中添加完整的堆栈调用信息。
|
||||
recover函数帮助Parse从panic中恢复。在deferred函数内部,panic value被附加到错误信息中;并用err变量接收错误信息,返回给调用者。我们也可以通过调用runtime.Stack往错误信息中添加完整的堆栈调用信息。
|
||||
|
||||
不加区分的恢复所有的panic异常,不是可取的做法;因为在panic之后,无法保证包级变量的状态仍然和我们预期一致。比如,对数据结构的一次重要更新没有被完整完成、文件或者网络连接没有被关闭、获得的锁没有被释放。此外,如果写日志时产生的panic被不加区分的恢复,可能会导致漏洞被忽略。
|
||||
|
||||
|
||||
@@ -85,4 +85,4 @@ fmt.Println(geometry.PathDistance(perim)) // "12", standalone function
|
||||
fmt.Println(perim.Distance()) // "12", method of geometry.Path
|
||||
```
|
||||
|
||||
**译注:** 如果我们要用方法去计算perim的distance,还需要去写全geometry的包名,和其函数名,但是因为Path这个类型定义了一个可以直接用的Distance方法,所以我们可以直接写perim.Distance()。相当于可以少打很多字,作者应该是这个意思。因为在Go里包外调用函数需要带上包名,还是挺麻烦的。
|
||||
**译注:** 如果我们要用函数去计算perim的distance,还需要去写全geometry的包名,和其函数名,但是因为Path这个类型定义了一个可以直接用的Distance方法,所以我们可以直接写perim.Distance()。相当于可以少打很多字,作者应该是这个意思。因为在Go里包外调用函数需要带上包名,还是挺麻烦的。
|
||||
|
||||
@@ -88,7 +88,7 @@ type ColoredPoint struct {
|
||||
|
||||
方法只能在命名类型(像Point)或者指向类型的指针上定义,但是多亏了内嵌,有些时候我们给匿名struct类型来定义方法也有了手段。
|
||||
|
||||
下面是一个小trick。这个例子展示了简单的cache,其使用两个包级别的变量来实现,一个mutex互斥量(§9.2)和它所操作的cache:
|
||||
下面是一个小trick。这个例子展示了简单的cache,其使用两个包级别的变量来实现,一个mutex互斥锁(§9.2)和它所操作的cache:
|
||||
|
||||
```go
|
||||
var (
|
||||
|
||||
@@ -109,6 +109,6 @@ func (*IntSet) Copy() *IntSet // return a copy of the set
|
||||
|
||||
**练习 6.3:** (*IntSet).UnionWith会用`|`操作符计算两个集合的并集,我们再为IntSet实现另外的几个函数IntersectWith(交集:元素在A集合B集合均出现),DifferenceWith(差集:元素出现在A集合,未出现在B集合),SymmetricDifference(并差集:元素出现在A但没有出现在B,或者出现在B没有出现在A)。
|
||||
|
||||
***练习6.4: ** 实现一个Elems方法,返回集合中的所有元素,用于做一些range之类的遍历操作。
|
||||
**练习 6.4:** 实现一个Elems方法,返回集合中的所有元素,用于做一些range之类的遍历操作。
|
||||
|
||||
**练习 6.5:** 我们这章定义的IntSet里的每个字都是用的uint64类型,但是64位的数值可能在32位的平台上不高效。修改程序,使其使用uint类型,这种类型对于32位平台来说更合适。当然了,这里我们可以不用简单粗暴地除64,可以定义一个常量来决定是用32还是64,这里你可能会用到平台的自动判断的一个智能表达式:32 << (^uint(0) >> 63)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
从90年代早期开始,面向对象编程(OOP)就成为了称霸工程界和教育界的编程范式,所以之后几乎所有大规模被应用的语言都包含了对OOP的支持,go语言也不例外。
|
||||
|
||||
尽管没有被大众所接受的明确的OOP的定义,从我们的理解来讲,一个对象其实也就是一个简单的值或者一个变量,在这个对象中会包含一些方法,而一个方法则是一个一个和特殊类型关联的函数。一个面向对象的程序会用方法来表达其属性和对应的操作,这样使用这个对象的用户就不需要直接去操作对象,而是借助方法来做这些事情。
|
||||
尽管没有被大众所接受的明确的OOP的定义,从我们的理解来讲,一个对象其实也就是一个简单的值或者一个变量,在这个对象中会包含一些方法,而一个方法则是一个和特殊类型关联的函数。一个面向对象的程序会用方法来表达其属性和对应的操作,这样使用这个对象的用户就不需要直接去操作对象,而是借助方法来做这些事情。
|
||||
|
||||
在早些的章节中,我们已经使用了标准库提供的一些方法,比如time.Duration这个类型的Seconds方法:
|
||||
|
||||
|
||||
@@ -20,10 +20,9 @@ func Sprintf(format string, args ...interface{}) string {
|
||||
}
|
||||
```
|
||||
|
||||
Fprintf的前缀F表示文件(File)也表明格式化输出结果应该被写入第一个参数提供的文件中。在Printf函数中的第一个参数os.Stdout是`*os.File`类型;在Sprintf函数中的第一个参数&buf是一个指向可以写入字节的内存缓冲区,然而它
|
||||
并不是一个文件类型尽管它在某种意义上和文件类型相似。
|
||||
Fprintf的前缀F表示文件(File),也表明格式化输出结果应该被写入第一个参数提供的文件中。在Printf函数中的第一个参数os.Stdout是`*os.File`类型;在Sprintf函数中的第一个参数&buf是一个指向可以写入字节的内存缓冲区。然而它并不是一个文件类型,尽管它在某种意义上和文件类型相似。
|
||||
|
||||
即使Fprintf函数中的第一个参数也不是一个文件类型。它是io.Writer类型,这是一个接口类型定义如下:
|
||||
即使是Fprintf函数中的第一个参数,它也不是一个文件类型。它是io.Writer类型,这是一个接口类型定义如下:
|
||||
|
||||
``` go
|
||||
package io
|
||||
|
||||
@@ -199,7 +199,7 @@ func (memo *Memo) Get(key string) (value interface{}, err error) {
|
||||
|
||||
这些修改使性能再次得到了提升,但有一些URL被获取了两次。这种情况在两个以上的goroutine同一时刻调用Get来请求同样的URL时会发生。多个goroutine一起查询cache,发现没有值,然后一起调用f这个慢不拉叽的函数。在得到结果后,也都会去更新map。其中一个获得的结果会覆盖掉另一个的结果。
|
||||
|
||||
理想情况下是应该避免掉多余的工作的。而这种“避免”工作一般被称为duplicate suppression(重复抑制/避免)。下面版本的Memo每一个map元素都是指向一个条目的指针。每一个条目包含对函数f调用结果的内容缓存。与之前不同的是这次entry还包含了一个叫ready的channel。在条目的结果被设置之后,这个channel就会被关闭,以向其它goroutine广播(§8.9)去读取该条目内的结果是安全的了。
|
||||
理想情况下是应该避免掉多余的工作的。而这种“避免”工作一般被称为duplicate suppression(重复抑制/避免)。下面版本的Memo每一个map元素都是指向一个条目的指针。每一个entry包含对函数f调用结果的内容缓存。与之前不同的是这次entry还包含了一个叫ready的channel。在entry的res字段被设置之后,这个channel就会被关闭,以向其它goroutine广播(§8.9)去读取该entry内的结果是安全的了。
|
||||
|
||||
<u><i>gopl.io/ch9/memo4</i></u>
|
||||
```go
|
||||
@@ -334,7 +334,7 @@ func (e *entry) deliver(response chan<- result) {
|
||||
|
||||
和基于互斥量的版本类似,第一个对某个key的请求需要负责去调用函数f并传入这个key,将结果存在条目里,并关闭ready channel来广播条目的ready消息。使用`(*entry).call`来完成上述工作。
|
||||
|
||||
紧接着对同一个key的请求会发现map中已经有了存在的条目,然后会等待结果变为ready,并将结果从response发送给客户端的goroutien。上述工作是用`(*entry).deliver`来完成的。对call和deliver方法的调用必须让它们在自己的goroutine中进行以确保monitor goroutines不会因此而被阻塞住而没法处理新的请求。
|
||||
紧接着对同一个key的请求会发现map中已经有了存在的条目,然后会等待结果变为ready,并将结果从response发送给客户端的goroutine。上述工作是用`(*entry).deliver`来完成的。对call和deliver方法的调用必须让它们在自己的goroutine中进行以确保monitor goroutines不会因此而被阻塞住而没法处理新的请求。
|
||||
|
||||
这个例子说明我们无论用上锁,还是通信来建立并发程序都是可行的。
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
相反,一个goroutine会以一个很小的栈开始其生命周期,一般只需要2KB。一个goroutine的栈,和操作系统线程一样,会保存其活跃或挂起的函数调用的本地变量,但是和OS线程不太一样的是,一个goroutine的栈大小并不是固定的;栈的大小会根据需要动态地伸缩。而goroutine的栈的最大值有1GB,比传统的固定大小的线程栈要大得多,尽管一般情况下,大多goroutine都不需要这么大的栈。
|
||||
|
||||
** 练习 9.4:** 创建一个流水线程序,支持用channel连接任意数量的goroutine,在跑爆内存之前,可以创建多少流水线阶段?一个变量通过整个流水线需要用多久?(这个练习题翻译不是很确定)
|
||||
**练习 9.4:** 创建一个流水线程序,支持用channel连接任意数量的goroutine,在跑爆内存之前,可以创建多少流水线阶段?一个变量通过整个流水线需要用多久?(这个练习题翻译不是很确定)
|
||||
|
||||
### 9.8.2. Goroutine调度
|
||||
|
||||
@@ -18,7 +18,7 @@ Go的运行时包含了其自己的调度器,这个调度器使用了一些技
|
||||
|
||||
和操作系统的线程调度不同的是,Go调度器并不是用一个硬件定时器,而是被Go语言“建筑”本身进行调度的。例如当一个goroutine调用了time.Sleep,或者被channel调用或者mutex操作阻塞时,调度器会使其进入休眠并开始执行另一个goroutine,直到时机到了再去唤醒第一个goroutine。因为这种调度方式不需要进入内核的上下文,所以重新调度一个goroutine比调度一个线程代价要低得多。
|
||||
|
||||
** 练习 9.5: ** 写一个有两个goroutine的程序,两个goroutine会向两个无buffer channel反复地发送ping-pong消息。这样的程序每秒可以支持多少次通信?
|
||||
**练习 9.5:** 写一个有两个goroutine的程序,两个goroutine会向两个无buffer channel反复地发送ping-pong消息。这样的程序每秒可以支持多少次通信?
|
||||
|
||||
### 9.8.3. GOMAXPROCS
|
||||
|
||||
@@ -42,7 +42,7 @@ $ GOMAXPROCS=2 go run hacker-cliché.go
|
||||
|
||||
在第一次执行时,最多同时只能有一个goroutine被执行。初始情况下只有main goroutine被执行,所以会打印很多1。过了一段时间后,GO调度器会将其置为休眠,并唤醒另一个goroutine,这时候就开始打印很多0了,在打印的时候,goroutine是被调度到操作系统线程上的。在第二次执行时,我们使用了两个操作系统线程,所以两个goroutine可以一起被执行,以同样的频率交替打印0和1。我们必须强调的是goroutine的调度是受很多因子影响的,而runtime也是在不断地发展演进的,所以这里的你实际得到的结果可能会因为版本的不同而与我们运行的结果有所不同。
|
||||
|
||||
** 练习9.6:** 测试一下计算密集型的并发程序(练习8.5那样的)会被GOMAXPROCS怎样影响到。在你的电脑上最佳的值是多少?你的电脑CPU有多少个核心?
|
||||
**练习9.6:** 测试一下计算密集型的并发程序(练习8.5那样的)会被GOMAXPROCS怎样影响到。在你的电脑上最佳的值是多少?你的电脑CPU有多少个核心?
|
||||
|
||||
### 9.8.4. Goroutine没有ID号
|
||||
|
||||
|
||||
2
index.md
2
index.md
@@ -1,6 +1,6 @@
|
||||
# Go语言圣经(中文版)
|
||||
|
||||
Go语言圣经 [《The Go Programming Language》](http://gopl.io) 中文版本,仅供学习交流之用。对于希望学习CGO、Go汇编语言等高级用法的同学,我们推荐[《Go语言高级编程》](https://github.com/chai2010/advanced-go-programming-book)开源图书。如果希望深入学习Go语言语法树结构,可以参考[《Go语法树入门——开启自制编程语言和编译器之旅》](https://github.com/chai2010/go-ast-book)。如果想从头实现一个玩具Go语言可以参考[《从头实现µGo语言》](https://github.com/chai2010/ugo-compiler-book)。
|
||||
Go语言圣经 [《The Go Programming Language》](http://gopl.io) 中文版本,仅供学习交流之用。对于希望学习CGO、Go汇编语言等高级用法的同学,我们推荐[《Go语言高级编程》](https://github.com/chai2010/advanced-go-programming-book)开源图书。如果希望深入学习Go语言语法树结构,可以参考[《Go语法树入门——开启自制编程语言和编译器之旅》](https://github.com/chai2010/go-ast-book)。如果想从头实现一个玩具Go语言可以参考[《从头实现µGo语言》](https://github.com/wa-lang/ugo-compiler-book)(µGo 是 [凹语言](https://wa-lang.org/) 阶段的产物)。
|
||||
|
||||

|
||||
|
||||
|
||||
19
preface-zh.md
Normal file
19
preface-zh.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# 译者序
|
||||
|
||||
在上个世纪70年代,贝尔实验室的 [Ken Thompson](http://genius.cat-v.org/ken-thompson/) 和 [Dennis M. Ritchie](http://genius.cat-v.org/dennis-ritchie/) 合作发明了 [UNIX](http://doc.cat-v.org/unix/) 操作系统,同时 [Dennis M. Ritchie](http://genius.cat-v.org/dennis-ritchie/) 为了解决 [UNIX](http://doc.cat-v.org/unix/) 系统的移植性问题而发明了 C 语言,贝尔实验室的 [UNIX](http://doc.cat-v.org/unix/) 和 C 语言两大发明奠定了整个现代IT行业最重要的软件基础(目前的三大桌面操作系统的中[Linux](http://www.linux.org/)和[Mac OS X](http://www.apple.com/cn/osx/)都是源于 [UNIX](http://doc.cat-v.org/unix/) 系统,两大移动平台的操作系统 iOS 和 Android 也都是源于 [UNIX](http://doc.cat-v.org/unix/) 系统。C 系家族的编程语言占据统治地位达几十年之久)。在 [UNIX](http://doc.cat-v.org/unix/) 和 C 语言发明40年之后,目前已经在 Google 工作的 [Ken Thompson](http://genius.cat-v.org/ken-thompson/) 和 [Rob Pike](http://genius.cat-v.org/rob-pike/)(他们在贝尔实验室时就是同事)、还有[Robert Griesemer](http://research.google.com/pubs/author96.html)(设计了 V8 引擎和 HotSpot 虚拟机)一起合作,为了解决在21世纪多核和网络化环境下越来越复杂的编程问题而发明了 Go 语言。从 Go 语言库早期代码库日志可以看出它的演化历程( Git 用 `git log --before={2008-03-03} --reverse` 命令查看):
|
||||
|
||||

|
||||
|
||||
从早期提交日志中也可以看出,Go 语言是从 [Ken Thompson](http://genius.cat-v.org/ken-thompson/) 发明的 B 语言、[Dennis M. Ritchie](http://genius.cat-v.org/dennis-ritchie/) 发明的 C 语言逐步演化过来的,是 C 语言家族的成员,因此很多人将 Go 语言称为 21 世纪的 C 语言。纵观这几年来的发展趋势,Go 语言已经成为云计算、云存储时代最重要的基础编程语言。
|
||||
|
||||
在 C 语言发明之后约5年的时间之后(1978年),[Brian W. Kernighan](http://www.cs.princeton.edu/~bwk/) 和 [Dennis M. Ritchie](http://genius.cat-v.org/dennis-ritchie/) 合作编写出版了C语言方面的经典教材《[The C Programming Language](http://s3-us-west-2.amazonaws.com/belllabs-microsite-dritchie/cbook/index.html)》,该书被誉为 C 语言程序员的圣经,作者也被大家亲切地称为 [K&R](https://en.wikipedia.org/wiki/K%26R)。同样在 Go 语言正式发布(2009 年)约 5 年之后(2014 年开始写作,2015 年出版),由 Go 语言核心团队成员 [Alan A. A. Donovan](https://github.com/adonovan) 和 [K&R](https://en.wikipedia.org/wiki/K%26R) 中的 [Brian W. Kernighan](http://www.cs.princeton.edu/~bwk/) 合作编写了Go语言方面的经典教材《[The Go Programming Language](http://gopl.io)》。Go 语言被誉为 21 世纪的 C 语言,如果说 [K&R](https://en.wikipedia.org/wiki/K%26R) 所著的是圣经的旧约,那么 D&K 所著的必将成为圣经的新约。该书介绍了 Go 语言几乎全部特性,并且随着语言的深入层层递进,对每个细节都解读得非常细致,每一节内容都精彩不容错过,是广大 Gopher 的必读书目。大部分 Go 语言核心团队的成员都参与了该书校对工作,因此该书的质量是可以完全放心的。
|
||||
|
||||
同时,单凭阅读和学习其语法结构并不能真正地掌握一门编程语言,必须进行足够多的编程实践——亲自编写一些程序并研究学习别人写的程序。要从利用 Go 语言良好的特性使得程序模块化,充分利用 Go 的标准函数库以 Go 语言自己的风格来编写程序。书中包含了上百个精心挑选的习题,希望大家能先用自己的方式尝试完成习题,然后再参考官方给出的解决方案。
|
||||
|
||||
该书英文版约从 2015 年 10 月开始公开发售,其中日文版本最早参与翻译和审校(参考致谢部分)。在 2015 年 10 月,我们并不知道中文版是否会及时引进、将由哪家出版社引进、引进将由何人来翻译、何时能出版,这些信息都成了一个秘密。中国的 Go 语言社区是全球最大的Go语言社区,我们从一开始就始终紧跟着 Go 语言的发展脚步。我们应该也完全有能力以中国 Go 语言社区的力量同步完成 Go 语言圣经中文版的翻译工作。与此同时,国内有很多 Go 语言爱好者也在积极关注该书(本人也在第一时间购买了纸质版本,[亚马逊价格314人民币](http://www.amazon.cn/The-Go-Programming-Language-Donovan-Alan-A-A/dp/0134190440/)。补充:国内也即将出版英文版,[价格79元](http://product.china-pub.com/4912464))。为了 Go 语言的学习和交流,大家决定合作免费翻译该书。
|
||||
|
||||
翻译工作从 2015 年 11 月 20 日前后开始,到 2016 年 1 月底初步完成,前后历时约 2 个月时间(在其它语言版本中,全球第一个完成翻译的,基本做到和原版同步)。其中,[chai2010](https://github.com/chai2010) 翻译了前言、第2 ~ 4章、第10 ~ 13章,[Xargin](https://github.com/cch123) 翻译了第1章、第6章、第8 ~ 9章,[CrazySssst](https://github.com/CrazySssst) 翻译了第5章,[foreversmart](https://github.com/foreversmart) 翻译了第7章,大家共同参与了基本的校验工作,还有其他一些朋友提供了积极的反馈建议。如果大家还有任何问题或建议,可以直接到中文版项目页面提交 [Issue](https://github.com/golang-china/gopl-zh/issues),如果发现英文版原文在[勘误](http://www.gopl.io/errata.html)中未提到的任何错误,可以直接去[英文版项目](https://github.com/adonovan/gopl.io/)提交。
|
||||
|
||||
最后,希望这本书能够帮助大家用Go语言快乐地编程。
|
||||
|
||||
2016年 1月 于 武汉
|
||||
@@ -21,7 +21,7 @@
|
||||
width: 200px;
|
||||
height: calc(100vh - var(--menu-bar-height) - 0.67em * 4);
|
||||
overflow: auto;
|
||||
z-index: 1000;
|
||||
/* z-index: 1000; */
|
||||
}
|
||||
.pagetoc a {
|
||||
border-left: 1px solid var(--sidebar-bg);
|
||||
|
||||
@@ -186,10 +186,9 @@
|
||||
<main>
|
||||
<!-- 头部 -->
|
||||
<ul dir="auto">
|
||||
<li><em>KusonStack一站式可编程配置技术栈(Go): <a href="https://github.com/KusionStack/kusion">https://github.com/KusionStack/kusion</a></em></li>
|
||||
<li><em>KCL 配置编程语言(Rust): <a href="https://github.com/KusionStack/KCLVM">https://github.com/KusionStack/KCLVM</a></em></li>
|
||||
<li><em>凹语言™: <a href="https://github.com/wa-lang/wa">https://github.com/wa-lang/wa</a></em></li>
|
||||
</ul>
|
||||
<li><em>凹语言(专为 WebAssembly 设计): <a href="https://github.com/wa-lang/wa">https://github.com/wa-lang/wa</a></em></li>
|
||||
<li><em>KCL 配置语言(Rust): <a href="https://github.com/kcl-lang/kcl">https://github.com/kcl-lang/kcl</a></em></li>
|
||||
</ul>
|
||||
<hr>
|
||||
|
||||
{{{ content }}}
|
||||
|
||||
Reference in New Issue
Block a user