Goでは、forとインデックスを使ってsliceを繰り返し処理することもできますし、rangeを使うこともできます。rangeを使えば、mapを繰り返し処理することもできます。
それぞれの繰り返しでは、インデックスかキーと、そのインデックスかキーにある要素のコピーの、2つの値が返されます。
sliceを繰り返し処理するとても簡単です。sliceの繰り返しは、期待どおりの順序で進みます。
xi := []int{10, 20, 30}
for i, x := range xi {
fmt.Println(i, x)
}
// outputs:
// 0, 10
// 1, 20
// 2, 30
mapを繰り返し処理するmapを繰り返し処理すると、新たな問題が生じます。順序がランダムになるのです。
hash := map[int]int{9: 10, 99: 20, 999: 30}
for k, v := range hash {
fmt.Println(k, v)
}
// outputs, for example:
// 99 20
// 999 30
// 9 10
上の出力は正しくないように見えるかもしれません。mapの宣言にある最初のキーと値のペア9 10が、最後ではなく最初に出力されると期待するのが自然だからです。
しかし、mapは本質的に順序を持ちません。最初や最後のキーと値のペアというものはないのです。
そのため、mapの要素を繰り返し処理するとき、どの要素がどの順序でたどられるかはランダムで、決まったパターンには従いません。
つまり、上の出力は起こりえますが、自分で実行したときに得られる結果とは異なるかもしれません。
詳しくは、Go Language Spec: range clauseを参照してください。
Goでは、使われていない変数があるとビルド時にエラーになります。 最初の例のように、値だけが必要なこともあります。
xi := []int{10, 20, 30}
for i, x := range xi {
fmt.Println(x)
}
// Go build failed: i declared but not used
iを_に置き換えると、その値を使わないことをコンパイラーに伝えられます。
xi := []int{10, 20, 30}
for _, x := range xi {
fmt.Println(x)
}
// outputs:
// 10
// 20
// 30
インデックスだけを出力したい場合は、xを_に置き換えるか、宣言そのものを省略します。
xi := []int{10, 20, 30}
// for i, _ := range xi {
for i := range xi {
fmt.Println(i)
}
// outputs:
// 0
// 1
// 2
型定義では、新しい名前付きの型を作ります。その型は、たとえ基底型が同じであっても、他のどの型とも異なる別個の型です。
type File []bool
ここでのFileは、チェスボードの縦1列を指します。この列には、ふつうAからHのラベルが付いています。
この列のマスはそれぞれ、そこにコマが置かれているかどうかを示す真偽値で表されます。
Fileが定義できたので、今度はこれを他の定義の中で使えます。
type Chessboard map[string]File
board := Chessboard{
"A": File{true, false, true},
"B": File{false, true, false},
}
Chessboardは、ファイルの識別子をFileの値に対応付けます。
それぞれのFileは、チェスの1つのファイルにあるマスを表す真偽値のsliceです。
こうした型定義がないと、boardはmap[string][]boolになってしまい、何を表しているのかが分かりにくくなります。
チェスの愛好家なら、自分だけのバージョンのゲームを書いてみたいと思うのではないでしょうか。確かに、ネット上にはすでにたくさんのチェスの実装が公開されているかもしれません。でも、自分で作るものは唯一無二です!
チェス盤の各マスは、アルファベットと数字の組み合わせで表されます。
A B C D E F G H
8 # _ _ _ # _ _ # 8
7 _ _ _ _ _ _ _ _ 7
6 _ _ _ _ # _ _ # 6
5 _ # _ _ _ _ _ # 5
4 _ _ _ _ _ _ # # 4
3 # _ # _ _ _ _ # 3
2 _ _ _ _ _ _ _ # 2
1 # _ _ _ _ _ _ # 1
A B C D E F G H
CountInFile(board Chessboard, file string) int関数を実装しましょう。
マップをrangeで繰り返し処理して、埋まっているマスの合計数を数えます。
整数を返します。
指定されたファイルがマップの中に見つからない場合は、0を返します。
CountInFile(board, "A")
// => 3
CountInRank(board Chessboard, rank int) int関数を実装しましょう。
指定されたランクをrangeで繰り返し処理して、埋まっているマスの合計数を数えます。
整数を返します。
指定されたランクが有効でない場合(1から8までに含まれない場合)は、0を返します。
CountInRank(board, 2)
// => 1
CountAll(board Chessboard) int関数を実装しましょう。
チェス盤にあるマスの数を数え、整数を返します。
マスの中身を調べる必要はないので、indexとvalueの両方を省略したrangeを使うことを考えてみましょう。
CountAll(board)
// => 64
CountOccupied(board Chessboard) int関数を実装しましょう。
チェス盤で埋まっているマスの数を数えます。
整数を返します。
CountOccupied(board)
// => 15