
こんにちは。よっしーです(^^)
本日は、Go言語の言語仕様について解説しています。
背景
Go言語を学び始めて、公式の「The Go Programming Language Specification(言語仕様書)」を開いてみたものの、「英語で書かれていて読むのが大変…」「専門用語ばかりで何を言っているのかわからない…」と感じたことはありませんか? 実は、多くのGo初心者が同じ壁にぶつかっています。
言語仕様書は、Go言語の「正式な取扱説明書」のような存在です。プログラミング言語がどのように動くのか、どんなルールで書くべきなのかが詳しく書かれていますが、その分、初めて読む人には難しく感じられるのも事実です。
そこでこの記事では、言語仕様書の導入部分を丁寧な日本語訳とともに、初心者の方でも理解しやすい補足説明を加えてお届けします。「強く型付けされている」「ガベージコレクション」「並行プログラミング」といった専門用語も、具体例を交えながらわかりやすく解説していきます。
言語仕様書は難しそうに見えますが、一つひとつの概念を丁寧に読み解いていけば、必ず理解できます。一緒に、Go言語の基礎をしっかり学んでいきましょう!
要素型の省略(Elision of element types)
配列、スライス、またはmap型 T の複合リテラル内で、要素またはmapキー自体が複合リテラルである場合、それが T の要素型またはキー型と同一であれば、対応するリテラル型を省略することができる。同様に、要素またはキーが複合リテラルのアドレスである場合、要素型またはキー型が *T であれば &T を省略することができる。
[...]Point{{1.5, -3.5}, {0, 0}} // [...]Point{Point{1.5, -3.5}, Point{0, 0}} と同じ
[][]int{{1, 2, 3}, {4, 5}} // [][]int{[]int{1, 2, 3}, []int{4, 5}} と同じ
[][]Point{{{0, 1}, {1, 2}}} // [][]Point{[]Point{Point{0, 1}, Point{1, 2}}} と同じ
map[string]Point{"orig": {0, 0}} // map[string]Point{"orig": Point{0, 0}} と同じ
map[Point]string{{0, 0}: "orig"} // map[Point]string{Point{0, 0}: "orig"} と同じ
type PPoint *Point
[2]*Point{{1.5, -3.5}, {}} // [2]*Point{&Point{1.5, -3.5}, &Point{}} と同じ
[2]PPoint{{1.5, -3.5}, {}} // [2]PPoint{PPoint(&Point{1.5, -3.5}), PPoint(&Point{})} と同じ
有効な配列、スライス、およびmapリテラルの例:
// 素数のリスト
primes := []int{2, 3, 5, 7, 9, 2147483647}
// ch が母音なら vowels[ch] は true
vowels := [128]bool{'a': true, 'e': true, 'i': true, 'o': true, 'u': true, 'y': true}
// 配列 [10]float32{-1, 0, 0, 0, -0.1, -0.1, 0, 0, 0, -1}
filter := [10]float32{-1, 4: -0.1, -0.1, 9: -1}
// 平均律の周波数(Hz)(A4 = 440Hz)
noteFrequency := map[string]float32{
"C0": 16.35, "D0": 18.35, "E0": 20.60, "F0": 21.83,
"G0": 24.50, "A0": 27.50, "B0": 30.87,
}
解説
型名の省略ってなに?
入れ子になった複合リテラルを書くとき、内側の型名を省略できる、という便利なルールです。
省略なしで書くとこうなります。
points := []Point{Point{1, 2}, Point{3, 4}, Point{5, 6}}
// ^^^^^ ^^^^^ ^^^^^
// 毎回 Point と書くのが面倒...
外側が []Point なので、中の要素が Point 型であることはコンパイラにはわかりきっています。だから省略できます。
points := []Point{{1, 2}, {3, 4}, {5, 6}}
// すっきり!
{1, 2} だけで「これは Point{1, 2} のことだな」と自動的に解釈してくれるわけですね。
入れ子が深くなるほど効果大
この省略は入れ子が深くなるほど威力を発揮します。
// 省略なし:型名だらけで読みにくい
grid := [][]Point{[]Point{Point{0, 1}, Point{1, 2}}}
// 省略あり:すっきり!
grid := [][]Point{{{0, 1}, {1, 2}}}
省略なしのほうは、正直なところ何が何だかわかりにくいですよね。省略ありのほうが、波括弧の構造がそのままデータの構造を表していて読みやすくなります。
mapでも使える
mapの値やキーが複合リテラルの場合にも省略できます。
// 省略なし
m := map[string]Point{"orig": Point{0, 0}}
// 省略あり
m := map[string]Point{"orig": {0, 0}}
キー側が複合リテラルの場合も同様です。
// 省略なし
m := map[Point]string{Point{0, 0}: "orig"}
// 省略あり
m := map[Point]string{{0, 0}: "orig"}
ポインタ型の場合:& も省略できる
要素型がポインタ *Point の場合、通常なら &Point{...} と書く必要がありますが、&Point の部分を省略して {...} だけで書けます。
// 省略なし
ps := [2]*Point{&Point{1.5, -3.5}, &Point{}}
// 省略あり:& も Point も書かなくていい
ps := [2]*Point{{1.5, -3.5}, {}}
コンパイラが「要素型は *Point だから、{1.5, -3.5} は &Point{1.5, -3.5} のことだな」と補ってくれます。
実用的な例の読み方
原文の後半にある実用例をいくつか見てみましょう。
母音テーブル
vowels := [128]bool{'a': true, 'e': true, 'i': true, 'o': true, 'u': true, 'y': true}
配列リテラルのキーに文字(rune)を使っています。'a' は整数値 97 なので、vowels[97] が true になります。指定していないインデックスはすべてゼロ値(false)です。ある文字が母音かどうかを vowels[ch] で一発判定できる、というテクニックですね。
フィルタ配列
filter := [10]float32{-1, 4: -0.1, -0.1, 9: -1}
これは前回学んだインデックスの自動採番を活用しています。展開するとこうなります。
// filter = [10]float32{
// 0: -1, ← キーなし、最初なのでインデックス0
// 1: 0, ← 省略されたのでゼロ値
// 2: 0,
// 3: 0,
// 4: -0.1, ← キーあり
// 5: -0.1, ← キーなし、直前の4+1=5
// 6: 0,
// 7: 0,
// 8: 0,
// 9: -1, ← キーあり
// }
大部分がゼロ値の配列で、特定の位置だけに値を入れたいときに便利な書き方です。
おわりに
本日は、Go言語の言語仕様について解説しました。

何か質問や相談があれば、コメントをお願いします。また、エンジニア案件の相談にも随時対応していますので、お気軽にお問い合わせください。
それでは、また明日お会いしましょう(^^)

コメント