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

スポンサーリンク
Go言語入門:言語仕様 -Vol.11- ノウハウ
Go言語入門:言語仕様 -Vol.11-
この記事は約23分で読めます。
よっしー
よっしー

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

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

スポンサーリンク

背景

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

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

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

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

Operators and punctuation(演算子と句読点)

以下の文字列は、演算子(代入演算子を含む)と句読点を表します[Go 1.18]:

+    &     +=    &=     &&    ==    !=    (    )
-    |     -=    |=     ||    <     <=    [    ]
*    ^     *=    ^=     <-    >     >=    {    }
/    <<    /=    <<=    ++    =     :=    ,    ;
%    >>    %=    >>=    --    !     ...   .    :
     &^          &^=          ~

解説

演算子と句読点とは何か?

演算子は、値に対して操作を行う記号です。句読点は、コードの構造を示す記号です。これらはGoプログラムを書く上で欠かせない基本要素です。

たとえ話: 数学の式「3 + 5 = 8」で、「+」が演算子、「=」も演算子です。同じように、プログラミングでも演算子を使って計算や操作を行います。


1. 算術演算子(Arithmetic operators)

数値の計算に使う演算子です。

+ (加算)

package main

import "fmt"

func main() {
    // 数値の加算
    result := 10 + 5
    fmt.Println(result)  // 15
    
    // 文字列の連結
    message := "Hello" + " " + "World"
    fmt.Println(message)  // Hello World
    
    // 浮動小数点
    pi := 3.14 + 0.00159
    fmt.Println(pi)  // 3.14159
}

- (減算・符号反転)

package main

import "fmt"

func main() {
    // 減算
    result := 10 - 5
    fmt.Println(result)  // 5
    
    // 負の数(単項演算子)
    negative := -42
    fmt.Println(negative)  // -42
    
    // 正の数に変換
    positive := -(-10)
    fmt.Println(positive)  // 10
}

* (乗算)

package main

import "fmt"

func main() {
    // 乗算
    result := 10 * 5
    fmt.Println(result)  // 50
    
    // ポインタの宣言(別の意味でも使う)
    var x int = 42
    var ptr *int = &x  // *intはintのポインタ型
    fmt.Println(*ptr)  // 42 (ポインタの参照外し)
}

/ (除算)

package main

import "fmt"

func main() {
    // 整数同士の除算は整数部分のみ
    result1 := 10 / 3
    fmt.Println(result1)  // 3 (小数部分は切り捨て)
    
    // 浮動小数点の除算
    result2 := 10.0 / 3.0
    fmt.Println(result2)  // 3.3333...
    
    // 型変換して浮動小数点除算
    a := 10
    b := 3
    result3 := float64(a) / float64(b)
    fmt.Println(result3)  // 3.3333...
}

% (剰余・モジュロ)

package main

import "fmt"

func main() {
    // 剰余(割り算の余り)
    result := 10 % 3
    fmt.Println(result)  // 1
    
    // 偶数・奇数の判定
    num := 7
    if num%2 == 0 {
        fmt.Println("偶数")
    } else {
        fmt.Println("奇数")  // こちらが出力される
    }
    
    // 負の数の剰余
    fmt.Println(-10 % 3)   // -1
    fmt.Println(10 % -3)   // 1
    fmt.Println(-10 % -3)  // -1
}

2. 比較演算子(Comparison operators)

値を比較して真偽値(true/false)を返します。

== (等しい)

package main

import "fmt"

func main() {
    // 数値の比較
    fmt.Println(10 == 10)  // true
    fmt.Println(10 == 5)   // false
    
    // 文字列の比較
    fmt.Println("Hello" == "Hello")  // true
    fmt.Println("Hello" == "World")  // false
    
    // 真偽値の比較
    fmt.Println(true == true)   // true
    fmt.Println(true == false)  // false
}

!= (等しくない)

package main

import "fmt"

func main() {
    fmt.Println(10 != 5)        // true
    fmt.Println(10 != 10)       // false
    fmt.Println("A" != "B")     // true
    
    // エラーチェックでよく使う
    value, err := someFunction()
    if err != nil {
        fmt.Println("エラーが発生しました")
    }
}

< (より小さい)

package main

import "fmt"

func main() {
    fmt.Println(5 < 10)   // true
    fmt.Println(10 < 10)  // false
    fmt.Println(15 < 10)  // false
    
    // 年齢チェック
    age := 17
    if age < 18 {
        fmt.Println("未成年です")
    }
}

<= (以下)

package main

import "fmt"

func main() {
    fmt.Println(5 <= 10)   // true
    fmt.Println(10 <= 10)  // true (等しい場合もtrue)
    fmt.Println(15 <= 10)  // false
}

> (より大きい)

package main

