HaskellでFizzBuzz問題

(2007.6.1作成; 2007.6.3新規公開)

ちょー遅ればせながら、FizzBuzz問題について考えてみる。Rubyだと、考えるの一瞬、あとはタイピングの時間だけなので、つまらない。Haskellで考えてみよう。

1から100までの数をプリントするプログラムを書け。ただし3の倍数のときは数の代わりに「Fizz」と、5の倍数のときは「Buzz」とプリントし、3と5両方の倍数の場合には「FizzBuzz」とプリントすること。

こういう風に考えてみた;

  1. 1から100まで、ということは、まずリストを用意してみよう。
  2. リストの各要素について、(1) 数字を文字列にしたもの、あるいは (2) Fizz, Buzz, FizzBuzz に置きかえればいいんじゃないかな。
  3. あとは置き換え後のリストを連結すればいいだろう。

実装してみる。リストの各要素を変換するところはmapが使えそうだ。文字列のリストを連結して一つにまとめるのはunwordsかな。

  1. repl_num x
  2. | x `mod` 15 == 0 = "FizzBuzz"
  3. | x `mod` 3 == 0 = "Fizz"
  4. | x `mod` 5 == 0 = "Buzz"
  5. | otherwise = show x
  6. fizzbuzz list = unwords $ map repl_num list
  7. main = do
  8. putStrLn $ fizzbuzz [1..100]

ghcでコンパイルし、実行してみる。いいようだ。

repl_num関数が冗長すぎる。タプルで設定を書いてみよう。その設定と置換対象の数値を受ける関数を書いてみる。

  1. fizz = (3, "Fizz")
  2. buzz = (5, "Buzz")
  3. repl_num' t@(m, str) x
  4. | x `mod` m == 0 = str
  5. | otherwise = show x

でもこれだけじゃ15(の倍数)のときに困るなぁ。

Fizzに置換した場合でもBuzzを足すかどうか判定しないといけない、最後にFizzにもBuzzにもしなかった分だけ数値(を文字列にしたもの)を足すと。

fizz, buzzタプルをリストにして、変換対象のリストの各値について設定リストを順に見ていけばよさそうだ。こうかな?

  1. fizz = (3, "Fizz")
  2. buzz = (5, "Buzz")
  3. repl_num2 [] f x = f x
  4. repl_num2 (pat@(m, str):xs) f x
  5. | x `mod` m == 0 = str ++ (repl_num2 xs nul x)
  6. | otherwise = repl_num2 xs f x
  7. where
  8. nul x = ""
  9. fizzbuzz list = unwords $ map (repl_num2 [fizz, buzz] show) list
  10. main = do
  11. putStrLn $ fizzbuzz [1..100]

動いているけど、どうもゴチャゴチャしてる。

ほかの人が書いたのも見てみよう。

ふむふむ。