はじめに
正月休み時間があったので Go 言語を勉強しました。
基本文法の確認のため FizzBuzz を書いてみたら Go 言語ならではの面白い書き方ができるなー、と思ったので投稿することにしました。
FizzBuzz のルール
-
1
から100
まで数字を出力する - その数字が
3
で割り切れるならFizz
と出力する - その数字が
5
で割り切れるならBuzz
と出力する - その数字が
3
でも5
でも割り切れるならFizzBuzz
と出力する
コード
type FizzBuzzInt int
func (i FizzBuzzInt) String() string {
switch {
case i%15 == 0:
return "FizzBuzz"
case i%3 == 0:
return "Fizz"
case i%5 == 0:
return "Buzz"
default:
return strconv.Itoa(int(i))
}
}
func main() {
i := FizzBuzzInt(1)
for i <= 100 {
fmt.Printf("%3d => %v\n", i, i)
i++
}
}
実行結果
1 => 1
2 => 2
3 => Fizz
4 => 4
5 => Buzz
6 => Fizz
7 => 7
8 => 8
9 => Fizz
10 => Buzz
11 => 11
12 => Fizz
13 => 13
14 => 14
15 => FizzBuzz
16 => 16
17 => 17
18 => Fizz
19 => 19
20 => Buzz
(以下略)
解説
型定義
type FizzBuzzInt int
上記のようにして int
型を元に新たな型を定義できる。
このように定義した型は基本的には int
と同じように扱うことができる。
var i FizzBuzzInt = 10
// 算術演算子や比較演算子はそのまま使用可能
i++
fmt.Println(i) // => 11
fmt.Println(i % 2) // => 1
fmt.Println(i <= 10) // => false
ただし型チェックは FizzBuzzInt
型として行われるので、
引数が int
型の関数に渡すとコンパイルエラーになる。
int型の関数に渡した場合
func printInt(i int) {
fmt.Println(i)
}
var i FizzBuzzInt = 10
// 引数の型が一致しないのでコンパイルエラーになる
printInt(i)
// FizzBuzzInt と int は相互変換可能なので、キャストしてやれば問題ない
printInt(int(i))
メソッド
Go言語では 型 にメソッドを紐付ける。
例えば以下のように、整数型(を元に定義した型)に奇数判定用のメソッドを付けることができる。
整数型にメソッドを追加する
type MyInt int
// MyInt 型に IsOdd メソッドを紐付ける
func (mi MyInt) IsOdd() bool {
return mi%2 != 0
}
i := MyInt(1)
i.IsOdd() // => true
i++
i.IsOdd() // => false
Stringer インターフェース
Java でいう Object#toString()
みたいなもの。
string
型を返す String()
というメソッドを定義しておくと、
Println
に渡したときなどにそのメソッドが呼ばれる。
文字列関係の書式指定子 (%v
%s
%q
%x
%X
)でのみ String()
メソッドが呼ばれる仕様なので、書式指定子で出力内容を切り替えることが可能です。
Stringerインターフェースの例
type MyInt int
func (mi MyInt) String() string {
return "hoge"
}
i := MyInt(3)
fmt.Printf("%d\n", i) // => 3
fmt.Printf("%v\n", i) // => hoge
参考: https://golang.org/pkg/fmt/#pkg-overview
コード
以上を踏まえてコメントを追記しました。
// FizzBuzzInt は int を元にした型
type FizzBuzzInt int
// FizzBuzzInt 型に Stringer インターフェースを実装。
// 出力するときにこのメソッドが呼ばれ、FizzBuzz のロジックで出力内容を切り替える
func (i FizzBuzzInt) String() string {
switch {
case i%15 == 0:
return "FizzBuzz"
case i%3 == 0:
return "Fizz"
case i%5 == 0:
return "Buzz"
default:
return strconv.Itoa(int(i)) // Itoaの引数は int 型なのでキャストして渡す
}
}
func main() {
i := FizzBuzzInt(1) // i は FizzBuzzInt 型で初期化
// 比較演算は int と同様に使える
for i <= 100 {
// %d は int として表示、%v は String() メソッドの結果を表示
fmt.Printf("%3d => %v\n", i, i)
// インクリメントも int と同様に使える
i++
}
}