今日のコード
引用元のコード:A Tour of Go / Slices of slices
package main
import (
"fmt"
"strings" // ①
)
func main() {
// デフォルトの盤を準備…②
board := [][]string{
[]string{"_", "_", "_"},
[]string{"_", "_", "_"},
[]string{"_", "_", "_"},
}
// ○×を入れていく…③
board[0][0] = "X"
board[2][2] = "O"
board[1][2] = "X"
board[1][0] = "O"
board[0][2] = "X"
// 全体を描画する…④
for i := 0; i < len(board); i++ {
fmt.Printf("%s\n", strings.Join(board[i], " "))
}
}
X _ X
O _ X
_ _ O
はじめに
今回は、A Tour of Goを勉強していて、本家の解説が少なくて分かりにくかった部分を共有したいと思います。
解説
サンプルコード内に①〜④の目印をつけています。
1つずつ調べたことを共有します。
1.stringsパッケージ
まず、①のパッケージについて。
これは、大変分かりやすくまとめてくれているサイトがあったので、解説はこちらに任せますが、要はGo言語で文字列操作を、シンプルに行える関数を集めたパッケージのようです。
かなりよく使いそうなものがたくさん並んでいるので、僕と同じ入門者の方はサラッと見ておくと良いかと思います!
2.ネストされたスライスの定義
board := [][]string{
[]string{"_", "_", "_"},
[]string{"_", "_", "_"},
[]string{"_", "_", "_"},
}
この部分です。ちょっと個人的には、ワケわからなくなったので、整理しておきます。
通常の(ネストしていない)スライスを定義するには、
ネストしていなければ…
x := []string{"a", "b", "c"}
fmt.Println(x) // => [a b c]
スライスなので…
-
[]
の中には何も入れず、 - 格納するデータがstring型なので、それを記述し、
- 実際に、
{"a", "b", "c"}
このようにデータを格納している。
という感じです。
これをネストさせると…
y := [][]string{
[]string{"a1", "a2", "a3"},
[]string{"b1", "b2", "b3"},
[]string{"c1", "c2", "c3"}
}
fmt.Println(y) // => [[a1 a2 a3] [b1 b2 b3] [c1 c2 c3]]
先ほどの
{"a", "b", "c"}
↑この部分が、
{
[]string{"a1", "a2", "a3"},
[]string{"b1", "b2", "b3"},
[]string{"c1", "c2", "c3"}
}
↑こうなります。
ネスト(入れ子構造に)しているだけなので、落ち着いてよく見れば分かるかと思います。
大丈夫!…怖くないから…!!(笑)
ポイントは[][]string{}
←ここ
[][]
ここで、ネストのスライスだと定義しているのかと思いきや、内側でもしっかり[]
で定義。
ここは、もう決まり文句として捉えようと思います(笑)
あと、個人的に意外だった部分ですが、このネスト構造の一番外側の定義部分…
stringなんですね…
個人的には、中に入っているデータはスライスなので、ここは[][]slice{}
(←適当)みたいなことになるのかなと考えていたのですが、違いました。
というか、そんな型ないですよね…(笑)
ネストされるスライスの型は一律(?)
そこで予想されるのが、
[["あ", "い", "う"], [1, 2, 3], [true, false, false]]
↑こんな感じの、バラバラの型はネストして格納できなさそう…ということ。
つまり、ネスト格納するデータは、同じ種類の型のもの入れられないのではないかと、予想されます。
(違っていたらすみません。その場合はコメントでご指摘もらえると嬉しいです。)
実際、強引にやってみたら「intは無理っす」的なエラーがでました。
board := [][]string{
[]string{"_", "_", "_"},
[]string{"_", "_", "_"},
[]string{"_", "_", "_"},
[]int{1,2,3},
}
cannot use []int literal (type []int) as type []string in array or slice literal
3.1行ずつ描画させて、ネストしたスライスのどこに格納されていくかを確認してみた
board[0][0] = "X"
board[2][2] = "O"
board[1][2] = "X"
board[1][0] = "O"
board[0][2] = "X"
ここの部分です。
1行ずつ結果を出力して描画させることで、どの階層の、どこに値を代入されせているかを、確認しながら見ると理解が深まりましたので、共有しておきます。
_ _ _
_ _ _
_ _ _
board[0][0] = "X"
X _ _
_ _ _
_ _ _
board[0][0] = "X"
board[2][2] = "O"
X _ _
_ _ _
_ _ O
board[0][0] = "X"
board[2][2] = "O"
board[1][2] = "X"
X _ _
_ _ X
_ _ O
board[0][0] = "X"
board[2][2] = "O"
board[1][2] = "X"
board[1][0] = "O"
X _ _
O _ X
_ _ O
board[0][0] = "X"
board[2][2] = "O"
board[1][2] = "X"
board[1][0] = "O"
board[0][2] = "X"
X _ X
O _ X
_ _ O
このような感じになります。
分かりやすくなりました。
4.全体strings.Join関数
for i := 0; i < len(board); i++ {
fmt.Printf("%s\n", strings.Join(board[i], " "))
}
最後に、ここの関数についてざっくり解説です。
前提として、今回のスライスは1階層目に3つのスライスが入っています。
それらの描画方法として、ここの部分では
fmt.Printf("%s\n", strings.Join(board[i], " "))
この1行だけで、
- 1つずつのスライスを結合して、
- 間に
(空文字)を入れて、
- string型として文字列にしています。
strings.Join関数…便利な関数です。
さいごに
最後まで読んでいただき、ありがとうございました。
まだまだ入門者なので、間違いもあるかと思います。
ご指摘頂ければ、幸いです。