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の方がわかりやすそうです。
ここまでお読みいただきありがとうございました!!
参考
この記事が気に入ったらサポートをしてみませんか?