batch replace escape

This commit is contained in:
Xargin 2016-10-18 13:29:52 +08:00
parent 3da62d2c3f
commit d3ce2c9ba6
8 changed files with 15 additions and 15 deletions

View File

@ -106,7 +106,7 @@ func Benchmark1000(b *testing.B) { benchmark(b, 1000) }
**练习 11.6:** 为2.6.2节的练习2.4和练习2.5的PopCount函数编写基准测试。看看基于表格算法在不同情况下对提升性能会有多大帮助。 **练习 11.6:** 为2.6.2节的练习2.4和练习2.5的PopCount函数编写基准测试。看看基于表格算法在不同情况下对提升性能会有多大帮助。
**练习 11.7:** 为\*IntSet§6.5的Add、UnionWith和其他方法编写基准测试使用大量随机输入。你可以让这些方法跑多快选择字的大小对于性能的影响如何IntSet和基于内建map的实现相比有多快 **练习 11.7:** 为`*IntSet`§6.5的Add、UnionWith和其他方法编写基准测试使用大量随机输入。你可以让这些方法跑多快选择字的大小对于性能的影响如何IntSet和基于内建map的实现相比有多快

View File

@ -71,7 +71,7 @@ func compute() (value float64, ok bool) {
} }
``` ```
接下来的程序演示了通过浮点计算生成的图形。它是带有两个参数的z = f(x, y)函数的三维形式使用了可缩放矢量图形SVG格式输出SVG是一个用于矢量线绘制的XML标准。图3.1显示了sin(r)/r函数的输出图形其中r是sqrt(x*x+y*y)。 接下来的程序演示了通过浮点计算生成的图形。它是带有两个参数的z = f(x, y)函数的三维形式使用了可缩放矢量图形SVG格式输出SVG是一个用于矢量线绘制的XML标准。图3.1显示了sin(r)/r函数的输出图形其中r是`sqrt(x*x+y*y)`
![](../images/ch3-01.png) ![](../images/ch3-01.png)

View File

