Skip to content

Instantly share code, notes, and snippets.

@jbrains
Forked from srbaker/gist:b82ff8f170f1c71f30e9
Last active August 29, 2015 14:01
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jbrains/2a29465457bddcca0c45 to your computer and use it in GitHub Desktop.
Save jbrains/2a29465457bddcca0c45 to your computer and use it in GitHub Desktop.
-- Thanks to @kimwallmark for teaching me `maybe` and showing me how a single lookup.
-- I had the brilliant idea of using `snd`, even though I dislike the name. :)
fizzbuzz :: Integer -> String
fizzbuzz n = maybe (show n) snd $ classify n
where
classify n = find (\(m, _) -> n `mod` m == 0) [(15, "Fizzbuzz"), (5, "Buzz"), (3, "Fizz")]
@jbrains
Copy link
Author

jbrains commented May 7, 2014

I can change classifications to map Maybe Integer to String, then add (Nothing, show n) as a sentinel, but that feels wrong somehow. It looks more complicated, even though it eliminates the "error" path.

@jni-
Copy link

jni- commented May 7, 2014

Guards. Like so :

fizzbuzz :: Integer -> String
fizzbuzz number 
  | number `mod` 15 == 0 = "fizzbuzz"
  | number `mod` 5 == 0 = "buzz"
  | number `mod` 3 == 0 = "fizz"
  | otherwise = show number

@jbrains
Copy link
Author

jbrains commented May 7, 2014

I could turn classifications into a Map, then use findWithDefault, I guess. Then I get all kinds of conflicts among Data.Map.map, Data.List.map and Prelude.map, and I'd rather have an adult tell me how to deal with that than flail about until it compiles. :)

@jbrains
Copy link
Author

jbrains commented May 7, 2014

Thanks, @jni-. I wanted to try writing this without guards. I find the guards version simple enough, especially with only four cases.

@jni-
Copy link

jni- commented May 7, 2014

@jbrains

How about this one? Everything in haskell is a fold :)

fizzbuzz :: Integer -> String 
fizzbuzz number = foldr (\(divisor, word) acc -> if number `mod` divisor == 0 then word else acc) (show number) table
  where table = [(15, "fizzbuzz"), (5, "buzz"), (3, "fizz")]

The only catch is that table has to be in descending order. Could be a simple matter of sorting it by key first if you wanted.

@jbrains
Copy link
Author

jbrains commented May 7, 2014

Thanks again, @jni-. I'll have to read that more carefully. :)

@jbrains
Copy link
Author

jbrains commented May 7, 2014

Interesting use of fold: it feels similar to using it to compute max of an arbitrary list, using the "accumulator" to represent "highest so far". In this case, the accumulator represents "the best answer so far".

As you say, though, this relies on the sequence of the items in table, and in a way that I didn't easily see. While I find this too clever, it might become convenient during refactoring to know that foldr can behave like find in certain situations.

I would prefer to do this with findWithDefault, which the maybe version implements.

@jni-
Copy link

jni- commented May 8, 2014

Actually, find is a better solution in this case I would think (I'm no expert either, not even close). But for academic purposes, I like to do everything with a fold because... well, because everything manipulating a list can be a fold. Helps me understand the rest.

Find can be written with a fold. In reality it uses filter though. Filter could also be implemented with fold, but it uses pattern matching and recursion. Someone clever than me must know why this is a better choice :)

Your code with the find implementation has the exact same gotcha as mine does though, just less obvious. find is just listToMaybe . filter. Filter will return a list filtered by a predicate, and listToMaybe turns [1, 2, 3] into Just 1 (and [] into Nothing). The 2 and 3 go into the trash.

@davidallsopp
Copy link

Have you already seen http://www.haskell.org/haskellwiki/Haskell_Quiz/FizzBuzz ? There are several quite different approaches there...

@davidallsopp
Copy link

A variant on the guards version is to use a pattern match. I adapted this from the Scala version at http://rosettacode.org/wiki/FizzBuzz#Scala

fizzbuzz :: Integer -> String
fizzbuzz n = case map (mod n) [3,5] of [0,0]->"FizzBuzz"; [0,_]->"Fizz"; [_,0]->"Buzz"; _->show n

(Naturally, this can be formatted sensibly rather than as a gratuitous one-liner! Although I now realize that the canonical guard version can actually be packed into a smaller one-liner, if one is that way inclined ;-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment