ファーストクラス関数
変数に関数を代入
realSensor()
と、faceSensor()
関数を作成
本物のセンサーを繋がるまでの間は、偽物のセンサーが擬似乱数を返す
互換性を実現する設計
sensor
にrealSensor
を再代入できるのは、
どちらの関数も、同じ数、同じ型のパラメータと戻り値だから
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
package main
import (
"fmt"
"math/rand"
"time"
)
type kelvin float64 // 独自の型を定義
func fakeSensor() kelvin {
rand.Seed(time.Now().UnixNano()) // Seedに時刻を与える
return kelvin(rand.Intn(151) + 151)
}
func realSensor() kelvin {
return 0 // 後で本物のセンサーを実装する予定ってこと
}
func main() {
sensor := fakeSensor // 関数を変数に代入する
fmt.Println(sensor())
sensor = realSensor // 関数を変数に代入する
fmt.Println(sensor())
}
|
実行結果
261
0
関数を他の関数に渡す
関数に関数を渡すことはコードを細分化する協力な手段になります
- 変数は関数に渡すことができる(一般的)
- 変数は関数を参照できる(直前の変数に関数を代入で試した)
- 関数を他の関数に渡すことができる、そうGoならね(他にできる言語があるのか知らない)
試してみたら面白かった、使いこなせればとても便利
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
package main
import (
"fmt"
"math/rand"
"time"
)
type kelvin float64 // 独自の型を定義
// 追加
// 第2引数で関数を受け取る
func measureTemperture(samples int, sensor func() kelvin) {
for i := 0; i < samples; i++ {
k := sensor()
fmt.Printf("%vo K\n", k)
time.Sleep(time.Second)
}
}
func fakeSensor() kelvin {
rand.Seed(time.Now().UnixNano()) // Seedに時刻を与える
return kelvin(rand.Intn(151) + 151)
}
func realSensor() kelvin {
return 0 // 後で本物のセンサーを実装する予定ってこと
}
func main() {
//追加
// 関数を第2引数に
measureTemperture(3, fakeSensor)
}
|
実行結果
155o K
202o K
171o K
関数型の宣言
関数に多数のパラメータがあるときは、型を使うことによってすっきりとさせられる
sensor型
の関数
1
|
type sensor func() kelvin
|
sensor型
を使ってコードを圧縮する
この例だけだと改善とは思えない
sensor
をあいちこちで使う場合や、関数に多数パラメータがある場合に有効
使いこなせる自信がない
Befor
1
|
func measureTemperture(samples int, sensor func() kelvin)
|
After
1
|
func measureTemperture(samples int, s sensor)
|
クロージャと無名関数
普通の関数と違って、クロージャ
自分を囲むスコープにある変数への参照を保持できる
何を言っているのかさっぱりわからない
無名関数に変数を代入すれば、その変数を他の関数と同様に使える
1
2
3
4
5
6
7
8
9
10
11
|
package main
import "fmt"
var f = func() { // 変数(f)に無名関数を代入
fmt.Println("無名関数を使った")
}
func main() {
f()
}
|
実行結果
無名関数を使った
変数(f)の宣言は、パッケージのスコープにあっても、関数内にあってもOK
1
2
3
4
5
6
7
8
9
10
|
package main
import "fmt"
func main() {
f := func(message string) { // 無名関数を変数に代入
fmt.Println(message)
}
f("関数の中にあるパターン")
}
|
実行結果
関数の中にあるパターン
memo
無名関数の宣言と呼び出しを1ステップで行うことも可能
1
2
3
4
5
6
7
8
9
|
package main
import "fmt"
func main() {
func() { // 無名関数の宣言
fmt.Println("省略パターン")
}() // ()で関数を呼び出し
}
|
実行結果
省略パターン
memo
他の関数から既存の名前付き関数を返すことは可能ですが
新しい無名関数を宣言して返す方がずっと便利
難しくて何を言っているのかわからない
無名関数はスコープにある変数を囲い込むので、
クロージャ(閉包)という名前が付けられた
クロージャは、それを囲むスコープの変数の値をコピーするのではなく、
その変数へのリファレンスを保持するので、その変数を変更すると、
無名関数の呼び出しに反映されます
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
package main
import "fmt"
type kelvin float64
// sensor 関数型
type sensor func() kelvin
func realSensor() kelvin {
return 2 // TODO:実数値で実装
}
func calibrate(s sensor, offset kelvin) sensor {
return func() kelvin { // 無名関数を宣言して返す
return s() + offset
}
}
func main() {
sensor := calibrate(realSensor, 5)
fmt.Println(sensor())
}
|
実行結果
7
まとめ
- 関数をファーストクラスと
- メソッドは型に関数を割り当てた感じ
感想
手を動かしてみるまで全然イメージ掴めなかったけど、 実際に書いてみると理解できた
便利で型によって振る舞いが変わるから変なエラーの防止(信頼性)にも繋がりそうだ
参考
なし