@ -13,7 +13,7 @@ func (p *Point) ScaleBy(factor float64) {
在现实的程序里一般会约定如果Point这个类有一个指针作为接收器的方法那么所有Point的方法都必须有一个指针接收器即使是那些并不需要这个指针接收器的函数。我们在这里打破了这个约定只是为了展示一下两种方法的异同而已。 在现实的程序里一般会约定如果Point这个类有一个指针作为接收器的方法那么所有Point的方法都必须有一个指针接收器即使是那些并不需要这个指针接收器的函数。我们在这里打破了这个约定只是为了展示一下两种方法的异同而已。
只有类型(Point)和指向他们的指针(*Point),才是可能会出现在接收器声明里的两种接收器。此外,为了避免歧义,在声明方法时,如果一个类型名本身是一个指针的话,是不允许其出现在接收器中的,比如下面这个例子: 只有类型(Point)和指向他们的指针`(*Point)`,才是可能会出现在接收器声明里的两种接收器。此外,为了避免歧义,在声明方法时,如果一个类型名本身是一个指针的话,是不允许其出现在接收器中的,比如下面这个例子:
```go ```go
type P *int type P *int

View File

@ -17,7 +17,7 @@ scaleP(3) // then (6, 12)
scaleP(10) // then (60, 120) scaleP(10) // then (60, 120)
``` ```
在一个包的API需要一个函数值、且调用方希望操作的是某一个绑定了对象的方法的话方法"值"会非常实用(=_=真是绕)。举例来说下面例子中的time.AfterFunc这个函数的功能是在指定的延迟时间之后来执行一个(译注:另外的)函数。且这个函数操作的是一个Rocket对象r 在一个包的API需要一个函数值、且调用方希望操作的是某一个绑定了对象的方法的话方法"值"会非常实用(``=_=`真是绕)。举例来说下面例子中的time.AfterFunc这个函数的功能是在指定的延迟时间之后来执行一个(译注:另外的)函数。且这个函数操作的是一个Rocket对象r
```go ```go
type Rocket struct { /* ... */ } type Rocket struct { /* ... */ }
@ -36,7 +36,7 @@ time.AfterFunc(10 * time.Second, r.Launch)
和方法"值"相关的还有方法表达式。当调用一个方法时,与调用一个普通的函数相比,我们必须要用选择器(p.Distance)语法来指定方法的接收器。 和方法"值"相关的还有方法表达式。当调用一个方法时,与调用一个普通的函数相比,我们必须要用选择器(p.Distance)语法来指定方法的接收器。
当T是一个类型时方法表达式可能会写作T.f或者(*T).f会返回一个函数"值",这种函数会将其第一个参数用作接收器,所以可以用通常(译注:不写选择器)的方式来对其进行调用: 当T是一个类型时方法表达式可能会写作`T.f`或者`(*T).f`,会返回一个函数"值",这种函数会将其第一个参数用作接收器,所以可以用通常(译注:不写选择器)的方式来对其进行调用:
```go ```go
p := Point{1, 2} p := Point{1, 2}

View File

@ -86,7 +86,7 @@ fmt.Println(x.String()) // "{1 9 42 144}"
fmt.Println(x.Has(9), x.Has(123)) // "true false" fmt.Println(x.Has(9), x.Has(123)) // "true false"
``` ```
这里要注意我们声明的String和Has两个方法都是以指针类型*IntSet来作为接收器的但实际上对于这两个类型来说把接收器声明为指针类型也没什么必要。不过另外两个函数就不是这样了因为另外两个函数操作的是s.words对象如果你不把接收器声明为指针对象那么实际操作的是拷贝对象而不是原来的那个对象。因此因为我们的String方法定义在IntSet指针上所以当我们的变量是IntSet类型而不是IntSet指针时可能会有下面这样让人意外的情况 这里要注意我们声明的String和Has两个方法都是以指针类型`*IntSet`来作为接收器的但实际上对于这两个类型来说把接收器声明为指针类型也没什么必要。不过另外两个函数就不是这样了因为另外两个函数操作的是s.words对象如果你不把接收器声明为指针对象那么实际操作的是拷贝对象而不是原来的那个对象。因此因为我们的String方法定义在IntSet指针上所以当我们的变量是IntSet类型而不是IntSet指针时可能会有下面这样让人意外的情况
```go ```go
fmt.Println(&x) // "{1 9 42 144}" fmt.Println(&x) // "{1 9 42 144}"
@ -94,7 +94,7 @@ fmt.Println(x.String()) // "{1 9 42 144}"
fmt.Println(x) // "{[4398046511618 0 65536]}" 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数组实现下面这些方法

View File

@ -12,13 +12,13 @@ type IntSet struct {
} }
``` ```
当然我们也可以把IntSet定义为一个slice类型尽管这样我们就需要把代码中所有方法里用到的s.words用*s替换掉了 当然我们也可以把IntSet定义为一个slice类型尽管这样我们就需要把代码中所有方法里用到的s.words用`*s`替换掉了:
```go ```go
type IntSet []uint64 type IntSet []uint64
``` ```
尽管这个版本的IntSet在本质上是一样的他也可以允许其它包中可以直接读取并编辑这个slice。换句话说相对*s这个表达式会出现在所有的包中s.words只需要在定义IntSet的包中出现(译注:所以还是推荐后者吧的意思)。 尽管这个版本的IntSet在本质上是一样的他也可以允许其它包中可以直接读取并编辑这个slice。换句话说相对`*s`这个表达式会出现在所有的包中s.words只需要在定义IntSet的包中出现(译注:所以还是推荐后者吧的意思)。
这种基于名字的手段使得在语言中最小的封装单元是package而不是像其它语言一样的类型。一个struct类型的字段对同一个包的所有代码都有可见性无论你的代码是写在一个函数还是一个方法里。 这种基于名字的手段使得在语言中最小的封装单元是package而不是像其它语言一样的类型。一个struct类型的字段对同一个包的所有代码都有可见性无论你的代码是写在一个函数还是一个方法里。

View File

@ -2,7 +2,7 @@
即使我们小心到不能再小心但在并发程序中犯错还是太容易了。幸运的是Go的runtime和工具链为我们装备了一个复杂但好用的动态分析工具竞争检查器(the race detector)。 即使我们小心到不能再小心但在并发程序中犯错还是太容易了。幸运的是Go的runtime和工具链为我们装备了一个复杂但好用的动态分析工具竞争检查器(the race detector)。
只要在go buildgo run或者go test命令后面加上-race的flag就会使编译器创建一个你的应用的“修改”版或者一个附带了能够记录所有运行期对共享变量访问工具的test并且会记录下每一个读或者写共享变量的goroutine的身份信息。另外修改版的程序会记录下所有的同步事件比如go语句channel操作以及对(\*sync.Mutex).Lock(\*sync.WaitGroup).Wait等等的调用。(完整的同步事件集合是在The Go Memory Model文档中有说明该文档是和语言文档放在一起的。译注https://golang.org/ref/mem) 只要在go buildgo run或者go test命令后面加上-race的flag就会使编译器创建一个你的应用的“修改”版或者一个附带了能够记录所有运行期对共享变量访问工具的test并且会记录下每一个读或者写共享变量的goroutine的身份信息。另外修改版的程序会记录下所有的同步事件比如go语句channel操作以及对`(*sync.Mutex).Lock``(*sync.WaitGroup).Wait`等等的调用。(完整的同步事件集合是在The Go Memory Model文档中有说明该文档是和语言文档放在一起的。译注https://golang.org/ref/mem)
竞争检查器会检查这些事件会寻找在哪一个goroutine中出现了这样的case例如其读或者写了一个共享变量这个共享变量是被另一个goroutine在没有进行干预同步操作便直接写入的。这种情况也就表明了是对一个共享变量的并发访问即数据竞争。这个工具会打印一份报告内容包含变量身份读取和写入的goroutine中活跃的函数的调用栈。这些信息在定位问题时通常很有用。9.7节中会有一个竞争检查器的实战样例。 竞争检查器会检查这些事件会寻找在哪一个goroutine中出现了这样的case例如其读或者写了一个共享变量这个共享变量是被另一个goroutine在没有进行干预同步操作便直接写入的。这种情况也就表明了是对一个共享变量的并发访问即数据竞争。这个工具会打印一份报告内容包含变量身份读取和写入的goroutine中活跃的函数的调用栈。这些信息在定位问题时通常很有用。9.7节中会有一个竞争检查器的实战样例。

View File

@ -71,7 +71,7 @@ for url := range incomingURLs() {
} }
``` ```
我们可以使用测试包(第11章的主题)来系统地鉴定缓存的效果。从下面的测试输出我们可以看到URL流包含了一些重复的情况尽管我们第一次对每一个URL的(\*Memo).Get的调用都会花上几百毫秒但第二次就只需要花1毫秒就可以返回完整的数据了。 我们可以使用测试包(第11章的主题)来系统地鉴定缓存的效果。从下面的测试输出我们可以看到URL流包含了一些重复的情况尽管我们第一次对每一个URL的`(*Memo).Get`的调用都会花上几百毫秒但第二次就只需要花1毫秒就可以返回完整的数据了。
``` ```
$ go test -v gopl.io/ch9/memo1 $ go test -v gopl.io/ch9/memo1
@ -300,7 +300,7 @@ func (memo *Memo) Close() { close(memo.requests) }
上面的Get方法会创建一个response channel把它放进request结构中然后发送给monitor goroutine然后马上又会接收它。 上面的Get方法会创建一个response channel把它放进request结构中然后发送给monitor goroutine然后马上又会接收它。
cache变量被限制在了monitor goroutine (\*Memo).server中下面会看到。monitor会在循环中一直读取请求直到request channel被Close方法关闭。每一个请求都会去查询cache如果没有找到条目的话那么就会创建/插入一个新的条目。 cache变量被限制在了monitor goroutine ``(*Memo).server`下面会看到。monitor会在循环中一直读取请求直到request channel被Close方法关闭。每一个请求都会去查询cache如果没有找到条目的话那么就会创建/插入一个新的条目。
```go ```go
func (memo *Memo) server(f Func) { func (memo *Memo) server(f Func) {
@ -332,13 +332,13 @@ func (e *entry) deliver(response chan<- result) {
} }
``` ```
和基于互斥量的版本类似第一个对某个key的请求需要负责去调用函数f并传入这个key将结果存在条目里并关闭ready channel来广播条目的ready消息。使用(\*entry).call来完成上述工作。 和基于互斥量的版本类似第一个对某个key的请求需要负责去调用函数f并传入这个key将结果存在条目里并关闭ready channel来广播条目的ready消息。使用`(*entry).call`来完成上述工作。
紧接着对同一个key的请求会发现map中已经有了存在的条目然后会等待结果变为ready并将结果从response发送给客户端的goroutien。上述工作是用(\*entry).deliver来完成的。对call和deliver方法的调用必须让它们在自己的goroutine中进行以确保monitor goroutines不会因此而被阻塞住而没法处理新的请求。 紧接着对同一个key的请求会发现map中已经有了存在的条目然后会等待结果变为ready并将结果从response发送给客户端的goroutien。上述工作是用`(*entry).deliver`来完成的。对call和deliver方法的调用必须让它们在自己的goroutine中进行以确保monitor goroutines不会因此而被阻塞住而没法处理新的请求。
这个例子说明我们无论用上锁,还是通信来建立并发程序都是可行的。 这个例子说明我们无论用上锁,还是通信来建立并发程序都是可行的。
上面的两种方案并不好说特定情境下哪种更好,不过了解他们还是有价值的。有时候从一种方式切换到另一种可以使你的代码更为简洁。(译注不是说好的golang推崇通信并发么) 上面的两种方案并不好说特定情境下哪种更好,不过了解他们还是有价值的。有时候从一种方式切换到另一种可以使你的代码更为简洁。(译注不是说好的golang推崇通信并发么)
**练习 9.3** 扩展Func类型和(\*Memo).Get方法支持调用方提供一个可选的done channel使其具备通过该channel来取消整个操作的能力(§8.9)。一个被取消了的Func的调用结果不应该被缓存。 **练习 9.3** 扩展Func类型和`(*Memo).Get`方法支持调用方提供一个可选的done channel使其具备通过该channel来取消整个操作的能力(§8.9)。一个被取消了的Func的调用结果不应该被缓存。