mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-12-17 19:24:19 +08:00
batch replace escape
This commit is contained in:
@@ -13,7 +13,7 @@ func (p *Point) ScaleBy(factor float64) {
|
||||
|
||||
在现实的程序里,一般会约定如果Point这个类有一个指针作为接收器的方法,那么所有Point的方法都必须有一个指针接收器,即使是那些并不需要这个指针接收器的函数。我们在这里打破了这个约定只是为了展示一下两种方法的异同而已。
|
||||
|
||||
只有类型(Point)和指向他们的指针(*Point),才是可能会出现在接收器声明里的两种接收器。此外,为了避免歧义,在声明方法时,如果一个类型名本身是一个指针的话,是不允许其出现在接收器中的,比如下面这个例子:
|
||||
只有类型(Point)和指向他们的指针`(*Point)`,才是可能会出现在接收器声明里的两种接收器。此外,为了避免歧义,在声明方法时,如果一个类型名本身是一个指针的话,是不允许其出现在接收器中的,比如下面这个例子:
|
||||
|
||||
```go
|
||||
type P *int
|
||||
|
||||
@@ -17,7 +17,7 @@ scaleP(3) // then (6, 12)
|
||||
scaleP(10) // then (60, 120)
|
||||
```
|
||||
|
||||
在一个包的API需要一个函数值、且调用方希望操作的是某一个绑定了对象的方法的话,方法"值"会非常实用(=_=真是绕)。举例来说,下面例子中的time.AfterFunc这个函数的功能是在指定的延迟时间之后来执行一个(译注:另外的)函数。且这个函数操作的是一个Rocket对象r
|
||||
在一个包的API需要一个函数值、且调用方希望操作的是某一个绑定了对象的方法的话,方法"值"会非常实用(``=_=`真是绕)。举例来说,下面例子中的time.AfterFunc这个函数的功能是在指定的延迟时间之后来执行一个(译注:另外的)函数。且这个函数操作的是一个Rocket对象r
|
||||
|
||||
```go
|
||||
type Rocket struct { /* ... */ }
|
||||
@@ -36,7 +36,7 @@ time.AfterFunc(10 * time.Second, r.Launch)
|
||||
|
||||
和方法"值"相关的还有方法表达式。当调用一个方法时,与调用一个普通的函数相比,我们必须要用选择器(p.Distance)语法来指定方法的接收器。
|
||||
|
||||
当T是一个类型时,方法表达式可能会写作T.f或者(*T).f,会返回一个函数"值",这种函数会将其第一个参数用作接收器,所以可以用通常(译注:不写选择器)的方式来对其进行调用:
|
||||
当T是一个类型时,方法表达式可能会写作`T.f`或者`(*T).f`,会返回一个函数"值",这种函数会将其第一个参数用作接收器,所以可以用通常(译注:不写选择器)的方式来对其进行调用:
|
||||
|
||||
```go
|
||||
p := Point{1, 2}
|
||||
|
||||
@@ -86,7 +86,7 @@ fmt.Println(x.String()) // "{1 9 42 144}"
|
||||
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
|
||||
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]}"
|
||||
```
|
||||
|
||||
在第一个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数组实现下面这些方法
|
||||
|
||||
|
||||
@@ -12,13 +12,13 @@ type IntSet struct {
|
||||
}
|
||||
```
|
||||
|
||||
当然,我们也可以把IntSet定义为一个slice类型,尽管这样我们就需要把代码中所有方法里用到的s.words用*s替换掉了:
|
||||
当然,我们也可以把IntSet定义为一个slice类型,尽管这样我们就需要把代码中所有方法里用到的s.words用`*s`替换掉了:
|
||||
|
||||
```go
|
||||
type IntSet []uint64
|
||||
```
|
||||
|
||||
尽管这个版本的IntSet在本质上是一样的,他也可以允许其它包中可以直接读取并编辑这个slice。换句话说,相对*s这个表达式会出现在所有的包中,s.words只需要在定义IntSet的包中出现(译注:所以还是推荐后者吧的意思)。
|
||||
尽管这个版本的IntSet在本质上是一样的,他也可以允许其它包中可以直接读取并编辑这个slice。换句话说,相对`*s`这个表达式会出现在所有的包中,s.words只需要在定义IntSet的包中出现(译注:所以还是推荐后者吧的意思)。
|
||||
|
||||
这种基于名字的手段使得在语言中最小的封装单元是package,而不是像其它语言一样的类型。一个struct类型的字段对同一个包的所有代码都有可见性,无论你的代码是写在一个函数还是一个方法里。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user