見出し画像

Golang_switch文とforのラベル #463

C由来の多くの言語にあるように、Go言語にもswitch文があります。

Go言語のswitch文は「フォールスルー」しないという特徴があります。つまり一致するcaseが見つかれば以降は実行されず、breakは不要です。一方で、breakで明示的に停止することも可能で、forのラベルと組み合わせるとその意味が出てきます。

それぞれコード例と共に見ていきたいと思います。

switchの基本構文

基本構文は以下です。
フォールスルーしないので、各caseでのbreakは不要です。

switch 簡易ステートメント; 比較対象の変数 {
case 比較する値:
    // 該当した場合の処理
case 比較する値:
    // 該当した場合の処理
      .
      .
      .
default:
    // 何も該当しなかった場合の処理
}

複数の値で同じ処理をしたい場合、caseに「,」で区切って指定します。

switch 簡易ステートメント; 比較対象の変数 {
case 比較する値, 比較する値, 比較する値:
    // 該当した場合の処理
case 比較する値, 比較する値:
    // 該当した場合の処理
      .
      .
      .
default:
    // 何も該当しなかった場合の処理
}

例として、文字列の文字数に応じて処理を分ける関数です。ちなみにutf8.RuneCountInString関数はUTF-8エンコーディングでの「文字」の数、つまり「ルーン」の数を数えます。一方でlen関数はメモリ上におけるバイト列の長さ(バイト数)を返します。

import (
	"fmt"
	"unicode/utf8"
)

func judgeWords(words []string) {
	fmt.Println("judgeWords!!!!!")
	for _, word := range words {
		switch size := utf8.RuneCountInString(word); size {
		case 1, 2, 3, 4:
			fmt.Printf("[%s]の文字列は%d文字で、短い。\n", word, size)
		case 5:
			fmt.Printf("[%s]の文字列は%d文字で、ちょうど良い。\n", word, size)
		case 6, 7, 8, 9:
		default:
			fmt.Printf("[%s]の文字列は%d文字で、とても長い。\n", word, size)
			if n := len(word); size < n {
				fmt.Printf("%dバイトもある!\n", n)
			}
		}
	}
}
 
func main() {
	words := []string{"YM202110", "test", "ポスター", "Mac", "ヘッドホン", "とても長い文章を想定している"}
	judgeWords(words)
}

これを実行すると以下の結果が得られます。

judgeWords!!!!!
[test]の文字列は4文字で、短い。
[ポスター]の文字列は4文字で、短い。
[Mac]の文字列は3文字で、短い。
[ヘッドホン]の文字列は5文字で、ちょうど良い。
[とても長い文章を想定している]の文字列は14文字で、とても長い。
42バイトもある!

前述の通り、breakを付けることもできます。ただしこれが必要になるケースはあまり無いみたいです。あるとすれば、for文でswitch文を使っており、caseに一致した際にループを抜けたい場合などです。

その場合、for文にラベルを付けてbreakします。先程のfor文にjudgeLoopというラベルをつけて、5文字の文字列が見つかった時点でループを停止するようにしてみます。

ちなみにGoでは、ラベルによってfor文のインデントが下がらないように書けます。

import (
	"fmt"
	"unicode/utf8"
)

func judgeWordsWithBreak(words []string) {
	fmt.Println("judgeWordsWithBreak!!!!!")
judgeLoop:
	for _, word := range words {
		switch size := utf8.RuneCountInString(word); size {
		case 1, 2, 3, 4:
			fmt.Printf("[%s]の文字列は%d文字で、短い。\n", word, size)
		case 5:
			fmt.Printf("[%s]の文字列は%d文字で、ちょうど良い。\n", word, size)
			break judgeLoop
		case 6, 7, 8, 9:
		default:
			fmt.Printf("[%s]の文字列は%d文字で、とても長い。\n", word, size)
			if n := len(word); size < n {
				fmt.Printf("%dバイトもある!\n", n)
			}
		}
	}
}
 
func main() {
	words := []string{"YM202110", "test", "ポスター", "Mac", "ヘッドホン", "とても長い文章を想定している"}
	judgeWordsWithBreak(words)
}

これを実行すると以下の結果が得られます。

judgeWordsWithBreak!!!!!
[test]の文字列は4文字で、短い。
[ポスター]の文字列は4文字で、短い。
[Mac]の文字列は3文字で、短い。
[ヘッドホン]の文字列は5文字で、ちょうど良い。


基本構文からの省略

基本構文から、簡易ステートメントや比較対象の変数を省略することもできます。

簡易ステートメントを省略してみます。

switch 比較対象の変数 {
case 比較する値:
    // 該当した場合の処理
case 比較する値:
    // 該当した場合の処理
      .
      .
      .
default:
    // 何も該当しなかった場合の処理
}

土曜日かどうかで処理を分ける関数です。

import (
	"fmt"
	"time"
)

func isSaturday(d time.Weekday) {
	switch d {
	case time.Saturday:
		fmt.Println("d is Saturday!")
	default:
		fmt.Println("d is not Saturday.")
	}
}
 
func main() {
	d := time.Saturday
	isSaturday(d)
}

ブランクswitch

比較対象の変数も省略でき、これをブランクswitchと呼びます。
ブランクswitchでは、各caseに対して「==」や「<」等の論理演算子を使用した比較が可能です。

func greeting() {
	switch t := time.Now(); {
	case t.Hour() < 12:
		fmt.Println("Good morning!")
	case t.Hour() < 17:
		fmt.Println("Good afternoon.")
	default:
		fmt.Println("Good evening.")
	}
}

この場合でも簡易ステートメントは省略できます。

func greetingWithArg(t time.Time) {
	switch {
	case t.Hour() < 12:
		fmt.Println("Good morning!")
	case t.Hour() < 17:
		fmt.Println("Good afternoon.")
	default:
		fmt.Println("Good evening.")
	}
}


case節での計算

case節は比較対象の変数と値が一致するかどうかを見ていますが、この中で足し算などをして値を調節することも可能です。

例えば「今日が土曜日までどの程度あるか」を判定する関数です。

func whenIsSaturday(d time.Weekday) {
	fmt.Println("When's Saturday?")
	switch time.Saturday {
	case d + 0:
		fmt.Println("Today.")
	case d + 1:
		fmt.Println("Tomorrow.")
	case d + 2:
		fmt.Println("In two days.")
	default:
		fmt.Println("Too far away.")
	}
}

Weekdayは、日曜日が1〜土曜日が7となる数字で表現されます。ここではtime.Saturday (=7)を与えられたd (=曜日)と比較していくことで条件分岐させています。

事例があまり直観的では無いかもですが、要はcase節で値を調節しながら比較できる、ということです。


ifを使うかswitchを使うか

ここまで見ていただければ分かる通り、switchはif文と機能が近く、特にブランクswitchはifと同じ処理を記載することが可能です。ではどのような場合にifを使い、どのような場合にswitchを使うべきでしょうか?

1つの解は、「各ケースに登場する値の間に何らかの関係がある場合はswitchを使う」というものです。

例えば曜日だったり時間だったり、ビジネスロジック上の特定の分類だったり、、値同士が横並びにできるものだったら、switchの方がわかりやすそうです。


ここまでお読みいただきありがとうございました!!

参考


この記事が気に入ったらサポートをしてみませんか?