Go言語でFizzBuzzを書いてみた


はじめに

正月休み時間があったので 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++
    }
}