5
\$\begingroup\$

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 and fizz so that there's only one function?
  • In fizz, is it possible to use a string buffer so that I get rid of the mod x 15 == 0 guard, taking advantage of its redundancy with mod 3 x == 0 and mod 5 x == 0?
\$\endgroup\$
  • 1
    \$\begingroup\$ Did you take a look at other fizzbuzzhaskell questions, like this one? \$\endgroup\$ – Mast Nov 30 '16 at 14:49
  • \$\begingroup\$ @Mast I didn't check this particular one - great answers indeed \$\endgroup\$ – Jivan Nov 30 '16 at 14:55
  • 1
    \$\begingroup\$ "string buffer" isn't really a thing in Haskell. After all, all values are immutable. You're probably thinking of str = n % 3 ? "Fizz" : ""; str += n % 5 ? "Buzz" : ""; return str == "" ? n.toString() : str;. But you cannot mutate str in Haskell. \$\endgroup\$ – Zeta Nov 30 '16 at 16:38
  • 1
    \$\begingroup\$ The equivalent of (n % 3 ? "Fizz" : "") + (n % 5 ? "Buzz" : "") would work, but you wouldn't get the "otherwise show x" part for free. \$\endgroup\$ – Gurkenglas Nov 30 '16 at 20:32
  • \$\begingroup\$ @Gurkenglas: Sure, but at that point you're using a binding either way. It's not like you'll type (if rem n 3 == 0 then "Fizz" else "") ++ (if rem n 5 == 0 then "Buzz" else "") twice, so there is a where str = ... (hopefully). An Alternative instance that doesn't concatenate the lists would make that a real one-liner, though. Or a small helper orElse :: [a] -> [a] -> [a]; orElse [] ys = ys; orElse xs _ = xs. But then were merely moving lines. \$\endgroup\$ – Zeta Nov 30 '16 at 21:16
3
\$\begingroup\$

[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 ""
\$\endgroup\$
2
\$\begingroup\$

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

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.