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?
asked Nov 30, 2016 at 14:41
\$\endgroup\$
5
  • 1
    \$\begingroup\$ Did you take a look at other fizzbuzz haskell questions, like this one? \$\endgroup\$ Commented Nov 30, 2016 at 14:49
  • \$\begingroup\$ @Mast I didn't check this particular one - great answers indeed \$\endgroup\$ Commented Nov 30, 2016 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\$ Commented Nov 30, 2016 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\$ Commented Nov 30, 2016 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\$ Commented Nov 30, 2016 at 21:16

2 Answers 2

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 ""
answered Dec 1, 2016 at 15:02
\$\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

answered Jan 7, 2017 at 20:12
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.