tanish-kr's learning log

Learning output log

Go

関数

関数宣言

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

結果リストを省略すると、結果を返さない関数になる。結果リストが存在する場合は必ずreturn文で終了しなければならない。

シグネチャ(signature)

関数の型はその関数のsignatureと呼ばれることがあり、パラメータの型の型と結果の列の型が同じであれば、同じ型、すなわち同じsignatureを持つ。パラメータの名前と結果の名前は関数の方には影響しない。パラメータや結果をまとめた形式で宣言しても影響しない。

  • すべての関数呼び出しは、パラメータgは宣言された順にそれぞれに対する引数を提供しなければならない
  • デフォルトパラメータという概念は存在しない
  • パラメータは関数本体内ではローカル変数であり、その初期値は呼び出し元が提供した引数の値に設定されている。

注意点

  • 引数は値で渡されているので、関数はそれぞれの引数のコピーを受け取る。基本的にコピーに対する修正は、呼び出し元には影響しないが、引数にポインタ等を渡した場合、引数によって間接的に参照されている変数に対して関数側で修正を行うと、呼び出し元の変数に影響が出てしまう。

再帰

関数は自分自身を直接的あるいは間接的に呼び出すことができる。

func sum(num int) (totalNum int) {
    if num == 0 {
      return 0
    }
    return num + sum(num - 1)
}

func main() {
  for n := 0; n <= 10; n++ {
      fmt.Println(sum(10))
  }
}

複数戻り値

  • 結果は値のタプル
resp, err := http.Get(url)
  • 値の一つを無視するためには、その値をブランク識別子へ代入する
links, _ := findLinks(url)

エラー処理について

関数がエラーを返した場合、そのエラーを検査して適切に対応するのは呼び出し元の責務。

エラーを返してきた場合の対応

  • エラーを呼び出し元に返す
resp, err := http.Get(url)
if err != nil {
    return nil, err
}
  • 元のエラーに追加の情報を付与して返す
doc, err := html.Parse(resp.Body)
resp.Body.Close()
if err != nil {
    return nil, fmt.Errorf("Parsing %s as HTML : %v", url, err)
}

fmt.Errorf関数はfmt.Sprintfを使ってエラーメッセージをフォーマットし、新たなerror値を返す

  • 処理を進めるのが不可能な場合、エラーを表示してプログラムを停止させる

mainパッケージのみに限定するべき

if err := WaitForServer(url); errr != nil {
    fmt.Fprintf(os.Stderr, "Site is down: %v\n", err)
    os.Exit(1)
}

以下log.Fatelfでも同じことが可能

if err := WaitForServer(url); errr != nil {
    log.Fatelf("Site is down: %v\n", err)
}

関数値

Goでは関数はファーストクラス値であり、他の値と同様に型を持ち、変数に代入したり、関数へ渡したり、関数から返したりできる。

  • 関数値は比較は出来ない
  • マップのキーとして使用できない

無名関数

名前付き関数はパッケージレベルで宣言できるが関数値を表す関数リテラルは全ての式内で使える。関数リテラルは式であり、その値は無名関数と呼ばれる。

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

func main() {
    f := squares()
    fmt.Println(f()) // 1
    fmt.Println(f()) // 4
    fmt.Println(f()) // 9
    fmt.Println(f()) // 16
}

ループ変数の補足

ループ変数により作成される全ての関数値は、同じ変数を「捕捉」して共有する(特定の瞬間の値ではなく、アドレス化可能なメモリ位置を共有する)

可変個引数関数

可変個の引数で呼び出すことができる関数

func sum(vals ...int) {
    total := 0
    for _, val := range vals {
        total += val
    }
    return total
}
fmt.Println(sum()) // 0
fmt.Println(sum(3)) // 3
fmt.Println(sum(1, 2, 3, 4)) // 10

遅延関数呼び出し

defer文を使用することで、それを呼び出した関数が終了する際に実行すべき処理を記述することができる。

deferとpanicとrecover

func main() {
    defer fmt.Println("A")
    fmt.Println("B")
}
// mainの最後にAが出力される

複数書くと、最初にdeferされた行が一番最後に実行される

パニック

Goランタイムが実行時にエラーを検出した場合にはpanicになる。通常の実行は停止し、そのゴルーチン内の全ての遅延関数の呼び出しが行われ、プログラムはログメッセージを表示してクラッシュする。そのログメッセージにはパニック値と、パニック時に動作していた関数呼び出しを示しているスタックトレースが含まれている。

Goのパニックと向き合う

リカバー

recoverはpanicの現状を終了させてパニック値を返す。パニック担っていない場合はnilを返す。 予期できるエラーの場合は、recoverは使うべきではない、通常errorで実装すること。