Go Switch (Japanese)

"Switch" from golang wiki

Go公式WikiのSwitchの日本語意訳になります。
(元記事の最終更新日: 2017/05/31 rev.7)

スイッチ文に関する簡単な説明がされています。


Switch

仕様: https://golang.org/ref/spec#Switch_statements

Go言語のswitch文は非常にきれいです。一例として、各ケースの最後にbreakをする必要がありません。

// 訳注: HTMLの特殊文字のエスケープを行っている

switch c {
case '&':
	esc = "&"
case '\'':
	esc = "'"
case '<':
	esc = "&lt;"
case '>':
	esc = "&gt;"
case '"':
	esc = "&quot;"
default:
	panic("unrecognized escape character")
}

int型以外の型もOK

スイッチはどのような型の値でも扱うことができます。

// 訳注: OSごとに異なるシステムディレクトリを取得
// (下記URLの処理から抜粋していると思われる)
// https://golang.org/src/os/os_test.go#L72

switch syscall.OS {
case "windows":
	sd = &sysDir{
		Getenv("SystemRoot") + `\system32\drivers\etc`,
		[]string{
			"hosts",
			"networks",
			"protocol",
			"services",
		},
	}
case "plan9":
	sd = &sysDir{
		"/lib/ndb",
		[]string{
			"common",
			"local",
		},
	}
default:
	sd = &sysDir{
		"/etc",
		[]string{
			"group",
			"hosts",
			"passwd",
		},
	}
}

式無しでもswitch可能

何かをスイッチする必要は全くありません。値なしでのスイッチは “switch true” を意味しています。
下記のEffective Goでの例のように、より洗練された if-else のチェーンを書くことができます。

func unhex(c byte) byte {
	switch {
	case '0' <= c && c <= '9':
		return c - '0'
	case 'a' <= c && c <= 'f':
		return c - 'a' + 10
	case 'A' <= c && c <= 'F':
		return c - 'A' + 10
	}
	return 0
}

ブレーク

Goのswitch文は暗黙のbreakをしています。
しかし、明示的なbreakはそれでも便利です。

// 訳注: コマンドとなる文字列を受け取り、そのコマンドに合わせて処理を実行する

command := ReadCommand()
argv := strings.Fields(command)
switch argv[0] {
case "echo":
	fmt.Print(argv[1:]...)
case "cat":
	if len(argv) <= 1 {
		fmt.Println("Usage: cat <filename>")
		break  // 訳注: 明示的なbreak
	}
	PrintFile(argv[1])
default:
	fmt.Println("Unknown command; try 'echo' or 'cat'")
}

フォールスルー

次のケースにフォールスルーして進むには、 fallthroughキーワードを使います。

// バイト文字列に対して、4バイトをuint32へとunpackし、base85形式の5バイトへと再度packする

var v uint32
switch len(src) {
default:
	v |= uint32(src[3])
	fallthrough
case 3:
	v |= uint32(src[2]) << 8
	fallthrough
case 2:
	v |= uint32(src[1]) << 16
	fallthrough
case 1:
	v |= uint32(src[0]) << 24
}

訳注: Go PlayGroundで確認

fallthrough’ はケースの最後に書く必要があります。
以下のような書き方はできません。

switch {
case f():
	if g() {
		fallthrough // これは動かない!(コンパイルエラー)
	}
	h()
default:
	error()
}

しかし、この問題を回避して動かすには「ラベル付き」の fallthrough を使います。

switch {
case f():
	if g() {
		goto nextCase // これで動く!
	}
	h()
    break
nextCase:
    fallthrough
default:
	error()
}

複数のケース

複数の値を同じケースとして扱いたい場合には、カンマ区切りのリストを使います。

func letterOp(code int) bool {
	switch chars[code].category {
	case "Lu", "Ll", "Lt", "Lm", "Lo":
		return true
	}
	return false
}

// 訳注: もし値のリストが長くなる場合は以下のように改行を加えると見やすくなるかもしれません。
switch str {
case "aaaaaaaaaaaaaaaaaaaaaaaaaaa",
	"bbbbbbbbbbbbbbbbbbbbbbbbbbbb",
	"ccccccccccccccccccc",
	"dddddddDDDDDDDDDDDDDDDDDDDD",
	"eeEEeeEEeeEEeeEEeeEEeeEE":
	return true
}

型スイッチ

型スイッチによって、インターフェースの値の実際の型でスイッチすることができます。

func typeName(v interface{}) string {
	switch v.(type) {
	case int:
		return "int"
	case string:
		return "string"
	default:
		return "unknown"
	}
}

変数を宣言することも可能で、各ケースの型を持った値となります。

func do(v interface{}) string {
	switch u := v.(type) {
	case int:
		return strconv.Itoa(u*2) // このケースでは u はint型
	case string:
		mid := len(u) / 2 // 分割 - このケースでは u はstring型
		return u[mid:] + u[:mid] // 結合
	}
	return "unknown"
}

do(21) == "42"
do("bitrab") == "rabbit"
do(3.142) == "unknown"

Noopケース

場合によっては何も実行しないケースは便利なこともあります。
これは紛らわしく見える可能性があり、Noopケースとその後のケースが同じ動作をするように見えますが、そうはなりません。

// 訳注: pluralEndingの引数に1を渡した場合は何もせず、
// それ以外のintを渡した場合は英単語の複数形となる s を返却する

func pluralEnding(n int) string {
	ending := ""

	switch n {
	case 1:
	default:
		ending = "s"
	}

	return ending
}

fmt.Sprintf("foo%s\n", pluralEnding(1))  == "foo"
fmt.Sprintf("bar%s\n", pluralEnding(2))  == "bars"
 
comments powered by Disqus