Go言語入門:言語仕様 -Vol.66-

スポンサーリンク
Go言語入門:言語仕様 -Vol.66- 用語解説
Go言語入門:言語仕様 -Vol.66-
この記事は約7分で読めます。
よっしー
よっしー

こんにちは。よっしーです(^^)

本日は、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言語の言語仕様について解説しました。

よっしー
よっしー

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

それでは、また明日お会いしましょう(^^)

コメント

タイトルとURLをコピーしました