
こんにちは。よっしーです(^^)
本日は、Go言語の言語仕様について解説しています。
背景
Go言語を学び始めて、公式の「The Go Programming Language Specification(言語仕様書)」を開いてみたものの、「英語で書かれていて読むのが大変…」「専門用語ばかりで何を言っているのかわからない…」と感じたことはありませんか? 実は、多くのGo初心者が同じ壁にぶつかっています。
言語仕様書は、Go言語の「正式な取扱説明書」のような存在です。プログラミング言語がどのように動くのか、どんなルールで書くべきなのかが詳しく書かれていますが、その分、初めて読む人には難しく感じられるのも事実です。
そこでこの記事では、言語仕様書の導入部分を丁寧な日本語訳とともに、初心者の方でも理解しやすい補足説明を加えてお届けします。「強く型付けされている」「ガベージコレクション」「並行プログラミング」といった専門用語も、具体例を交えながらわかりやすく解説していきます。
言語仕様書は難しそうに見えますが、一つひとつの概念を丁寧に読み解いていけば、必ず理解できます。一緒に、Go言語の基礎をしっかり学んでいきましょう!
複素数の操作(Manipulating complex numbers)
3つの関数が複素数を組み立ておよび分解する。組み込み関数 complex は浮動小数点の実部と虚部から複素数値を構築し、real と imag は複素数値の実部と虚部を抽出する。
complex(realPart, imaginaryPart floatT) complexT
real(complexT) floatT
imag(complexT) floatT
引数と戻り値の型は対応する。complex では、2つの引数は同じ浮動小数点型でなければならず、戻り値の型は対応する浮動小数点要素を持つ複素数型である。float32 引数に対しては complex64、float64 引数に対しては complex128 となる。一方の引数が型なし定数と評価される場合、まず暗黙的に他方の引数の型に変換される。両方の引数が型なし定数と評価される場合、それらは非複素数であるか、虚部がゼロでなければならず、関数の戻り値は型なし複素数定数である。
real と imag では、引数は複素数型でなければならず、戻り値の型は対応する浮動小数点型である。complex64 引数に対しては float32、complex128 引数に対しては float64 となる。引数が型なし定数と評価される場合、数値でなければならず、関数の戻り値は型なし浮動小数点定数である。
real と imag 関数は合わせて complex の逆を構成するため、複素数型 Z の値 z に対して z == Z(complex(real(z), imag(z))) が成り立つ。
これらの関数のオペランドがすべて定数の場合、戻り値は定数である。
var a = complex(2, -2) // complex128
const b = complex(1.0, -1.4) // 型なし複素数定数 1 - 1.4i
x := float32(math.Cos(math.Pi/2)) // float32
var c64 = complex(5, -x) // complex64
var s int = complex(1, 0) // 型なし複素数定数 1 + 0i は int に変換可能
_ = complex(1, 2<<s) // 不正: 2 は浮動小数点型を仮定し、シフトできない
var rl = real(c64) // float32
var im = imag(a) // float64
const c = imag(b) // 型なし定数 -1.4
_ = imag(3 << s) // 不正: 3 は複素数型を仮定し、シフトできない
型パラメータ型の引数は許可されない。
解説
複素数を扱う3つの関数
Go には複素数型(complex64 と complex128)があり、3つの組み込み関数で操作します。
// 複素数を作る
c := complex(3.0, 4.0) // 3 + 4i
// 実部を取り出す
r := real(c) // 3.0
// 虚部を取り出す
i := imag(c) // 4.0
たとえるなら、complex は「箱に2つの値を詰める」操作、real と imag は「箱から1つずつ取り出す」操作です。
型の対応関係
浮動小数点型と複素数型は1対1で対応しています。
| 浮動小数点型 | 複素数型 |
|---|---|
float32 | complex64(実部32ビット + 虚部32ビット) |
float64 | complex128(実部64ビット + 虚部64ビット) |
var x float32 = 1.0
var y float32 = 2.0
c := complex(x, y) // complex64 型(float32 の引数 → complex64)
r := real(c) // float32 型(complex64 → float32)
i := imag(c) // float32 型
引数の型を混ぜることはできません。
var x float32 = 1.0
var y float64 = 2.0
c := complex(x, y) // コンパイルエラー! 型が異なる
リテラルでも作れる
実は、複素数リテラルを直接書くこともできます。
c1 := 3 + 4i // complex128 型(デフォルト)
c2 := complex(3, 4) // 同じ意味
fmt.Println(c1 == c2) // true
i をつけた数値リテラルが虚数部分を表します。complex 関数を使わなくても、こちらの方が簡潔ですね。
分解と再構築は元に戻る
real と imag で分解してから complex で再構築すると、元の値に戻ります。
z := complex(3.0, 4.0)
z2 := complex(real(z), imag(z))
fmt.Println(z == z2) // true
型なし定数のルール
型なし定数を使う場合のルールは少しややこしいですが、実用上は直感的に動きます。
// 両方型なし定数 → 型なし複素数定数
const b = complex(1.0, -1.4) // 1 - 1.4i
// 虚部がゼロなら整数に変換可能
var s int = complex(1, 0) // 1 + 0i → 1 → int に変換 OK
// 虚部がゼロでないと整数に変換できない
// var s int = complex(1, 2) // コンパイルエラー!
シフト演算子との相性が悪い
原文の例にある「不正」なケースは、シフト演算子と複素数の関係です。
_ = complex(1, 2<<s) // コンパイルエラー!
complex の第2引数に 2<<s を渡そうとすると、2 が浮動小数点型として推論されます。しかしシフト演算子は整数型にしか使えないので、矛盾が生じてエラーになります。以前のシフト演算子の節で学んだルールと同じですね。
普段使う場面
複素数は一般的なアプリケーション開発ではめったに使いません。主に以下のような場面で登場します。
- 信号処理(フーリエ変換など)
- 科学技術計算
- フラクタルの描画(マンデルブロ集合など)
// マンデルブロ集合の判定(簡略版)
func mandelbrot(c complex128) int {
z := complex(0, 0)
for i := 0; i < 100; i++ {
z = z*z + c
if real(z)*real(z)+imag(z)*imag(z) > 4 {
return i
}
}
return 100
}
型パラメータは使えない
complex、real、imag には型パラメータ型の引数を渡せません。
func f[T ~float32|~float64](x, y T) {
c := complex(x, y) // コンパイルエラー!
}
ジェネリクスで複素数を扱いたい場合は、型制約で具体的な複素数型を使う必要があります。
おわりに
本日は、Go言語の言語仕様について解説しました。

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

コメント