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

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

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

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

スポンサーリンク

背景

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

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

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

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

Imaginary literals(虚数リテラル)

虚数リテラルは、複素数定数の虚数部を表します。整数または浮動小数点リテラルの後に小文字のiが続きます。虚数リテラルの値は、対応する整数または浮動小数点リテラルの値に虚数単位i(√-1)を掛けたものです[Go 1.13]。

imaginary_lit = (decimal_digits | int_lit | float_lit) "i" .

後方互換性のため、10進数の数字(および場合によってはアンダースコア)のみで構成される虚数リテラルの整数部分は、先頭に0が付いていても、10進整数と見なされます。

0i
0123i         // 後方互換性のため == 123i
0o123i        // == 0o123 * 1i == 83i
0xabci        // == 0xabc * 1i == 2748i
0.i
2.71828i
1.e+0i
6.67428e-11i
1E6i
.25i
.12345E+5i
0x1p-2i       // == 0x1p-2 * 1i == 0.25i

解説

虚数とは何か?

虚数は、数学で「√-1」を表す特別な数です。通常の実数では「-1の平方根」は存在しませんが、虚数を使うことで表現できます。虚数単位をiと書き、i² = -1という性質を持ちます。

たとえ話: 普通の数直線(1次元)では表せない「もう一つの次元」の数です。実数が東西方向だとすると、虚数は南北方向のようなイメージです。

複素数とは?

複素数は、実数部と虚数部を組み合わせた数です。

複素数 = 実数部 + 虚数部
例: 3 + 4i
   ↑   ↑
  実数部 虚数部

Goでは、complex64(32ビット)とcomplex128(64ビット)という型で複素数を扱えます。


1. 虚数リテラルの基本

虚数リテラルは、数値の後ろに小文字のiを付けるだけです。

整数ベースの虚数

package main

import "fmt"

func main() {
    // 整数 + i
    var img1 complex128 = 5i      // 虚数部が5
    var img2 complex128 = 42i     // 虚数部が42
    var img3 complex128 = 0i      // 虚数部が0
    
    fmt.Println(img1)  // (0+5i)
    fmt.Println(img2)  // (0+42i)
    fmt.Println(img3)  // (0+0i)
    
    // 型を確認
    fmt.Printf("型: %T\n", img1)  // complex128
}

浮動小数点ベースの虚数

package main

import "fmt"

func main() {
    // 浮動小数点 + i
    var img1 complex128 = 3.14i        // 円周率の虚数版
    var img2 complex128 = 2.71828i     // 自然対数の底の虚数版
    var img3 complex128 = 0.5i         // 0.5の虚数版
    var img4 complex128 = .25i         // 小数点前の0を省略
    
    fmt.Println(img1)  // (0+3.14i)
    fmt.Println(img2)  // (0+2.71828i)
    fmt.Println(img3)  // (0+0.5i)
    fmt.Println(img4)  // (0+0.25i)
}

2. 複素数の作成

実数部と虚数部を組み合わせる

package main

import "fmt"

func main() {
    // 実数部 + 虚数部
    var c1 complex128 = 3 + 4i       // 3 + 4i
    var c2 complex128 = 5.5 + 2.3i   // 5.5 + 2.3i
    var c3 complex128 = 10 + 0i      // 10 + 0i (実数と同じ)
    var c4 complex128 = 0 + 7i       // 0 + 7i (純虚数)
    
    fmt.Println(c1)  // (3+4i)
    fmt.Println(c2)  // (5.5+2.3i)
    fmt.Println(c3)  // (10+0i)
    fmt.Println(c4)  // (0+7i)
}

complex関数を使う方法

package main

import "fmt"

func main() {
    // complex(実数部, 虚数部) で作成
    c1 := complex(3, 4)       // 3 + 4i
    c2 := complex(5.5, 2.3)   // 5.5 + 2.3i
    
    fmt.Println(c1)  // (3+4i)
    fmt.Println(c2)  // (5.5+2.3i)
    
    // 実数部と虚数部を取り出す
    real_part := real(c1)  // 3
    imag_part := imag(c1)  // 4
    
    fmt.Printf("実数部: %v, 虚数部: %v\n", real_part, imag_part)
}

3. 様々な表記法

10進数の虚数

package main

import "fmt"

func main() {
    // 基本的な10進数
    var a complex128 = 0i
    var b complex128 = 123i
    var c complex128 = 1_000_000i  // アンダースコアも使える
    
    fmt.Println(a, b, c)
}

8進数・16進数の虚数

package main

import "fmt"

func main() {
    // 8進数ベース
    var oct1 complex128 = 0o123i    // 8進数の123 = 10進数の83
    fmt.Println(oct1)               // (0+83i)
    
    // 16進数ベース
    var hex1 complex128 = 0xabci    // 16進数のabc = 10進数の2748
    fmt.Println(hex1)               // (0+2748i)
    
    // 色の虚数版(あまり実用的ではないが可能)
    var colorImg complex128 = 0xFFi
    fmt.Println(colorImg)           // (0+255i)
}

