Haskell - 这个函数出了什么问题?

时间:2013-05-14 01:44:07

标签: haskell functional-programming

我正在尝试使用下面的函数计算谐波系列。但是有一个类型错误,不太确定它是什么意思?另一个问题,为什么[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]    

3 个答案:

答案 0 :(得分:7)

错误告诉您,您的代码类型不合适,因此没有意义。

你的职能:

sumHR = foldr (+) 0 (\x -> map (1/) [1..x])

考虑:

Prelude> :t foldr
foldr :: (a -> b -> b) -> b -> [a] -> b

为此,(+)是第一个参数,类型必须统一(a -> b -> bNum 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])

答案 1 :(得分:4)

您是否尝试无点编写?如果是,则需要使用合成运算符.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

答案 2 :(得分:4)

之前的答案已经解释了如何使用您显然想要的签名修复该功能;然而,这并不是计算序列的好方法,因为对于您请求的每个元素,它必须从头开始。一个更有效的,在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..] ]