Haskell プログラム集


Fizz Buzz

Fizz Buzz とは

Fizz Buzz は言葉遊びだ。どのような遊びかと言うと、

プレイヤーは円状に座る。最初のプレイヤーは「1」と数字を発言する。次のプレイヤーは直前のプレイヤーの次の数字を発言していく。ただし、3で割り切れる場合は「Fizz」(Bizz Buzzの場合は「Bizz」)、5で割り切れる場合は「Buzz」、両者で割り切れる場合(すなわち15で割り切れる場合)は「Fizz Buzz」(Bizz Buzzの場合は「Bizz Buzz」)を数の代わりに発言しなければならない。発言を間違えた者や、ためらった者は脱落となる。(Wikipedia より)

Jeff Atwood はこの言葉遊びをプログラムの問題として、プログラマのセンスを見分けるためにつかったということだ。

Fizz Buzzの Haskell プログラム

そこで、Fizz Buzz に Haskell で挑戦してみた。

ファイル名: fizzbuzz.hs

fizzbuzz n
  | n `mod` 15 ==  0 = "FizzBuzz"
  | n `mod` 3 == 0 = "Fizz"
  | n `mod` 5 == 0 = "Buzz"
  | otherwise = show n
main = mapM_ putStrLn $ map fizzbuzz [1..100]
実行例
~/sed$ runghc fizzbuzz.hs
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
以下略

State モナドを使った FizzBuzz

State モナドを使って Fizz Buzz を書いてみた。関数 fizz は3の倍数のとき "Fizz" を返し、buzz 関数は5の倍数のとき "Buzz" を返す。FizzBuzz 関数はないが、15の倍数のときは、"Fizz" と "Buzz" がマージされて "FizzBuzz" が出力される。State モナドを使ったのは、関数 fizz と関数 Buzz の結合を >>= 演算子でやりたかったからだ。

ファイル名: fizzbuzz.hs

import Control.Monad.State
fizz :: String -> State Int String
fizz str = do
  n <- get
  if mod n 3 == 0
    then return (str ++ "Fizz")
    else return (str ++ "")
buzz :: String -> State Int String
buzz str = do
  n <- get
  if mod n 5 == 0
    then return (str ++ "Buzz")
    else return (str ++ "")
fizzbuzz n = let (str,m) = runState (return [] >>= fizz >>= buzz) n
             in case str of
                [] -> show m
                _ -> str
main = mapM_ putStrLn $ map fizzbuzz [1..100]

モジュール化 Fizz Buzz

上のプログラムでは fizz と buzz の定義が繰り返しになったのでモジュール化してみた。プログラムの都合で上のプログラムとは違って、状態を文字列にして、リターン値を整数にした。fizz 関数と buzz 関数は maker 関数の部分適用で得られる。

import Control.Monad.State
maker :: String -> Int -> Int -> State String Int
maker str m n = do
  stack <- get
  if mod n m == 0
    then put (stack ++ str)
    else put (stack ++ "")
  return n
fizz = maker "Fizz" 3
buzz = maker "Buzz" 5
fizzbuzz n = let (m, str) = runState (return n >>= fizz >>= buzz) []
             in case str of
                [] -> show m
                _ -> str
main = mapM_ putStrLn $ map fizzbuzz [1..100]