我有两个函数来计算整数列表的长度
lengthFoldl :: [Int] -> Int
lengthFoldl xs = (foldl (\_ y -> y+1) 0 xs)
和
lengthFold :: [a] -> Int
lengthFold xs = foldr (\_ y -> y+1) 0 xs
它们是相同的,除了一个使用文件夹和一个文件夹。
但是当尝试计算任何列表[1 .. n]
的长度时,我从lengthFoldl中得到了错误的结果(一个太大)。
答案 0 :(得分:5)
为补充joelfischerr的回答,我想指出您的函数类型给出了提示。
lengthFoldl :: [Int] -> Int
lengthFold :: [a] -> Int
为什么它们不同?我猜您可能必须更改第一个以使用[Int]
的方法,因为使用[a]
时它不会编译。但是,这是一个很大的警告信号!
如果确实在计算长度,为什么lengthFoldl
关心列表元素的类型是什么?为什么我们需要将元素设为Int
?仅需要Int
的一种可能解释:查看代码
lengthFoldl xs = foldl (\_ y -> y+1) 0 xs
我们可以看到这里唯一的数字变量是y
。如果y
被强制为数字,并且列表元素也被强制为数字,则似乎y
被视为列表元素!
的确是这样:foldl
首先将累加器传递给函数,然后将列表元素传递给函数,与foldr
不同。
一般的经验法则是:如果类型和代码不一致,则应仔细考虑哪一种是正确的。我想说大多数Haskellers会认为,在大多数情况下,正确的类型比正确的代码容易。因此,不应该只是将类型与代码相匹配以迫使其进行编译:类型错误可以代替代码中的错误。
答案 1 :(得分:3)
查看foldl和foldr的类型定义,可以清楚地知道问题出在哪里。
:t foldr
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
和
:t foldl
foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b
可以看到,文件夹将列表项和第二个参数带入函数,而文件夹foldl将列表中的第二个参数和列表项带入函数。
更改lengthFoldl
即可解决问题
lengthFoldl :: [Int] -> Int
lengthFoldl xs = foldl (\y _ -> y+1) 0 xs
编辑:使用foldl
代替foldl'
是一个坏主意:https://wiki.haskell.org/Foldr_Foldl_Foldl'