给出列表[x0,x1,x2,。 。 。 ,xn-1],函数应该返回列表[y0,y1,y2,。 。 。 ,yn-1],其中y0 = x0,y1 = x0 + x1
因此,如果将[1,2,3]
作为输入,则将获得[1,3,6]
作为输出
我不完全了解文件夹,所以也许我能在设法弄清楚如何更改最后一行以获得正确答案的过程中获得一些帮助。
scan :: [Integer] -> [Integer]
scan [] = []
scan [x] = [x]
scan (x:xs) = x : foldr (/y -> y (+) x) 0 (scan xs)
我的初始解决方案(有效)使用地图功能。
scan :: [Integer] -> [Integer]
scan [] = []
scan [x] = [x]
scan (x:xs) = x : map (+x) (scan xs)
EDIT,我添加了第一部分以更好地解决您的两个实现。
首先,使用foldr
解决您的实现问题,以下是几点说明:+
,在默认情况下为infix。如果在它们周围使用括号,它将变成前缀函数:$> (+) 1 5
$> 6
传递给
foldr
的函数有两个参数,而您在lambda中只提供了一个。如果您确实想忽略第二个,可以使用_
而不是将其绑定到变量(\x _ -> x
)。
Note:可以使用map
(foldr
)实现source,这是在工作(第二个)实现中使用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
是根据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
这甚至可能会提高性能,但这就是我能力的终结,所以我不会再继续了:)