後方互換性: 0で始まる10進数

package main

import "fmt"

func main() {
    // 注意: 0で始まる数字は通常8進数だが、
    // 虚数リテラルの場合は後方互換性のため10進数として扱われる
    
    var old complex128 = 0123i  // 123i (10進数の123)
    fmt.Println(old)            // (0+123i) ← 8進数の83ではない!
    
    // 明示的に8進数にするには 0o を使う
    var oct complex128 = 0o123i
    fmt.Println(oct)            // (0+83i)
}

4. 浮動小数点の虚数

基本的な小数

package main

import "fmt"

func main() {
    // 小数の虚数
    var f1 complex128 = 0.i        // 0.0i
    var f2 complex128 = 3.14i      // 3.14i
    var f3 complex128 = .5i        // 0.5i (小数点前の0を省略)
    var f4 complex128 = 2.i        // 2.0i
    
    fmt.Println(f1, f2, f3, f4)
}

指数表記の虚数

package main

import "fmt"

func main() {
    // 指数表記(科学的記数法)
    var e1 complex128 = 1e6i        // 1 × 10^6 = 1,000,000i
    var e2 complex128 = 1.e+0i      // 1 × 10^0 = 1i
    var e3 complex128 = 6.67428e-11i // 重力定数の虚数版
    var e4 complex128 = .12345E+5i   // 0.12345 × 10^5 = 12345i
    
    fmt.Println(e1)  // (0+1e+06i)
    fmt.Println(e2)  // (0+1i)
    fmt.Println(e3)  // (0+6.67428e-11i)
    fmt.Println(e4)  // (0+12345i)
}

16進数浮動小数点の虚数

package main

import "fmt"

func main() {
    // 16進数浮動小数点(0x1p-2 = 1 × 2^-2 = 0.25)
    var hexFloat complex128 = 0x1p-2i
    fmt.Println(hexFloat)  // (0+0.25i)
    
    // 他の例
    var hf2 complex128 = 0x1p0i   // 1 × 2^0 = 1i
    var hf3 complex128 = 0x1p2i   // 1 × 2^2 = 4i
    var hf4 complex128 = 0x2p-1i  // 2 × 2^-1 = 1i
    
    fmt.Println(hf2, hf3, hf4)
}

5. 複素数の演算

基本的な四則演算

package main

import "fmt"

func main() {
    c1 := 3 + 4i
    c2 := 1 + 2i
    
    // 加算
    sum := c1 + c2
    fmt.Println("加算:", sum)  // (4+6i)
    
    // 減算
    diff := c1 - c2
    fmt.Println("減算:", diff)  // (2+2i)
    
    // 乗算
    prod := c1 * c2
    // (3+4i)(1+2i) = 3 + 6i + 4i + 8i²
    //              = 3 + 10i - 8  (i² = -1)
    //              = -5 + 10i
    fmt.Println("乗算:", prod)  // (-5+10i)
    
    // 除算
    quot := c1 / c2
    fmt.Println("除算:", quot)  // (2.2-0.4i)
}

複素数の絶対値(大きさ)

package main

import (
    "fmt"
    "math/cmplx"
)

func main() {
    c := 3 + 4i
    
    // 絶対値(原点からの距離)
    // |3+4i| = √(3² + 4²) = √25 = 5
    abs := cmplx.Abs(c)
    fmt.Println("絶対値:", abs)  // 5
    
    // 位相(角度)
    phase := cmplx.Phase(c)
    fmt.Printf("位相: %.4f ラジアン\n", phase)
}

共役複素数

package main

import (
    "fmt"
    "math/cmplx"
)

func main() {
    c := 3 + 4i
    
    // 共役複素数(虚数部の符号を反転)
    conj := cmplx.Conj(c)
    fmt.Println("共役:", conj)  // (3-4i)
    
    // 複素数とその共役の積は実数になる
    prod := c * conj
    fmt.Println("積:", prod)  // (25+0i) = 25
}

6. 実用例

例1: 電気回路(インピーダンス)

package main

import (
    "fmt"
    "math/cmplx"
)

func main() {
    // 交流回路のインピーダンス計算
    // Z = R + jωL (抵抗 + 誘導リアクタンス)
    
    R := 100.0      // 抵抗 (Ω)
    L := 0.1        // インダクタンス (H)
    omega := 314.0  // 角周波数 (rad/s) = 2π × 50Hz
    
    Z := complex(R, omega*L)  // インピーダンス
    fmt.Printf("インピーダンス: %.2f\n", Z)
    
    // 絶対値(インピーダンスの大きさ)
    magZ := cmplx.Abs(Z)
    fmt.Printf("インピーダンスの大きさ: %.2f Ω\n", magZ)
}

例2: 信号処理(フーリエ変換)

package main

import (
    "fmt"
    "math"
    "math/cmplx"
)

