thirose’s blog

openstackやpythonなどなど

A tour of Go の Flow control statements の exercise

最近インフラ周りでも、golangOSSが増えてきてコードを読まないといけないことが増えたからgolangチュートリアルで書き方を覚えようと思い始めた。

exercise-loops-and-functions.go

関数とループを使った簡単な練習として、平方根の計算を実装してみましょう: 数値 x が与えられたときに z² が最も x に近い数値 z を求めるプログラムです。

コード

$ cat exercise-loops-and-functions.go
package main

import (
    "fmt"
)

func Sqrt(x float64) float64 {
    z := 1.0
    for i := 0; i < 10; i++ {
        z -= (z*z - x) / (2*z)
    }
    return z
}

func main() {
    fmt.Println(Sqrt(2))
}

結果

$ go run exercise-loops-and-functions.go
1.414213562373095

確認

問題文には、「 あなたの関数の結果は標準ライブラリの math.Sqrt にどれくらい近づきましたか?」と書かれていたので、実際に動かしてみる。

$ cat loop-and-functions.go 
package main

import (
    "fmt"
    "math"
)

func main() {
    fmt.Println(math.Sqrt(2))
}

結果

$ go run loop-and-functions.go 
1.4142135623730951

答えは1桁多く標準ライブラリの方が多く表示されてるけど、同じと言えるだろう。 ざっくりだけど、他は写経して終わり。

deferがとても新鮮だったような気がする。 deferで定義しておいたprintが最後に評価され、それが出力される。 だから、確実にそのメソッドを終わらせるために定義したい場合に使うのだろうなと思う。 調べてたら、検証している人がいた。

blog.amedama.jp

自分でも以下のようなコードで調べてみたら、deferでpanicによるハンドリングができることがわかった。

$ cat recover.go
package main

import (
    "fmt"
)

func judge_err() {
    fmt.Println("judge the error")
    err := recover()
    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Println("no error")
    }
}

func hello_no_error() {
    defer judge_err()
    fmt.Println("Hello World")
}

func hello_error() {
    defer judge_err()
    fmt.Println("Hello World")
    panic("something error")
}

func main() {
    hello_no_error()
    hello_error()
}

これを実行すると、

$ go run recover.go
Hello World
judge the error
no error
Hello World
judge the error
something error

と、一つ目はrecoverで何も入らずno errorが入り、 二つ目はpanicを使っているので、deferで後から評価されsomething errorが出力されていることがわかります。

追記

公式的にもしっかり書いてあった。 Defer, Panic, and Recover - The Go Blog