编辑,我添加了第一部分以更好地解决您的两个实现。
首先,使用foldr 解决您的实施问题,这里有几点说明:
在 Haskell 中,Lambda 以反斜杠开头,而不是斜杠。这是因为反斜杠有点像 lambda 希腊字母 (λ)。
仅使用特殊字符命名的函数,如+,默认情况下是中缀。如果你在它们周围使用括号,它会将它们变成前缀函数:
$> (+) 1 5
$> 6
- 传递给
foldr 的函数需要两个参数,而您只在您的lambda 中提供一个。如果您真的想忽略第二个,可以使用 _ 而不是将其绑定到变量 (\x _ -> x)。
我认为你会在这个实现中陷入困境。请参阅下面的讨论,了解我对解决此问题的正确方法的看法。
注意:可以使用foldr (source) 来实现map,这是您可以在工作(第二个)实现中使用foldr 的一种方式。
使用foldr 实现这一点并不是最优的,因为顾名思义,它是从右侧折叠的:
foldr1 (+) [1..5]
--is equivalent to:
(1+(2+(3+(4+5))))
如您所见,求和操作是从列表的尾部开始完成的,这不是您要查找的。要完成这项工作,您必须“作弊”,并将列表翻转两次,一次是在折叠之前,一次是在折叠之后:
scan = tail . reverse . foldr step [0] . reverse where
step e acc@(a:_) = (e + a) : acc
您可以使用从左侧折叠的左折叠来改善这一点:
foldl1 (+) [1..5]
--is equivalent to:
((((1+2)+3)+4)+5)
然而,这仍然不理想,因为要保持累加器中元素的顺序相同,您必须使用 ++ 函数,这相当于此类函数的二次时间复杂度。一个折衷方案是使用: 函数,但是你仍然需要在折叠后反转你的累加器列表,这只是线性复杂度:
scan' :: [Integer] -> [Integer]
scan' = tail . reverse . foldl step [0] where
step acc@(a:_) e = (e + a) : acc
这仍然不是很好,因为reverse 增加了额外的计算。因此,理想的解决方案是使用scanl1,作为奖励,它不需要您提供起始值(上述示例中的[0]):
scan'' :: [Integer] -> [Integer]
scan'' = scanl1 (+)
scanl1是按照scanl来实现的,大致是这样定义的:
scanl f init list = init : (case list of
[] -> []
x:xs -> scanl f (f init x) xs)
因此,您可以这样做:
$> scanl1 (+) [1..3]
$> [1,3,6]
最后一点,您的 scan 函数不必要地专用于 Integer,因为它只需要一个 Num 约束:
scan :: Num a => [a] -> [a]
这甚至可能会提高性能,但这就是我的能力结束的地方,所以我不会再进一步了:)