-
-
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")] |
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
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. :)
Thanks, @jni-. I wanted to try writing this without guards. I find the guards version simple enough, especially with only four cases.
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.
Thanks again, @jni-. I'll have to read that more carefully. :)
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.
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.
Have you already seen http://www.haskell.org/haskellwiki/Haskell_Quiz/FizzBuzz ? There are several quite different approaches there...
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 ;-)
I can change
classifications
to mapMaybe Integer
toString
, then add(Nothing, show n)
as a sentinel, but that feels wrong somehow. It looks more complicated, even though it eliminates the"error"
path.