mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2024-11-24 23:29:01 +00:00
Merge branch 'master' of github.com:gopl-zh/gopl-zh.github.com
This commit is contained in:
commit
25d798e636
@ -16,7 +16,6 @@ Go语言圣经 [《The Go Programming Language》](http://gopl.io) 中文版本
|
|||||||
- http://shifei.me/gopl-zh/
|
- http://shifei.me/gopl-zh/
|
||||||
- http://2goo.info/media/html/gopl-zh-gh-pages/
|
- http://2goo.info/media/html/gopl-zh-gh-pages/
|
||||||
- http://docs.plhwin.com/gopl-zh/
|
- http://docs.plhwin.com/gopl-zh/
|
||||||
- http://gopl-zh.simple-is-best.tk/
|
|
||||||
- https://docs.hacknode.org/gopl-zh/
|
- https://docs.hacknode.org/gopl-zh/
|
||||||
|
|
||||||
**注意,在线预览不是最新版,最新以仓库里的内容为准**
|
**注意,在线预览不是最新版,最新以仓库里的内容为准**
|
||||||
|
@ -57,7 +57,7 @@ func (path Path) Distance() float64 {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Path是一个命名的slice类型,而不是Point那样的struct类型,然而我们依然可以为它定义方法。在能够给任意类型定义方法这一点上,Go和很多其它的面向对象的语言不太一样。因此在Go语言里,我们为一些简单的数值、字符串、slice、map来定义一些附加行为很方便。方法可以被声明到任意类型,只要不是一个指针或者一个interface。
|
Path是一个命名的slice类型,而不是Point那样的struct类型,然而我们依然可以为它定义方法。在能够给任意类型定义方法这一点上,Go和很多其它的面向对象的语言不太一样。因此在Go语言里,我们为一些简单的数值、字符串、slice、map来定义一些附加行为很方便。我们可以给同一个包内的任意命名类型定义方法,只要这个命名类型的底层类型(译注:这个例子里,底层类型是指[]Point这个slice,Path就是命名类型)不是指针或者interface。
|
||||||
|
|
||||||
两个Distance方法有不同的类型。他们两个方法之间没有任何关系,尽管Path的Distance方法会在内部调用Point.Distance方法来计算每个连接邻接点的线段的长度。
|
两个Distance方法有不同的类型。他们两个方法之间没有任何关系,尽管Path的Distance方法会在内部调用Point.Distance方法来计算每个连接邻接点的线段的长度。
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ fmt.Println(x) // "{[4398046511618 0 65536]}"
|
|||||||
|
|
||||||
在第一个Println中,我们打印一个`*IntSet`的指针,这个类型的指针确实有自定义的String方法。第二Println,我们直接调用了x变量的String()方法;这种情况下编译器会隐式地在x前插入&操作符,这样相当远我们还是调用的IntSet指针的String方法。在第三个Println中,因为IntSet类型没有String方法,所以Println方法会直接以原始的方式理解并打印。所以在这种情况下&符号是不能忘的。在我们这种场景下,你把String方法绑定到IntSet对象上,而不是IntSet指针上可能会更合适一些,不过这也需要具体问题具体分析。
|
在第一个Println中,我们打印一个`*IntSet`的指针,这个类型的指针确实有自定义的String方法。第二Println,我们直接调用了x变量的String()方法;这种情况下编译器会隐式地在x前插入&操作符,这样相当远我们还是调用的IntSet指针的String方法。在第三个Println中,因为IntSet类型没有String方法,所以Println方法会直接以原始的方式理解并打印。所以在这种情况下&符号是不能忘的。在我们这种场景下,你把String方法绑定到IntSet对象上,而不是IntSet指针上可能会更合适一些,不过这也需要具体问题具体分析。
|
||||||
|
|
||||||
练习6.1: 为bit数组实现下面这些方法
|
**练习6.1:** 为bit数组实现下面这些方法
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func (*IntSet) Len() int // return the number of elements
|
func (*IntSet) Len() int // return the number of elements
|
||||||
@ -105,9 +105,10 @@ func (*IntSet) Clear() // remove all elements from the set
|
|||||||
func (*IntSet) Copy() *IntSet // return a copy of the set
|
func (*IntSet) Copy() *IntSet // return a copy of the set
|
||||||
```
|
```
|
||||||
|
|
||||||
**练习 6.2:** 定义一个变参方法(*IntSet).AddAll(...int),这个方法可以为一组IntSet值求和,比如s.AddAll(1,2,3)。
|
**练习 6.2:** 定义一个变参方法(*IntSet).AddAll(...int),这个方法可以添加一组IntSet,比如s.AddAll(1,2,3)。
|
||||||
|
|
||||||
**练习 6.3:** (*IntSet).UnionWith会用|操作符计算两个集合的交集,我们再为IntSet实现另外的几个函数IntersectWith(交集:元素在A集合B集合均出现),DifferenceWith(差集:元素出现在A集合,未出现在B集合),SymmetricDifference(并差集:元素出现在A但没有出现在B,或者出现在B没有出现在A)。
|
**练习 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)
|
**练习 6.5:** 我们这章定义的IntSet里的每个字都是用的uint64类型,但是64位的数值可能在32位的平台上不高效。修改程序,使其使用uint类型,这种类型对于32位平台来说更合适。当然了,这里我们可以不用简单粗暴地除64,可以定义一个常量来决定是用32还是64,这里你可能会用到平台的自动判断的一个智能表达式:32 << (^uint(0) >> 63)
|
||||||
|
@ -108,7 +108,13 @@ html body div div h2: B Definitions for Character Normalization
|
|||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
**练习 7.17:** 扩展xmlselect程序以便让元素不仅仅可以通过名称选择,也可以通过它们CSS样式上属性进行选择;例如一个像这样<div id="page" class="wide">的元素可以通过匹配id或者class同时还有它的名称来进行选择。
|
**练习 7.17:** 扩展xmlselect程序以便让元素不仅仅可以通过名称选择,也可以通过它们CSS样式上属性进行选择;例如一个像这样
|
||||||
|
|
||||||
|
``` html
|
||||||
|
<div id="page" class="wide">
|
||||||
|
```
|
||||||
|
|
||||||
|
的元素可以通过匹配id或者class同时还有它的名称来进行选择。
|
||||||
|
|
||||||
**练习 7.18:** 使用基于标记的解码API,编写一个可以读取任意XML文档和构造这个文档所代表的普通节点树的程序。节点有两种类型:CharData节点表示文本字符串,和 Element节点表示被命名的元素和它们的属性。每一个元素节点有一个字节点的切片。
|
**练习 7.18:** 使用基于标记的解码API,编写一个可以读取任意XML文档和构造这个文档所代表的普通节点树的程序。节点有两种类型:CharData节点表示文本字符串,和 Element节点表示被命名的元素和它们的属性。每一个元素节点有一个字节点的切片。
|
||||||
|
|
||||||
|
@ -4,4 +4,4 @@
|
|||||||
|
|
||||||
相反,一个goroutine会以一个很小的栈开始其生命周期,一般只需要2KB。一个goroutine的栈,和操作系统线程一样,会保存其活跃或挂起的函数调用的本地变量,但是和OS线程不太一样的是一个goroutine的栈大小并不是固定的;栈的大小会根据需要动态地伸缩。而goroutine的栈的最大值有1GB,比传统的固定大小的线程栈要大得多,尽管一般情况下,大多goroutine都不需要这么大的栈。
|
相反,一个goroutine会以一个很小的栈开始其生命周期,一般只需要2KB。一个goroutine的栈,和操作系统线程一样,会保存其活跃或挂起的函数调用的本地变量,但是和OS线程不太一样的是一个goroutine的栈大小并不是固定的;栈的大小会根据需要动态地伸缩。而goroutine的栈的最大值有1GB,比传统的固定大小的线程栈要大得多,尽管一般情况下,大多goroutine都不需要这么大的栈。
|
||||||
|
|
||||||
练习 9.4: 创建一个流水线程序,支持用channel连接任意数量的goroutine,在跑爆内存之前,可以创建多少流水线阶段?一个变量通过整个流水线需要用多久?(这个练习题翻译不是很确定。。)
|
** 练习 9.4:** 创建一个流水线程序,支持用channel连接任意数量的goroutine,在跑爆内存之前,可以创建多少流水线阶段?一个变量通过整个流水线需要用多久?(这个练习题翻译不是很确定。。)
|
||||||
|
@ -6,4 +6,4 @@ Go的运行时包含了其自己的调度器,这个调度器使用了一些技
|
|||||||
|
|
||||||
和操作系统的线程调度不同的是,Go调度器并不是用一个硬件定时器而是被Go语言"建筑"本身进行调度的。例如当一个goroutine调用了time.Sleep或者被channel调用或者mutex操作阻塞时,调度器会使其进入休眠并开始执行另一个goroutine直到时机到了再去唤醒第一个goroutine。因为这种调度方式不需要进入内核的上下文,所以重新调度一个goroutine比调度一个线程代价要低得多。
|
和操作系统的线程调度不同的是,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消息。这样的程序每秒可以支持多少次通信?
|
||||||
|
@ -20,4 +20,4 @@ $ GOMAXPROCS=2 go run hacker-cliché.go
|
|||||||
|
|
||||||
在第一次执行时,最多同时只能有一个goroutine被执行。初始情况下只有main goroutine被执行,所以会打印很多1。过了一段时间后,GO调度器会将其置为休眠,并唤醒另一个goroutine,这时候就开始打印很多0了,在打印的时候,goroutine是被调度到操作系统线程上的。在第二次执行时,我们使用了两个操作系统线程,所以两个goroutine可以一起被执行,以同样的频率交替打印0和1。我们必须强调的是goroutine的调度是受很多因子影响的,而runtime也是在不断地发展演进的,所以这里的你实际得到的结果可能会因为版本的不同而与我们运行的结果有所不同。
|
在第一次执行时,最多同时只能有一个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有多少个核心?
|
||||||
|
Loading…
Reference in New Issue
Block a user