我正在尝试使用列表推导在 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 ]
这不是有效的 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”;您的代码似乎略有不同。
首先,您缺少表达的else
部分if
。在 Haskell 中,if
是一个表达式,而不是一个语句,所以该else
部分是强制性的。
其次,列表推导仅在所有保护表达式的计算结果为时才产生任何值True
。1 到 20 之间没有数字 0 以 3、4 和 5 为模,因此您不会得到任何结果。您将希望使用||
(logical OR) 来组合它们。
第三,FizzBuzz 的大多数定义都希望您返回不满足任何其他条件的数字本身。在这种情况下,您需要使用show
将数字转换为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
一般来说,如果你也给出错误,而不仅仅是代码,它会有所帮助。
但在这种情况下,问题在于每个人都if
需要一个else
子句。请记住,if
语句只是一个表达式,因此两个分支都必须返回适当类型的值。
顺便说一句,您的代码中有几个错误,但这是唯一的编译器错误。
在一些明智的柯里化的帮助下,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]]
这是使用列表理解 + 守卫对传统 FizzBuzz 问题的回应。
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]]
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
没有 x 满足在 1..20 范围内可被 3、4 和 5 整除的条件。
因此,如果示例在语法上正确,您将得到一个空列表,但事实并非如此。