Time for the most original question of the year: writing a FizzBuzz
in Haskell!
So here is what I came up with:
fizzBuzz :: [Int] -> [String]
fizzBuzz xs =
[fizz x | x <- xs]
fizz :: Int -> String
fizz x
| mod x 15 == 0 = "FizzBuzz"
| mod x 3 == 0 = "Fizz"
| mod x 5 == 0 = "Buzz"
| otherwise = show x
Then I can call
> mapM_ print $ fizzBuzz [1..15]
"1"
"2"
"Fizz"
"4"
"Buzz"
etc
My questions are:
- What are obvious awkwardnesses in this code?
- How could I mix
fizzBuzz
andfizz
so that there's only one function? - In
fizz
, is it possible to use a string buffer so that I get rid of themod x 15 == 0
guard, taking advantage of its redundancy withmod 3 x == 0
andmod 5 x == 0
?
2 Answers 2
[fizz x | x <- xs]
is the same as map fizz xs
, so fizzBuzz = map fizz
.
You can use putStrLn
instead of print
to print strings without quotes.
Using the fact that map (g . f) = map g . map f
, you can merge mapM_ putStrLn
and map fizz
into:
mapM_ (putStrLn . fizz) [1..15]
Another solution with everything in single function and without mod x 15
:
import Control.Monad (forM_, when)
fizzBuzz :: IO ()
fizzBuzz = forM_ [1..15] $ \x -> do
let [m3, m5] = map ((==0) . mod x) [3,5]
when m3 $ putStr "Fizz"
when m5 $ putStr "Buzz"
when (not m3 && not m5) $ putStr $ show x
putStrLn ""
I feel like your solution is overly complex, recursion can make this operation simpler:
fizzBuzz :: (Integral a) => [a] -> [String]
fizzBuzz [] = []
fizzBuzz (x : xs)
| x `mod` 15 == 0 = "FizzBuzz" : fizzBuzz xs
| x `mod` 3 == 0 = "Fizz" : fizzBuzz xs
| x `mod` 5 == 0 = "Buzz" : fizzBuzz xs
| otherwise = fizzBuzz xs
main = do
print $ fizzBuzz [1..20]
Using two functions to perform this is overkill. Using (Integral a) =>
in the type signature allows you to apply this function to float's, int's etc
str = n % 3 ? "Fizz" : ""; str += n % 5 ? "Buzz" : ""; return str == "" ? n.toString() : str;
. But you cannot mutatestr
in Haskell. \$\endgroup\$(n % 3 ? "Fizz" : "") + (n % 5 ? "Buzz" : "")
would work, but you wouldn't get the "otherwiseshow x
" part for free. \$\endgroup\$(if rem n 3 == 0 then "Fizz" else "") ++ (if rem n 5 == 0 then "Buzz" else "")
twice, so there is awhere str = ...
(hopefully). AnAlternative
instance that doesn't concatenate the lists would make that a real one-liner, though. Or a small helperorElse :: [a] -> [a] -> [a]; orElse [] ys = ys; orElse xs _ = xs
. But then were merely moving lines. \$\endgroup\$