开发者

fizzbuzz in haskell?

I'm trying to write 'fi开发者_开发问答zzbuzz' in haskell using list comprehensions.

Why doesn't the following work, and how should it be?

[ if x `mod` 5 == 0 then "BUZZFIZZ"
  if x `mod` 3 == 0 then "BUZZ" 
  if x `mod` 4 == 0 then "FIZZ" | x <- [1..20], 
    x `mod` 3 == 0, 
    x `mod` 4 == 0, 
    x `mod` 5 == 0 ]


This isn't valid Haskell. The else branch is not optional in if ... then ... else. Rather than using if, this seems like a good opportunity to use a case statement.

case (x `rem` 3, x `rem` 5) of
  (0,0) -> "fizzbuzz"
  (0,_) -> "fizz"
  (_,0) -> "buzz"
  _     -> show x

This snippet will work for a traditional "fizzbuzz"; your code seems to be slightly different.


First of all, you're missing the else parts of your if expressions. In Haskell, if is an expression, not a statement, so the else part is mandatory.

Secondly, the list comprehension only produces any values if all the guard expressions evaluate to True. There is no number between 1 and 20 that is 0 modulo 3, 4, and 5, so you'll get no results. You'll want to use || (logical OR) to combine them instead.

Third, most definitions of FizzBuzz want you to return the number itself if it does not meet any of the other conditions. In that case, you'll want to use show to convert the number to a String.


Here's another version that can be extended to an arbitrary number of substitutions:

fizzbuzz' :: [(Integer, String)] -> Integer -> String
fizzbuzz' ss n = foldl (\str (num, subst) -> if n `mod` num == 0 then str ++ subst else str ++ "") "" ss

fizzbuzz :: [(Integer, String)] -> Integer -> String
fizzbuzz ss n = if null str then show n else str
  where str = fizzbuzz' ss n

You could inline fizzbuzz' in the where clause of fizzbuzz, but I found a separate function easier for testing.

You can run it like this:

λ> mapM_ putStrLn $ map (fizzbuzz [(3, "fizz"), (5, "buzz")]) [9..15]
fizz
buzz
11
fizz
13
14
fizzbuzz

Or with extra substitutions:

λ> mapM_ putStrLn $ map (fizzbuzz [(3, "fizz"), (5, "buzz"), (7, "dazz")]) [19..24]
19
buzz
fizzdazz
22
23
fizz


In general, it helps if you give the error as well, not just the code.

But in this case, the problem is that every if needs an else clause. Keep in mind that an if statement is just an expression, so both branches must return a value of an appropriate type.

You've got several bugs in the code, by the way, but that's the only compiler error.


Here is a response to the traditional FizzBuzz problem using list comprehension + guards.

fizzbuzz = [fb x| x <- [1..100]]
    where fb y
        | y `mod` 15 == 0 = "FizzBuzz"
        | y `mod` 3  == 0 = "Fizz"
        | y `mod` 5  == 0 = "Buzz"
        | otherwise  = show y

Or alternatively don't be afraid to move where clause into separate succinct function for evaluating x then call that function from your list comprehension. Its better to build on small concise functions than trying to solve it in one more complex function. e.g.

fizzval x
    | x `mod` 15 == 0 = "FizzBuzz"
    | x `mod` 3  == 0 = "Fizz"
    | x `mod` 5  == 0 = "Buzz"
    | otherwise  = show x

fizzbuzz = [fizzval x| x <- [1..100]]


With the help of some judicious currying, Calvin Bottoms did it in 77 characters.

http://freshbrewedcode.com/calvinbottoms/2012/02/25/fizzbuzz-in-haskell/

[max(show x)(concat[n|(f,n)<-[(3,"Fizz"),(5,"Buzz")],mod x f==0])|x<-[1..25]]


The Monad Reader contains a solution as well as many valuable insights and some more fun exercises. The solution is copied here for those who just want to see it.

fizzbuzz :: Int -> String
fizzbuzz n = (test 3 "fizz" . test 5 "buzz") id (show n)
    where test d s x | n `mod` d == 0 = const (s ++ x "")
                     | otherwise      = x


There is no x that fullfills the condition to be divisible by 3, 4 and 5 in the range 1..20.

Therefore, you would get an empty list if the example were syntactically correct, which it is not.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