こんにちは。Go入門ブログの第10回です。
本記事では前回に引き続き、Goで使用頻度の高い制御構文であるswitchステートメントを解説していきます。
switchは他の多くの言語でも使用可能な条件分岐の構文ですが、Goのswitchは特徴的な機能を多く有しており、記述のバリエーションも広いため、独立した記事にて解説していきます。
Contents
Goのswitchの特徴
GoのswitchはCやJava等のそれと似ていますが、機能としては異なる部分が多くあります。
まず、Goでは選択されたcaseだけを実行し、それに続く全てのcase節は実行されません。
他の言語では各case節の最後にbreakステートメントを記述しますが、Goでは自動的にbreakが提供されます。
また、もう一つの重要な違いとして、Goのswitchのcase節には定数だけではなく式を指定することができます。
更に、値ではなく型による分岐も行うことが可能です。
case節に値を記述するswitch
以下は、最も一般的なswitchの構文です。
switch [簡易文;] [式] {
case 値A,値B…:
[式が値A,B…に一致した場合の処理]
case 値C,値D…:
[式が値C,D…に一致した場合の処理]
default:
[式がいずれの値にも一致しなかった場合の処理]
}
switchステートメントは最初に式を評価します。そのあとに複数のcase節が続きます。case節には複数の値をカンマ区切りで記述することができます。
そして、switchステートメントに記述された式の評価結果とcase節に列挙された値が一致する場合に、該当するcase節の処理が実行されます。
また、すべてのcase節へ分岐しなかった場合に実行されるdefault節を一つだけ記述できます。default節は省略可能です。
それではサンプルをみてみましょう。
import "fmt" func main() { swictheto("丑") swictheto("巳") swictheto("酉") swictheto("蛙") } func swictheto(trg string) { switch trg { case "子", "丑", "寅", "卯", "午", "未", "申", "戌", "亥": fmt.Println("哺乳類") case "辰", "巳": fmt.Println("爬虫類") case "酉": fmt.Println("鳥類") default: fmt.Println("この動物は干支に含まれません") } }
このサンプルの実行結果は以下のようになります。
哺乳類
爬虫類
鳥類
この動物は干支に含まれません
case節に式を記述するswitch
case節に値ではなく式を指定する場合、以下のように記述します。
switch {
case 式A…:
[式Aが真になる場合の処理]
case 式B…:
[式Bが真になる場合の処理]
default:
[いずれの式も真にならない場合の処理]
}
先ほどのcase節に値を記述する場合と比較し、switchの後ろに式が記述されていない点に注目してください。
正確に言えば、case節に式を記述する場合でも、switchの後ろに式を指定することは可能です。
しかし、case節に式を記述する場合はswitchに式を与えるべきではありません。
各case節に与えられた式が論理型を返すので、あえてswitchの後ろに真偽を指定する意味はありません。
それではサンプルをみてみましょう。
package main import "fmt" func main() { swicthnum(10) swicthnum(0) swicthnum(-1) } func swicthnum(trg int) { switch { case trg >= 10: fmt.Println("10以上") case trg < 10 && trg >= 0: fmt.Println("0以上10未満") default: fmt.Println("0未満") } }
このサンプルの実行結果は以下のようになります。
10以上
0以上10未満
0未満
型によるswitch
型アサーション
型によるswitchを見ていく前に、型アサーションについて少し解説していきましょう。
まず、Goにはすべての型と互換性があるinterface{}型があります。
interface{}型については、後の記事で詳しく解説します。
interface{}型の引数で受け渡された値は、元の型の情報が欠落しています。
Goでは、このような局面で利用するための型アサーションを提供しています。
型アサーションにより、interface{}型の値の実体の型が何であるかを動的にチェックすることができます。
型アサーションの構文は以下の通りです。
[アサーション対象の変数].([任意の型名])
型アサーションは基本的に以下のように使用します。
[変数1],[変数2] := [アサーション対象の変数].([任意の型名])
型アサーションに成功した場合、変数1にはその値が、変数2には型アサーションの成功を示す論理値trueが代入されます。
型アサーションに失敗した場合、変数1には型アサーションに指定した型のゼロ値が、変数2には型アサーションの失敗を示す論理値falseが代入されます。
それでは実例を見てみましょう。
package main import "fmt" func main() { var x interface{} = 1 // 変数iにxを、isIntに型アサーションの結果を代入 i, isInt := x.(int) fmt.Println(i, isInt) // 変数sにxを、isStringに型アサーションの結果を代入 s, isString := x.(string) fmt.Println(s, isString) }
このサンプルの実行結果は以下のようになります。
1 true
false
interface{}型変数xの値が整数値なので、x.(int)の型アサーションに成功しています。
対して、x.(string)の型アサーションには失敗し、変数sには空文字(“”)が代入されています。
型によるswitch
型アサーションを使用し、変数の型の種類による分岐を行うことができます。
switch [変数].(type){
case 型A:
[変数の型がAである場合の処理]
case 型B:
[変数の型がBである場合の処理]
default:
[いずれの式も真にならない場合の処理]
}
[変数].(type)のように、型アサーションの型名指定にtypeを用いる点に注意してください。
以下は実際の記述例です。
package main import "fmt" func main() { var b interface{} = true typeSwitch(b) var i interface{} = 1 typeSwitch(i) var s interface{} = "string" typeSwitch(s) var f interface{} = 3.14 typeSwitch(f) } func typeSwitch(trg interface{}) { switch trg.(type) { case bool: fmt.Println("論理型") case int, uint: fmt.Println("整数型") case string: fmt.Println("文字列型") default: fmt.Println("その他の型") } }
このサンプルの実行結果は以下のようになります。
論理型
整数型
文字列型
その他の型
また、型情報で分岐した後の処理内で、型アサーションした値を使用したいことがあります。
その場合は、switchの後に[値を代入する変数] := [アサーション対象の変数].(type)のように記述します。
package main import "fmt" func main() { var x interface{} = 2 typeSwitch(x) var y interface{} = 3.14 typeSwitch(y) var z interface{} = "100" typeSwitch(z) } func typeSwitch(trg interface{}) { switch val := trg.(type) { case int: // 整数の場合は2乗する fmt.Println(val * val) case float64: // 小数の場合は2倍する fmt.Println(val * 2) default: fmt.Println("数値ではないため演算できません") } }
このサンプルの実行結果は以下のようになります。
4
6.28
数値ではないため演算できません
終わりに
前回と今回の記事で、for・if・switchという代表的な制御構文の使用方法を解説しました。
制御構文についての解説は本記事で一段落とします。
Goには他にもgotoやdeferなど重要な制御構文がありますので、また機会を設けて解説していければと思います。
次回の記事では、ポインタと構造体について解説していきます。