gopl-zh.github.com/ch6/ch6-04.md
2015-12-18 14:49:31 +08:00

77 lines
3.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 6.4. 方法值和方法表達式
我們經常選擇一個方法併且在同一個表達式里執行比如常見的p.Distance()形式實際上將其分成兩步來執行也是可能的。p.Distance叫作“選擇器”選擇器會返迴一個方法"值"->一個將方法(Point.Distance)綁定到特定接收器變量的函數。這個函數可以不通過指定其接收器卽可被調用;卽調用時不需要指定接收器(譯註:因爲已經在前文中指定過了),隻要傳入函數的參數卽可:
```go
p := Point{1, 2}
q := Point{4, 6}
distanceFromP := p.Distance // method value
fmt.Println(distanceFromP(q)) // "5"
var origin Point // {0, 0}
fmt.Println(distanceFromP(origin)) // "2.23606797749979", ;5
scaleP := p.ScaleBy // method value
scaleP(2) // p becomes (2, 4)
scaleP(3) // then (6, 12)
scaleP(10) // then (60, 120)
```
在一個包的API需要一個函數值、且調用方希望操作的是某一個綁定了對象的方法的話方法"值"會非常實用(=_=眞是繞)。舉例來説下面例子中的time.AfterFunc這個函數的功能是在指定的延遲時間之後來執行一個(譯註:另外的)函數。且這個函數操作的是一個Rocket對象r
```go
type Rocket struct { /* ... */ }
func (r *Rocket) Launch() { /* ... */ }
r := new(Rocket)
time.AfterFunc(10 * time.Second, func() { r.Launch() })
```
直接用方法"值"傳入AfterFunc的話可以更爲簡短
```go
time.AfterFunc(10 * time.Second, r.Launch)
```
譯註:省掉了上面那個例子里的匿名函數。
和方法"值"相關的還有方法表達式。當調用一個方法時,與調用一個普通的函數相比,我們必鬚要用選擇器(p.Distance)語法來指定方法的接收器。
當T是一個類型時方法表達式可能會寫作T.f或者(*T).f會返迴一個函數"值",這種函數會將其第一個參數用作接收器,所以可以用通常(譯註:不寫選擇器)的方式來對其進行調用:
```go
p := Point{1, 2}
q := Point{4, 6}
distance := Point.Distance // method expression
//譯註這個Distance實際上是指定了Point對象爲接收器的一個方法func (p Point) Distance()但通過Point.Distance得到的函數需要比實際的Distance方法多一個參數卽其需要用第一個額外參數指定接收器後面排列Distance方法的參數。看起來本書中函數和方法的區别是指有沒有接收器而不像其他語言那樣是指有沒有返迴值。
fmt.Println(distance(p, q)) // "5"
fmt.Printf("%T\n", distance) // "func(Point, Point) float64"
scale := (*Point).ScaleBy
scale(&p, 2)
fmt.Println(p) // "{2 4}"
fmt.Printf("%T\n", scale) // "func(*Point, float64)"
```
當你根據一個變量來決定調用同一個類型的哪個函數時方法表達式就顯得很有用了。你可以根據選擇來調用接收器各不相同的方法。下面的例子變量op代表Point類型的addition或者subtraction方法Path.TranslateBy方法會爲其Path數組中的每一個Point來調用對應的方法
```go
type Point struct{ X, Y float64 }
func (p Point) Add(q Point) Point { return Point{p.X + q.X, p.Y + q.Y} }
func (p Point) Sub(q Point) Point { return Point{p.X - q.X, p.Y - q.Y} }
type Path []Point
func (path Path) TranslateBy(offset Point, add bool) {
var op func(p, q Point) Point
if add {
op = Point.Add
} else {
op = Point.Sub
}
for i := range path {
// Call either path[i].Add(offset) or path[i].Sub(offset).
path[i] = op(path[i], offset)
}
}
```