【问题标题】:Haskell - what is wrong with this function?Haskell - 这个功能有什么问题?
【发布时间】:2013-05-14 01:44:06
【问题描述】:
我正在尝试使用以下函数计算谐波级数。但是有一个类型错误,不太清楚这意味着什么?另一个问题,为什么 [5..1] 会给出一个空列表?
sumHR = foldr (+) 0 (\x -> map (1/) [1..x])
错误信息:
*** Expression : foldr (+) 0 (\x -> map (1 /) (enumFromTo x 1))
*** Term : \x -> map (1 /) (enumFromTo x 1)
*** Type : b -> [b]
*** Does not match : [a]
【问题讨论】:
标签:
haskell
functional-programming
【解决方案1】:
错误告诉您,您的代码类型不正确,因此没有意义。
你的功能:
sumHR = foldr (+) 0 (\x -> map (1/) [1..x])
考虑:
Prelude> :t foldr
foldr :: (a -> b -> b) -> b -> [a] -> b
所以要做到这一点,(+) 是第一个参数,并且类型必须统一(a -> b -> b 和 Num a => a -> a -> a 统一为 Num a => a -> a -> a)。
第二个参数是类型变量b,我们已经知道它必须是Num a => a。这很好,您提供了0 作为第二个参数。
第三个参数必须与Num a => [a] 类型一致。但是,您提供了第二个参数,它是一个函数:
Prelude> :t (\x -> map (1/) [1..x])
(\x -> map (1/) [1..x]) :: (Enum b, Fractional b) => b -> [b]
除非您可以向编译器展示如何使 (Enum b, Fractional b) => b -> [b] 的类型与 Num a => [a] 相同,否则您将陷入困境。
你可能有一个函数,例如:
sumHR x = foldr (+) 0 (map (1/) [1..x])
【解决方案2】:
你是不是想写点免费的?如果是这样,您需要使用组合运算符. 组合foldr (+) 0 和(\x -> map (1/) [1..x])。
sumHR = foldr (+) 0 . (\x -> map (1/) [1..x])
或者,完全指向:
sumHR x = foldr (+) 0 (map (1/) [1..x])
(顺便说一句,为了提高效率,您需要使用foldl' 而不是foldr)
【解决方案3】:
前面的答案已经解释了如何使用您显然想要的签名来修复函数;但是,这并不是计算 sequence 的真正好方法,因为对于您请求的每个元素,它都必须从头开始。一种更有效、在 Haskell 中实际上更简单的方法是计算一个代表整个序列的惰性列表。所以,你从
map (1/) [1..]
(或者,也许更易读,[ 1/i | i<-[1..] ]),然后执行“结果的每个元素都是给定列表中所有前面元素的总和”。这称为扫描。由于这在列表的整个一侧(而不是只有两个元素,如折叠)始终是严格的,因此需要从左侧完成。你可以写
sumHR' :: Fractional x => [x]
sumHR' = scanl (+) 0 [ 1/i | i<-[1..] ]
或者,由于无限列表永远不会为空,
sumHR' = scanl1 (+) [ 1/i | i<-[1..] ]