import "fmt"

func main() {
    fmt.Println(15 > 10)  // true
    fmt.Println(10 > 10)  // false
    fmt.Println(5 > 10)   // false
}

>= (以上)

package main

import "fmt"

func main() {
    fmt.Println(15 >= 10)  // true
    fmt.Println(10 >= 10)  // true
    fmt.Println(5 >= 10)   // false
    
    // 成人チェック
    age := 20
    if age >= 18 {
        fmt.Println("成人です")
    }
}

3. 論理演算子(Logical operators)

真偽値を組み合わせる演算子です。

&& (論理積・AND)

両方がtrueの場合のみtrue

package main

import "fmt"

func main() {
    // 両方trueならtrue
    fmt.Println(true && true)    // true
    fmt.Println(true && false)   // false
    fmt.Println(false && true)   // false
    fmt.Println(false && false)  // false
    
    // 実用例: 範囲チェック
    age := 25
    if age >= 18 && age < 65 {
        fmt.Println("成人労働者年齢")
    }
    
    // 短絡評価(左がfalseなら右は評価されない)
    x := 0
    if x != 0 && 10/x > 2 {  // x!=0がfalseなので10/xは実行されない
        fmt.Println("OK")
    }
}

|| (論理和・OR)

どちらか一方でもtrueならtrue

package main

import "fmt"

func main() {
    // どちらかtrueならtrue
    fmt.Println(true || true)    // true
    fmt.Println(true || false)   // true
    fmt.Println(false || true)   // true
    fmt.Println(false || false)  // false
    
    // 実用例: 休日チェック
    day := "土曜日"
    if day == "土曜日" || day == "日曜日" {
        fmt.Println("週末です")
    }
    
    // 短絡評価(左がtrueなら右は評価されない)
    x := 10
    if x == 10 || expensive() {  // x==10がtrueなのでexpensive()は呼ばれない
        fmt.Println("OK")
    }
}

func expensive() bool {
    fmt.Println("重い処理")
    return true
}

! (論理否定・NOT)

真偽値を反転します。

package main

import "fmt"

func main() {
    fmt.Println(!true)   // false
    fmt.Println(!false)  // true
    
    // 実用例
    isLoggedIn := false
    if !isLoggedIn {
        fmt.Println("ログインしてください")
    }
    
    // 二重否定
    value := true
    fmt.Println(!!value)  // true
}

4. ビット演算子(Bitwise operators)

ビット単位で操作する演算子です。

& (ビット積・AND)

package main

import "fmt"

func main() {
    // ビット単位のAND
    //   1010 (10)
    // & 1100 (12)
    // ------
    //   1000 (8)
    result := 10 & 12
    fmt.Println(result)  // 8
    
    // フラグチェック
    const (
        FlagRead    = 1 << 0  // 0001
        FlagWrite   = 1 << 1  // 0010
        FlagExecute = 1 << 2  // 0100
    )
    
    permissions := FlagRead | FlagWrite  // 0011
    if permissions&FlagWrite != 0 {
        fmt.Println("書き込み権限あり")
    }
    
    // アドレス演算子(別の意味)
    x := 42
    ptr := &x  // xのアドレスを取得
    fmt.Printf("アドレス: %p\n", ptr)
}

| (ビット和・OR)

package main

import "fmt"

func main() {
    // ビット単位のOR
    //   1010 (10)
    // | 1100 (12)
    // ------
    //   1110 (14)
    result := 10 | 12
    fmt.Println(result)  // 14
    
    // フラグの組み合わせ
    const (
        FlagRead  = 1  // 001
        FlagWrite = 2  // 010
        FlagExec  = 4  // 100
    )
    
    permissions := FlagRead | FlagWrite  // 011 (読み書き可能)
    fmt.Println(permissions)  // 3
}

^ (ビット排他的論理和・XOR)

package main

import "fmt"

func main() {
    // ビット単位のXOR
    //   1010 (10)
    // ^ 1100 (12)
    // ------
    //   0110 (6)
    result := 10 ^ 12
    fmt.Println(result)  // 6
    
    // ビット反転(単項演算子として)
    x := 10  // 0000...1010
    y := ^x  // 1111...0101 (全ビット反転)
    fmt.Println(y)  // -11
    
    // XORの特性: 同じ値で2回XORすると元に戻る
    original := 42
    key := 123
    encrypted := original ^ key
    decrypted := encrypted ^ key
    fmt.Println(decrypted)  // 42 (元に戻る)
}

&^ (ビットクリア・AND NOT)

Go特有の演算子です。

package main

import "fmt"

