読者です 読者をやめる 読者になる 読者になる

レガシーコード生産ガイド

私に教えられることなら

WriterでFizzBuzz

何か腕試しをやろう、ということでHaskellFizzBuzz書いてみた ただFizzBuzz表示するだけじゃ楽しくないので、Writerモナドのとこ思い出してFizzBuzzを作れるようにしてみた

import Control.Monad
import Data.Monoid

newtype FizzBuzzer log target = FizzBuzzer { runFizzBuzz :: (target, log) }

instance (Monoid log) => Monad (FizzBuzzer log) where
  return x = FizzBuzzer (x, mempty)

  (FizzBuzzer (x, old)) >>= f =
    let FizzBuzzer (y, new) = f x
    in  FizzBuzzer (y, old `mappend` new)

fbBuilder :: (Int -> Bool) -> String -> Int -> FizzBuzzer String Int
fbBuilder f mes x
  | f x == True = FizzBuzzer (x, mes)
  | otherwise   = FizzBuzzer (x, "")

mulsOf :: Int -> Int -> Bool
mulsOf x y = y `mod` x == 0

fzbz :: Int -> String -> Int -> FizzBuzzer String Int
fzbz i mes = fbBuilder (mulsOf i) mes

showFzbz :: (Int, String) -> String
showFzbz (x, "")  = show x
showFzbz (_, s) = s
        
test = map (\x -> showFzbz $ runFizzBuzz $
                  return x >>=
                  fzbz 3 "Fizz" >>=
                  fzbz 5 "Buzz" >>=
                  fzbz 7 "Jazz!" >>=
                  fzbz 9 "Foo!")
       [1 .. (3 * 5 * 7)]

これでtestを評価すると、

["1","2","Fizz","4","Buzz","Fizz","Jazz!","8","FizzFoo!","Buzz","11","Fizz","13","Jazz!","FizzBuzz","16","17","FizzFoo!","19","Buzz","FizzJazz!","22","23","Fizz","Buzz","26","FizzFoo!","Jazz!","29","FizzBuzz","31","32","Fizz","34","BuzzJazz!","FizzFoo!","37","38","Fizz","Buzz","41","FizzJazz!","43","44","FizzBuzzFoo!","46","47","Fizz","Jazz!","Buzz","Fizz","52","53","FizzFoo!","Buzz","Jazz!","Fizz","58","59","FizzBuzz","61","62","FizzJazz!Foo!","64","Buzz","Fizz","67","68","Fizz","BuzzJazz!","71","FizzFoo!","73","74","FizzBuzz","76","Jazz!","Fizz","79","Buzz","FizzFoo!","82","83","FizzJazz!","Buzz","86","Fizz","88","89","FizzBuzzFoo!","Jazz!","92","Fizz","94","Buzz","Fizz","97","Jazz!","FizzFoo!","Buzz","101","Fizz","103","104","FizzBuzzJazz!"]

となる。 本当は

mapFzBz fb xs = map (\x -> runFizzBuzz $ return x >>= fb) xs

test = mapFzBz (fzbz 3 "Fizz" >>= fzbz 5 "Buzz") [1 .. (3 * 5)]

とかやりたかったんだけど、returnを追加せずに fzbz 3 "Fizz" >>= fzbz 5 "Buzz" だけ渡そうとするとエラーになる。ここらへんの動作よくわからないな…

おまけ

import Data.List

contains :: Int -> Int -> Bool
contains i x = isInfixOf (show i) (show x)

nabeatsu = map (\x -> runFizzBuzz $
                     return x >>=
                     fzbz 3 "aho!" >>=
                     fbBuilder (contains 3) "aha!"
               )
           [1 .. 30]
広告を非表示にする