基本的に、 for
ループはセミコロン ;
で3つの部分に分かれています:
初期化ステートメントは、短い変数宣言によく利用します。その変数は for
ステートメントのスコープ内でのみ有効です。
ループは、条件式の評価が false
となった場合にイテレーションを停止します。
Note: 他の言語、C言語 や Java、JavaScriptの for
ループとは異なり、 for
ステートメントの3つの部分を括る括弧
( )
はありません。なお、中括弧 { }
は必要です。
package main import "fmt" func main() { sum := 0 for i := 0; i < 10; i++ { sum += i } fmt.Println(sum) }
初期化と後処理ステートメントの記述は任意です。
package main import "fmt" func main() { sum := 1 for ; sum < 1000; { sum += sum } fmt.Println(sum) }
セミコロン(;)を省略することもできます。つまり、C言語などにある while
は、Goでは for
だけを使います。
package main import "fmt" func main() { sum := 1 for sum < 1000 { sum += sum } fmt.Println(sum) }
ループ条件を省略すれば、無限ループ( infinite loop )になりますので、無限ループをコンパクトに表現できます。
package main func main() { for { } }
Go言語の if
ステートメントは、先ほどの for
ループと同様に、括弧 ( )
は不要で、中括弧 { }
は必要です。
(もうおなじみですね!)
package main import ( "fmt" "math" ) func sqrt(x float64) string { if x < 0 { return sqrt(-x) + "i" } return fmt.Sprint(math.Sqrt(x)) } func main() { fmt.Println(sqrt(2), sqrt(-4)) }
if
ステートメントは、 for
のように、条件の前に、評価するための簡単なステートメントを書くことができます。
ここで宣言された変数は、 if
のスコープ内だけで有効です。
(ためしに最後の return
文で、 v
を使ってみてください。
スコープの外なので使えないですよね?)
package main import ( "fmt" "math" ) func pow(x, n, lim float64) float64 { if v := math.Pow(x, n); v < lim { return v } return lim } func main() { fmt.Println( pow(3, 2, 10), pow(3, 3, 20), ) }
なお、 if
ステートメントで宣言された変数は、 else
ブロック内でも使うことができます。
(コードの補足: main
の fmt.Println
は、2つの pow
が先に実行されてから実行されます)
package main import ( "fmt" "math" ) func pow(x, n, lim float64) float64 { if v := math.Pow(x, n); v < lim { return v } else { fmt.Printf("%g >= %g\n", v, lim) } // can't use v here, though return lim } func main() { fmt.Println( pow(3, 2, 10), pow(3, 3, 20), ) }
関数とループを使った簡単な練習として、平方根の計算を実装してみましょう: 数値 x が与えられたときに z² が最も x に近い数値 z を求めたいと思います。
コンピュータは通常ループを使って x の平方根を計算します。 いくつかの z を推測することから始めて、z² がどれほど x に近づいているかに応じて z を調整できます。
z -= (z*z - x) / (2*z)
実際の平方根に近い答えになるまでこの調整を繰り返すことによって、推測はより良いものなります。
これを func Sqrt
に実装してください。
何が入力されても z の適切な開始推測値は 1 です。
まず計算を 10 回繰り返してそれぞれの z を表示します。
x (1, 2, 3, ...) のさまざまな値に対する答えがどれほど近似し、
推測が速くなるかを確認してください。
Hint: 浮動小数点の変数を初期化して宣言するには、型でキャストするか、浮動小数点を使ってみてください:
z := 1.0 z := float64(1)
次に値が変化しなくなった (もしくはごくわずかな変化しかしなくなった) 場合にループを停止させます。 それが 10 回よりも多いか少ないかを確認してください。 x や x/2 のように他の初期推測の値を z に与えてみてください。 あなたの関数の結果は標準ライブラリの math.Sqrt にどれくらい近づきましたか?
(メモ: アルゴリズムの詳細について興味がある人のために説明すると、 上の z² − xという式は、z² が最終的な期待値 x からどのくらい離れているかを表しています。 除算の 2z は z² の導関数で、z² の変化の大きさに応じて z の調整値を変化させます。 この一般的なアプローチはニュートン法と呼ばれています。 多くの関数で有効に働きますがとくに平方根では殊更有効です。)
package main import ( "fmt" ) func Sqrt(x float64) float64 { } func main() { fmt.Println(Sqrt(2)) }
switch
ステートメントは if - else
ステートメントのシーケンスを短く書く方法です。
Go の switch は C や C++、Java、JavaScript、PHP の switch と似ていますが、
Go では選択された case だけを実行してそれに続く全ての case は実行されません。
これらの言語の各 case の最後に必要な break
ステートメントが Go では自動的に提供されます。
もう一つの重要な違いは Go の switch の case は定数である必要はなく、
関係する値は整数である必要はないということです。
package main import ( "fmt" "runtime" ) func main() { fmt.Print("Go runs on ") switch os := runtime.GOOS; os { case "darwin": fmt.Println("OS X.") case "linux": fmt.Println("Linux.") default: // freebsd, openbsd, // plan9, windows... fmt.Printf("%s.", os) } }
switch caseは、上から下へcaseを評価します。 caseの条件が一致すれば、そこで停止(自動的にbreak)します。
(例えば、
switch i { case 0: case f(): }
では、 i==0
であれば、 case 0
でbreakされるため f
は呼び出されません)
package main import ( "fmt" "time" ) func main() { fmt.Println("When's Saturday?") today := time.Now().Weekday() switch time.Saturday { case today + 0: fmt.Println("Today.") case today + 1: fmt.Println("Tomorrow.") case today + 2: fmt.Println("In two days.") default: fmt.Println("Too far away.") } }
条件のないswitchは、 switch true
と書くことと同じです。
このswitchの構造は、長くなりがちな "if-then-else" のつながりをシンプルに表現できます。
package main import ( "fmt" "time" ) func main() { t := time.Now() switch { case t.Hour() < 12: fmt.Println("Good morning!") case t.Hour() < 17: fmt.Println("Good afternoon.") default: fmt.Println("Good evening.") } }
defer
ステートメントは、 defer
へ渡した関数の実行を、呼び出し元の関数の終わり(returnする)まで遅延させるものです。
defer
へ渡した関数の引数は、すぐに評価されますが、その関数自体は呼び出し元の関数がreturnするまで実行されません。
package main import "fmt" func main() { defer fmt.Println("world") fmt.Println("hello") }
defer
へ渡した関数が複数ある場合、その呼び出しはスタック( stack )されます。
呼び出し元の関数がreturnするとき、 defer
へ渡した関数は LIFO(last-in-first-out) の順番で実行されます。
defer
ステートメントについてさらに学ぶには、
こちら(blog post)を読んでみてください。
package main import "fmt" func main() { fmt.Println("counting") for i := 0; i < 10; i++ { defer fmt.Println(i) } fmt.Println("done") }
この章はこれで終わりです。