func main() {
    // ビットクリア
    //   1010 (10)
    // &^1100 (12)
    // ------
    //   0010 (2)
    // 右側が1のビットを左側から0にする
    result := 10 &^ 12
    fmt.Println(result)  // 2
    
    // フラグの削除
    const (
        FlagRead  = 1  // 001
        FlagWrite = 2  // 010
        FlagExec  = 4  // 100
    )
    
    permissions := FlagRead | FlagWrite | FlagExec  // 111
    permissions = permissions &^ FlagWrite          // 101 (書き込み権限を削除)
    fmt.Println(permissions)  // 5
}

<< (左シフト)

package main

import "fmt"

func main() {
    // 左シフト(ビットを左に移動、2のn乗を掛ける)
    result := 5 << 2
    // 0101 → 10100
    // 5 * 2^2 = 20
    fmt.Println(result)  // 20
    
    // 2のべき乗を簡単に作る
    fmt.Println(1 << 0)   // 1
    fmt.Println(1 << 1)   // 2
    fmt.Println(1 << 2)   // 4
    fmt.Println(1 << 3)   // 8
    fmt.Println(1 << 10)  // 1024
}

>> (右シフト)

package main

import "fmt"

func main() {
    // 右シフト(ビットを右に移動、2のn乗で割る)
    result := 20 >> 2
    // 10100 → 00101
    // 20 / 2^2 = 5
    fmt.Println(result)  // 5
    
    // 整数の2での除算
    fmt.Println(100 >> 1)  // 50 (100 / 2)
    fmt.Println(100 >> 2)  // 25 (100 / 4)
    fmt.Println(100 >> 3)  // 12 (100 / 8)
}

5. 代入演算子(Assignment operators)

= (代入)

package main

func main() {
    // 基本的な代入
    var x int = 10
    
    // 複数同時代入
    a, b := 1, 2
    
    // 構造体の代入
    type Point struct {
        X, Y int
    }
    p := Point{X: 10, Y: 20}
}

:= (短縮変数宣言)

package main

func main() {
    // varを省略した宣言と代入
    name := "太郎"  // var name string = "太郎" と同じ
    age := 25      // var age int = 25 と同じ
    
    // 複数変数
    x, y := 10, 20
    
    // 既存の変数と組み合わせ(少なくとも1つは新しい変数が必要)
    value, err := someFunction()
    value, err2 := anotherFunction()  // valueは既存、err2は新規
}

複合代入演算子

package main

import "fmt"

func main() {
    x := 10
    
    // 加算代入
    x += 5   // x = x + 5
    fmt.Println(x)  // 15
    
    // 減算代入
    x -= 3   // x = x - 3
    fmt.Println(x)  // 12
    
    // 乗算代入
    x *= 2   // x = x * 2
    fmt.Println(x)  // 24
    
    // 除算代入
    x /= 4   // x = x / 4
    fmt.Println(x)  // 6
    
    // 剰余代入
    x %= 4   // x = x % 4
    fmt.Println(x)  // 2
    
    // ビット演算代入
    x &= 3   // x = x & 3
    x |= 5   // x = x | 5
    x ^= 2   // x = x ^ 2
    x <<= 1  // x = x << 1
    x >>= 1  // x = x >> 1
    x &^= 1  // x = x &^ 1
}

6. インクリメント・デクリメント

++ (インクリメント)

package main

import "fmt"

func main() {
    x := 10
    x++  // x = x + 1
    fmt.Println(x)  // 11
    
    // ループでよく使う
    for i := 0; i < 5; i++ {
        fmt.Println(i)
    }
    
    // 注意: Goでは前置インクリメントは使えない
    // ++x  // エラー!
    
    // 注意: 式の一部としては使えない
    // y := x++  // エラー!
    // 正しくは:
    x++
    y := x
}

-- (デクリメント)

package main

import "fmt"

func main() {
    x := 10
    x--  // x = x - 1
    fmt.Println(x)  // 9
    
    // カウントダウン
    count := 5
    for count > 0 {
        fmt.Println(count)
        count--
    }
}

7. チャネル演算子

<- (チャネル送受信)

package main

import "fmt"

func main() {
    // チャネルの作成
    ch := make(chan int)
    
    // ゴルーチンで送信
    go func() {
        ch <- 42  // チャネルに値を送信
    }()
    
    // 受信
    value := <-ch  // チャネルから値を受信
    fmt.Println(value)  // 42
    
    // 受信専用チャネル
    go func(ch <-chan int) {
        val := <-ch
        fmt.Println(val)
    }(ch)
    
    // 送信専用チャネル
    go func(ch chan<- int) {
        ch <- 100
    }(ch)
}

8. 句読点(Punctuation)

() (丸括弧)

package main

func main() {
    // 関数呼び出し
    result := add(10, 20)
    
    // グループ化
    x := (10 + 5) * 2  // 30
    
    // 型変換
    f := float64(42)
    
    // 制御文
    if (x > 10) {
        // ...
    }
}

