Goの勉強 関数

Goの勉強をやり始めたのでメモ。
プログラミング言語 Go」を読んでる。

宣言

func name(parameter-list) (result-list) {  
        body  
}  

パラメータと同様に返り値にも名前を付けることができる
その場合は返り値の名前のローカル変数が作られる

func pi() (pi float64) {  
    pi = math.Pi // 宣言なしで変数 pi を使える  
    return  
}  

パラメータを使用しない場合、 _ を使うか、名前を省略できる

func zero(int, int) int {  
    return 0  
}  

func zero2(_ int, _ int) int {  
    return 0  
}  
  • Go にはデフォルト値や名前付き引数はない
  • 引数は値渡しされる(コピーを受け取る)が、間接的に参照されているものは影響を受ける可能性がある
  • 実装がない関数は Go 以外で実装されている

複数戻り値

複数戻り値はそのまま複数パラメータとして使用できる

func findLinks(url string) ([]string, error) {  
}  

log.Println(findLinks(url))  

戻り値にうまく名前付けをすることができれば、関数の結果の意味をドキュメント化することができる

func Size(rect image.Rectangle) (width, height int)  

エラー

  • Go にも例外機構はあるが、バグであることを示す本当に予期されていないエラーを報告するために使われ、予期されるエラーには使用されない
  • 関数がエラーを返した場合、エラーに対して対応するのは呼び出し元の責任である
  • エラーメッセージは連鎖されることが多いので、先頭を大文字にしたり改行を入れるべきではない
  • ファイル読み込み時にこれ以上入力がない場合にEOFがエラーとして返される

関数値

関数は他のプログラミング言語と同様に関数を変数に入れることができる

func square(n int) {  
        return n * n  
}  

f := square  
fmt.Println(f(3))  
  • 関数型はシグネチャで判断される。つまり func(int)func(int, int) は別の型
  • 関数型は nil と比較できるが、他の値とは比較可能ではない

無名関数

名前付き関数はパッケージレベルでだけ宣言できるが、関数リテラルはすべての式内で使用できる

strings.Map(func(r rune) rune { return r + 1 }, "HAL-9000")  

無名関数の外側の変数を参照できる。

func squares() func() int {  
        var x int  
        return func() int {  
                x++  
                return x * x  
        }  
}  

無名関数を再帰的に呼び出すためには、先に変数を定義し、その後に代入する必要がある。

無名関数はその瞬間の変数の値を補足するのではなく、変数自体を補足するので注意。

funcs []func()  
for _, item := range items {  
        funcs = append(funcs, func() string { return item })  
}  

for _, action := range funcs {  
    fmt.Println(action())  
}  

この場合、 items の最後の要素がずらずらと並ぶだけである。

可変個引数

  • 可変個引数はスライスで渡ってくる。
    • ただし、シグネチャとしては可変個引数とスライスは違う。

遅延関数呼び出し

  • defer を使用すると、その文は return のタイミングで呼ばれるようになる。

パニック・リカバー

  • panic を使用することで、システムをクラッシュさせることができる。
  • panic が使用されると、クラッシュされる前に遅延呼び出しが行われる。
  • recover を使用するとパニック状態を解消できる。
    • 一般的な規則として、他のパッケージのパニックからの回復を試みるべきでない。