もしRubyistがHaskellを学んだら(18) FizzBuzz問題
タグ: learning_haskell / 初版公開: 2014-01-12

Haskellの書き方も何となくわかってきたので、ここで基本に立ち返り、いわゆるFizzBuzz問題をHaskellで実装してみることにする。

FizzBuzz問題の詳細はWikipediaでも見てもらうことにする。要旨を説明すると、FizzBuzz問題は数値を順に表示するが、その際に3の倍数はFizz、5の倍数はBuzz、3の倍数かつ5の倍数はFizz Buzzに置き換えて表示するという問題だ。

FizzBuzz問題をRubyで実装すると以下のコードとなる。fizzbuzzは整数を取り、それを”Fizz”か”Buzz”か”Fizz Buzz”か数値に変換する。fizzbuzz_mapはArrayで渡された数値列にfizzbuzzを適用した結果のArrayを返す。

def fizzbuzz(x)
	if x % 15 == 0
		"Fizz Buzz"
	elsif x % 3 == 0
		"Fizz"
	elsif x % 5 == 0
		"Buzz"
	else
		x.to_s
	end
end

def fizzbuzz_map(xs)
	xs.map{|x|
		fizzbuzz x	
	}
end

p fizzbuzz_map [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
# => ["1", "2", "Fizz", "4", "Buzz", "Fizz", "7", "8", "Fizz", "Buzz", "11", "Fizz", "13", "14", "Fizz Buzz"]

これをHaskellで実装してみることにする。Haskellで分岐を実現する方法はif then elseやパターンマッチなどいくつか方法があるが、今回はガード式でfizzbuzzを実装することにする。fizzbuzz_mapはリストの整数に順にfizzbuzzを適用するものとし、リストから順に値を取り出すのにリスト内包表記のジェネレータを利用する。便宜的にこの内容をfizzbuzz.hsに保存してあるものとする。

fizzbuzz :: Int -> String
fizzbuzz x 
		| x `mod` 15 == 0 = "Fizz Buzz"
		| x `mod` 3 == 0 = "Fizz"
		| x `mod` 5 == 0 = "Buzz"
		| otherwise = show x

fizzbuzz_map :: [Int] -> [String]
fizzbuzz_map xs = [fizzbuzz x | x <- xs]

これをGHCiからロードして、実行させてみよう。Haskellではfizzbuzz_mapに渡すリストは範囲指定により簡潔に記述できる。

Prelude> :l fizzbuzz.hs
[1 of 1] Compiling Main             ( fizzbuzz.hs, interpreted )
Ok, modules loaded: Main.
*Main> fizzbuzz_map [1..15]
["1","2","Fizz","4","Buzz","Fizz","7","8","Fizz","Buzz","11","Fizz","13","14","Fizz Buzz"]

なお敢えてfizzbuzzfizzbuzz_mapを別の定義にしているが、fizzbuzzfizzbuzz_mapだけからしか呼び出されない局所関数でも良いなら、whereを使って以下の書き方もできる。

fizzbuzz_map :: [Int] -> [String]
fizzbuzz_map xs = [fizzbuzz x | x <- xs]
	where fizzbuzz x 
			| x `mod` 15 == 0 = "Fizz Buzz"
			| x `mod` 3 == 0 = "Fizz"
			| x `mod` 5 == 0 = "Buzz"
			| otherwise = show x

FizzBuzzの実装とは関係ないが、Haskellの入門には”すごいHaskellたのしく学ぼう!”がおすすめだ。これを読み進めるとみるみるHaskellでコードを書けるようになる。

すごいHaskellたのしく学ぼう!
Miran Lipovača
オーム社
売り上げランキング: 126,678