制御構造

編集

Go言語では、プログラムのフローを制御するためにいくつかの制御構造が用意されています。これらの構造は、プログラムのロジックを実行する際に重要な役割を果たします。本章では、Go言語における主な制御構造について説明します。

条件分岐

編集

Goでは、条件分岐をif文などで行いますが、C言語とは異なります。C言語にもif、else、switchなどがありますが、スコープルールなどに違いがあります。

基本的なif文

編集
package main

import "fmt"

func main() {
    number := 10
    if number > 5 {
        fmt.Println("numberは5より大きい")
    }
}

このコードでは、numberが5より大きいため、"numberは5より大きい"が出力されます。

else文

編集
package main

import "fmt"

func main() {
    number := 3
    if number > 5 {
        fmt.Println("numberは5より大きい")
    } else {
        fmt.Println("numberは5以下")
    }
}

このコードでは、numberが3なので、"numberは5以下"が出力されます。

else if文

編集
package main

import "fmt"

func main() {
    number := 7
    if number > 10 {
        fmt.Println("numberは10より大きい")
    } else if number > 5 {
        fmt.Println("numberは5より大きく、10以下")
    } else {
        fmt.Println("numberは5以下")
    }
}

このコードでは、numberが7なので、"numberは5より大きく、10以下"が出力されます。

コード例
package main

import (
	"fmt"
	"math"
)

func main() {
	if num := math.NaN(); num < 0.0 {
		fmt.Println("負")
	} else if num > 0.0 {
		fmt.Println("正")
	} else if num == 0.0 {
		fmt.Println("零")
	} else {
		fmt.Println("NaN")
	}
}
実行結果
NaN

Goでは、if文の条件式の前に「単純な文」を書くことが可能です。上記の例では、変数numがmath.NaN()で初期化されており、そのスコープはif文の中に限定されます。

switch文

編集

switch文を使うと、複雑なif文を簡潔に表現できます。

コード例
package main

import (
	"fmt"
	"math"
)

func main() {
	switch num := math.NaN(); {
	case num < 0.0:
		fmt.Println("負")
	case num > 0.0:
		fmt.Println("正")
	case num == 0.0:
		fmt.Println("零")
	default:
		fmt.Println("NaN")
	}
}
実行結果
NaN

Goのswitch文では、if文と同様に「単純な文」を書くことができます。C言語やJavaのようにbreak文は不要で、fallthrough文を使わない限り、自動的にcase節の終わりで処理が終了します。

型によるswitch

編集
package main

import "fmt"

func main() {
    var x interface{} = "Hello"
    switch v := x.(type) {
    case int:
        fmt.Println("xはint型です")
    case string:
        fmt.Println("xはstring型です:", v)
    default:
        fmt.Println("xはその他の型です")
    }
}

このコードでは、xstring型であるため、"xはstring型です: Hello"が出力されます。

select文

編集

select文は、通信チャンネルを使った並行処理のための構文です。複数のチャンネルからの入力を待機し、その中で最初に準備ができたものを処理します。

Select文を使ったタイムアウト
package main

import (
        "fmt"
        "time"
)

func main() {
        done := make(chan bool)
        go func(s int) {
                fmt.Printf("#%d..do\n", s)
                time.Sleep(time.Duration(s) * time.Second)
                fmt.Printf("#%d..done\n", s)
                done <- true
        }(2)

        select {
        case <-done:
                fmt.Println("Done!")
        case <-time.After(1 * time.Second):
                fmt.Println("Timeout!")
        }
}
実行結果
#2..do 
Timeout!

反復:for文

編集

Goでは、for文が唯一の繰り返し構文です。Goにはdowhileはなく、for文のみで繰り返し処理を行います。

Cスタイルの for 文 (初期化、条件式、後処理)

編集

Goでは、C言語のような構文で for 文を使用できます。この形式では、初期化、条件式、後処理を記述することができます。

package main

import "fmt"

func main() {
	for i := 0; i < 5; i++ {
		fmt.Println(i)
	}
}
実行結果
0
1
2
3 
4

この例では、i を0から始めて5未満の間、1ずつ増加させながらループを繰り返します。i++ は後処理にあたります。

条件式だけの for

編集

for 文の初期化と後処理を省略し、条件式のみを使うこともできます。C言語のwhile 文に相当します。

package main

import "fmt"

func main() {
	i := 0
	for i < 5 {
		fmt.Println(i)
		i++
	}
}
実行結果
0
1
2
3 
4

ここでは、i が5未満である限りループを繰り返します。i++ はループ内で更新されます。

無限ループ

編集

条件式を省略すると、無限ループを作成することができます。break を使ってループを終了させることができます。

package main

import "fmt"

func main() {
	for {
		fmt.Println("無限ループ")
		break // ループを終了
	}
	fmt.Println("脱出")
}
実行結果
無限ループ 
脱出

このように、Goの for 文は非常に柔軟で、様々なループの形に対応しています。

range を使ったループ

編集

Goでは、range を使うことで、配列やスライス、マップ、チャンネルなどを簡単にループ処理できます。

スライス
package main

import "fmt"

func main() {
	s := []int{2, 3, 5, 7, 11}
	for index, value := range s {
		fmt.Println(index, value)
	}
}
実行結果
0 2
1 3
2 5
3 7 
4 11
マップ
package main

import "fmt"

func main() {
	m := map[string]int{"a": 1, "b": 2, "c": 3}
	for key, value := range m {
		fmt.Println(key, value)
	}
}
実行結果
a 1
b 2 
c 3
チェンネル
package main

import "fmt"

func main() {
	ch := make(chan int)

	go func(ch chan int) {
		for i := 0; i < 5; i++ {
			ch <- 2*i + 1
		}
		close(ch)
	}(ch)

	for num := range ch {
		fmt.Println(num)
	}

	fmt.Println("done.")
}
実行結果
1
3
5
7
9 
done.

rangeと関数を使ったループ

編集

Go 1.23 では、for 文の range 構文が拡張され、関数を使ってイテレーションができるようになりました。これにより、配列やスライス、マップ、チャネルだけでなく、関数の結果を使ってループ処理を行うことが可能になりました。

具体的には、関数を引数として受け取り、その yield を使って値を返し、for 文がその値を反復処理します。この仕組みによって、再帰的なデータ構造や動的に生成されるデータを簡単に扱えるようになりました。

package main

import "fmt"

func main() {
	factorial := func(yield func(x int) bool) {
		result := 1
		i := 1
		for yield(result) {
			result *= i
			i++
		}
	}

	// print the factorial numbers below 1000:
	for x := range factorial {
		if x >= 1000 {
			break
		}
		fmt.Printf("%d ", x)
	}
}
実行結果
1 1 2 6 24 120 720

このように、関数内で yield を使って値を返し、for 文でその値を反復処理することができます。

まとめ

編集

Go言語の制御構造は、プログラムのフローを柔軟に制御するための強力なツールです。条件分岐やループ、並行処理を適切に使い分けることで、効率的で読みやすいコードを書くことができます。

脚註

編集