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

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

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

本日は、Go言語の言語仕様について解説しています。

スポンサーリンク

背景

Go言語を学び始めて、公式の「The Go Programming Language Specification(言語仕様書)」を開いてみたものの、「英語で書かれていて読むのが大変…」「専門用語ばかりで何を言っているのかわからない…」と感じたことはありませんか? 実は、多くのGo初心者が同じ壁にぶつかっています。

言語仕様書は、Go言語の「正式な取扱説明書」のような存在です。プログラミング言語がどのように動くのか、どんなルールで書くべきなのかが詳しく書かれていますが、その分、初めて読む人には難しく感じられるのも事実です。

そこでこの記事では、言語仕様書の導入部分を丁寧な日本語訳とともに、初心者の方でも理解しやすい補足説明を加えてお届けします。「強く型付けされている」「ガベージコレクション」「並行プログラミング」といった専門用語も、具体例を交えながらわかりやすく解説していきます。

言語仕様書は難しそうに見えますが、一つひとつの概念を丁寧に読み解いていけば、必ず理解できます。一緒に、Go言語の基礎をしっかり学んでいきましょう!

複素数の操作(Manipulating complex numbers)

3つの関数が複素数を組み立ておよび分解する。組み込み関数 complex は浮動小数点の実部と虚部から複素数値を構築し、realimag は複素数値の実部と虚部を抽出する。

complex(realPart, imaginaryPart floatT) complexT
real(complexT) floatT
imag(complexT) floatT

引数と戻り値の型は対応する。complex では、2つの引数は同じ浮動小数点型でなければならず、戻り値の型は対応する浮動小数点要素を持つ複素数型である。float32 引数に対しては complex64float64 引数に対しては complex128 となる。一方の引数が型なし定数と評価される場合、まず暗黙的に他方の引数の型に変換される。両方の引数が型なし定数と評価される場合、それらは非複素数であるか、虚部がゼロでなければならず、関数の戻り値は型なし複素数定数である。

realimag では、引数は複素数型でなければならず、戻り値の型は対応する浮動小数点型である。complex64 引数に対しては float32complex128 引数に対しては float64 となる。引数が型なし定数と評価される場合、数値でなければならず、関数の戻り値は型なし浮動小数点定数である。

realimag 関数は合わせて 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 には複素数型(complex64complex128)があり、3つの組み込み関数で操作します。

// 複素数を作る
c := complex(3.0, 4.0)   // 3 + 4i

// 実部を取り出す
r := real(c)   // 3.0

// 虚部を取り出す
i := imag(c)   // 4.0

たとえるなら、complex は「箱に2つの値を詰める」操作、realimag は「箱から1つずつ取り出す」操作です。

型の対応関係

浮動小数点型と複素数型は1対1で対応しています。

浮動小数点型複素数型
float32complex64(実部32ビット + 虚部32ビット)
float64complex128(実部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 関数を使わなくても、こちらの方が簡潔ですね。

分解と再構築は元に戻る

realimag で分解してから 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
}

型パラメータは使えない

complexrealimag には型パラメータ型の引数を渡せません。

func f[T ~float32|~float64](x, y T) {
    c := complex(x, y)  // コンパイルエラー!
}

ジェネリクスで複素数を扱いたい場合は、型制約で具体的な複素数型を使う必要があります。

おわりに 

本日は、Go言語の言語仕様について解説しました。

よっしー
よっしー

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

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

コメント

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