func add(a, b int) int {
    return a + b
}

[] (角括弧)

package main

func main() {
    // 配列の宣言
    var arr [5]int
    
    // スライスの宣言
    slice := []int{1, 2, 3}
    
    // インデックスアクセス
    value := slice[0]
    
    // スライス操作
    subSlice := slice[1:3]  // [2, 3]
}

{} (波括弧)

package main

func main() {  // ブロックの開始
    // 複合リテラル
    point := struct {
        X, Y int
    }{10, 20}
    
    // マップ
    m := map[string]int{
        "a": 1,
        "b": 2,
    }
    
    // スライス
    s := []int{1, 2, 3}
}  // ブロックの終了

, (カンマ)

package main

func main() {
    // 要素の区切り
    slice := []int{1, 2, 3}
    
    // 複数変数の宣言
    var a, b, c int
    
    // 関数の引数
    result := add(10, 20)
    
    // 複数の戻り値
    value, err := someFunc()
}

; (セミコロン)

package main

func main() {
    // 通常は省略(自動挿入)
    x := 10
    y := 20
    
    // 明示的に書くことも可能(非推奨)
    a := 1; b := 2
    
    // forループでは必須
    for i := 0; i < 10; i++ {
        // ...
    }
}

. (ドット)

package main

import "fmt"

type Person struct {
    Name string
}

func main() {
    // パッケージのメンバーアクセス
    fmt.Println("Hello")
    
    // 構造体のフィールドアクセス
    p := Person{Name: "太郎"}
    name := p.Name
    
    // メソッド呼び出し
    p.SayHello()
}

func (p Person) SayHello() {
    fmt.Println("Hello,", p.Name)
}

: (コロン)

package main

func main() {
    // 短縮変数宣言
    x := 10
    
    // スライスの範囲指定
    slice := []int{1, 2, 3, 4, 5}
    sub := slice[1:3]  // [2, 3]
    
    // 構造体リテラルのフィールド名
    type Point struct {
        X, Y int
    }
    p := Point{X: 10, Y: 20}
    
    // ラベル
Loop:
    for {
        break Loop
    }
}

... (省略記号)

package main

import "fmt"

func main() {
    // 可変長引数
    sum := add(1, 2, 3, 4, 5)
    fmt.Println(sum)
    
    // スライスの展開
    numbers := []int{1, 2, 3}
    sum2 := add(numbers...)
    fmt.Println(sum2)
    
    // 配列リテラルの長さ自動計算
    arr := [...]int{1, 2, 3, 4, 5}  // 長さ5の配列
}

func add(numbers ...int) int {
    total := 0
    for _, n := range numbers {
        total += n
    }
    return total
}

~ (型制約の近似)[Go 1.18]

ジェネリクスで使用されます。

package main

import "fmt"

// 型制約で使用
type Integer interface {
    ~int | ~int8 | ~int16 | ~int32 | ~int64
}

func Sum[T Integer](values []T) T {
    var total T
    for _, v := range values {
        total += v
    }
    return total
}

type MyInt int

func main() {
    // ~int があるのでMyInt型も使える
    numbers := []MyInt{1, 2, 3}
    result := Sum(numbers)
    fmt.Println(result)  // 6
}

まとめ: 演算子と句読点で覚えておくべきこと

演算子の優先順位(高い→低い)

  1. *, /, %, <<, >>, &, &^
  2. +, -, |, ^
  3. ==, !=, <, <=, >, >=
  4. &&
  5. ||
package main

import "fmt"

func main() {
    // 優先順位の例
    result := 10 + 5 * 2  // 20 (5*2が先に計算される)
    
    // 括弧で優先順位を変更
    result2 := (10 + 5) * 2  // 30
    
    fmt.Println(result, result2)
}

実用的なアドバイス

package main

import "fmt"

func main() {
    // よく使う演算子の組み合わせ
    
    // 算術
    x := 10 + 5
    x *= 2
    x++
    
    // 比較
    if x > 20 && x < 50 {
        fmt.Println("範囲内")
    }
    
    // ビット演算(フラグ管理)
    const (
        FlagA = 1 << 0
        FlagB = 1 << 1
        FlagC = 1 << 2
    )
    flags := FlagA | FlagB
    if flags&FlagA != 0 {
        fmt.Println("FlagAが立っている")
    }
    
    // チャネル
    ch := make(chan int)
    go func() {
        ch <- 42
    }()
    value := <-ch
    fmt.Println(value)
}

演算子と句読点は、Goプログラムの基本的な構成要素です。これらを組み合わせることで、複雑なロジックを表現できます!

おわりに 

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

よっしー
よっしー

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

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

コメント

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