func main() {
    // 離散フーリエ変換(DFT)の基礎
    // W_N^k = e^(-2πi k/N)
    
    N := 8  // サンプル数
    k := 2  // 周波数インデックス
    
    // オイラーの公式: e^(iθ) = cos(θ) + i*sin(θ)
    theta := -2 * math.Pi * float64(k) / float64(N)
    
    w := complex(math.Cos(theta), math.Sin(theta))
    fmt.Printf("W_%d^%d = %.4f\n", N, k, w)
    
    // 絶対値は常に1
    abs := cmplx.Abs(w)
    fmt.Printf("絶対値: %.4f\n", abs)
}

例3: 2次方程式の解(虚数解)

package main

import (
    "fmt"
    "math"
    "math/cmplx"
)

// 2次方程式 ax² + bx + c = 0 の解を求める
func solveQuadratic(a, b, c float64) (complex128, complex128) {
    // 判別式
    discriminant := b*b - 4*a*c
    
    if discriminant >= 0 {
        // 実数解
        sqrt_d := math.Sqrt(discriminant)
        x1 := complex((-b+sqrt_d)/(2*a), 0)
        x2 := complex((-b-sqrt_d)/(2*a), 0)
        return x1, x2
    } else {
        // 虚数解
        sqrt_d := cmplx.Sqrt(complex(discriminant, 0))
        x1 := (-complex(b, 0) + sqrt_d) / complex(2*a, 0)
        x2 := (-complex(b, 0) - sqrt_d) / complex(2*a, 0)
        return x1, x2
    }
}

func main() {
    // x² + 2x + 5 = 0
    x1, x2 := solveQuadratic(1, 2, 5)
    fmt.Printf("解1: %v\n", x1)  // (-1+2i)
    fmt.Printf("解2: %v\n", x2)  // (-1-2i)
}

例4: マンデルブロ集合

package main

import (
    "fmt"
    "math/cmplx"
)

// マンデルブロ集合の判定
func mandelbrot(c complex128, maxIter int) int {
    z := complex(0, 0)
    
    for n := 0; n < maxIter; n++ {
        z = z*z + c
        
        if cmplx.Abs(z) > 2 {
            return n  // 発散
        }
    }
    
    return maxIter  // 収束(集合に含まれる)
}

func main() {
    // いくつかの点でテスト
    points := []complex128{
        0 + 0i,
        0.5 + 0i,
        -1 + 0i,
        0 + 1i,
        -0.5 + 0.5i,
    }
    
    maxIter := 100
    
    for _, c := range points {
        iter := mandelbrot(c, maxIter)
        if iter == maxIter {
            fmt.Printf("%v: 集合に含まれる\n", c)
        } else {
            fmt.Printf("%v: %d回で発散\n", c, iter)
        }
    }
}

7. 複素数型の種類

Goには2種類の複素数型があります。

package main

import "fmt"

func main() {
    // complex64: float32の実数部 + float32の虚数部
    var c64 complex64 = 3 + 4i
    fmt.Printf("complex64: %v (型: %T)\n", c64, c64)
    
    // complex128: float64の実数部 + float64の虚数部(デフォルト)
    var c128 complex128 = 3 + 4i
    fmt.Printf("complex128: %v (型: %T)\n", c128, c128)
    
    // 型推論はcomplex128になる
    auto := 3 + 4i
    fmt.Printf("型推論: %v (型: %T)\n", auto, auto)
}

まとめ: 虚数リテラルで覚えておくべきこと

虚数リテラルの作り方

  1. 整数 + i: 5i, 42i, 1_000i
  2. 浮動小数点 + i: 3.14i, .5i, 2.71828i
  3. 指数表記 + i: 1e6i, 6.67e-11i
  4. 16進数 + i: 0xFFi, 0x1p-2i
  5. 8進数 + i: 0o123i

複素数の作り方

// 方法1: リテラルで直接
c1 := 3 + 4i

// 方法2: complex関数
c2 := complex(3, 4)

// 方法3: 実数部と虚数部を別々に
real := 3.0
imag := 4.0
c3 := complex(real, imag)

よく使う関数

import "math/cmplx"

c := 3 + 4i

real(c)        // 実数部を取得: 3
imag(c)        // 虚数部を取得: 4
cmplx.Abs(c)   // 絶対値: 5
cmplx.Phase(c) // 位相(角度)
cmplx.Conj(c)  // 共役複素数: 3-4i
cmplx.Sqrt(c)  // 平方根

実用的なアドバイス

package main

import (
    "fmt"
    "math/cmplx"
)

func main() {
    // 複素数は、信号処理、制御工学、量子力学などで使われる
    
    // 基本的な使い方
    z := 3 + 4i
    
    // 演算
    fmt.Println("加算:", z+1)
    fmt.Println("乗算:", z*2)
    fmt.Println("絶対値:", cmplx.Abs(z))
    
    // 実部と虚部
    fmt.Printf("実部: %.2f, 虚部: %.2f\n", real(z), imag(z))
}

虚数リテラルは、科学技術計算や信号処理で威力を発揮します。日常的なプログラミングではあまり使いませんが、特定の分野では必須の機能です!

おわりに 

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

よっしー
よっしー

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

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

コメント

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