4

我正在尝试使用列表推导在 haskell 中编写“fizzbuzz”。

为什么以下工作不起作用,应该如何工作?

[ 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 ]
4

8 回答 8

38

这不是有效的 Haskell。分支在else中不是可选的if ... then ... else。与其使用if,这似乎是使用case语句的好机会。

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

该片段适用于传统的“fizzbuzz”;您的代码似乎略有不同。

于 2011-08-05T13:57:27.323 回答
11

首先,您缺少表达的else部分if。在 Haskell 中,if是一个表达式,而不是一个语句,所以该else部分是强制性的。

其次,列表推导仅在所有保护表达式的计算结果为时才产生任何值True。1 到 20 之间没有数字 0 以 3、4 和 5 为模,因此您不会得到任何结果。您将希望使用||(logical OR) 来组合它们。

第三,FizzBu​​zz 的大多数定义都希望您返回不满足任何其他条件的数字本身。在这种情况下,您需要使用show将数字转换为String.

于 2011-08-05T13:56:34.453 回答
4

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
于 2011-08-06T14:01:20.317 回答
3

一般来说,如果你也给出错误,而不仅仅是代码,它会有所帮助。

但在这种情况下,问题在于每个人都if需要一个else子句。请记住,if语句只是一个表达式,因此两个分支都必须返回适当类型的值。

顺便说一句,您的代码中有几个错误,但这是唯一的编译器错误。

于 2011-08-05T13:51:52.297 回答
2

在一些明智的柯里化的帮助下,Calvin Bottoms 用 77 个字符做到了这一点。

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]]
于 2013-07-29T21:41:01.200 回答
2

这是使用列表理解 + 守卫对传统 FizzBu​​zz 问题的回应。

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

或者不要害怕将where子句移动到单独的简洁函数中进行评估x,然后从列表理解中调用该函数。建立在简洁的小函数上比尝试在一个更复杂的函数中解决它更好。例如

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]]
于 2011-08-05T23:45:48.023 回答
2

Monad Reader包含一个解决方案以及许多有价值的见解和一些更有趣的练习。解决方案在这里复制给那些只想看到它的人。

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
于 2015-06-30T18:25:21.280 回答
1

没有 x 满足在 1..20 范围内可被 3、4 和 5 整除的条件。

因此,如果示例在语法上正确,您将得到一个空列表,但事实并非如此。

于 2011-08-05T13:52:32.083 回答