Haskell -- 列表中的总和

Haskell -- Sums Accumulated in Lists

我需要创建或了解 Haskell 中是否有允许您从列表中添加项目的函数。所以,例如:

cumulativeAmount :: [Integer] -> [Integer]

cumulativeAmount [1,2,5,8,8,0,4,2] = [1,3,8,16,24,24,28,30]

cumulativeAmount [1,4,7,0,5] = [1, 1+4, 1+4+7, 1+4+7+0, 1+4+7+0+5] = [1,5,12,12,17]

我尝试使用 mapscanl 功能,但我没有得到我想要的,因为我添加了所有元素。

这正是scanl1 :: (a -> a -> a) -> [a] -> [a]的目的:

Prelude> scanl1 (+) [1,2,5,8,8,0,4,2]
[1,3,8,16,24,24,28,30]

scanl1 将函数 f :: a -> a -> a(此处为 (+))和 a 的列表作为输入。它构造一个列表,其中第一项是列表的第一项。这是 累加器 的第一个值。然后对于每个值,通过使用累加器和列表的下一个值调用 f 来更新累加器,然后产生该项目。

所以在 scal1 (+) [1,2,5] 的情况下,我们发出的第一个项目是 1,我们还将累加器设置为 1。下一项是 2,所以我们调用 (+) 1 2(即 3),这是结果和新的累加器,接下来我们调用 (+) ((+) 1 2) 5(即 8), 等等

但我认为使用递归作为练习会更好。就像我们使用累加器之前所说的那样。我们可以通过引入一个额外的函数来实现这一点,其中累加器是我们通过递归调用(和更新)传递的函数。所以在那种情况下它看起来像:

cumulativeAmount :: [Integer] -> [Integer]
cumulativeAmount [] = ...
cumulativeAmount (x:xs) = go x xs
    where go x xs = ...

所以这里 go (x) 的第一个参数是累加器。我把它留作练习,用递归实现它。

在我的脑海中,你可以通过列表理解来解决这个问题,就像这样:

cumulativeAmount xs = [ sum $ take x xs | x <- [1..length xs] ]

使用累加器怎么样:

cumulativeAmount :: (Num a) => [a] -> [a]
cumulativeAmount xs = go xs 0
    where go [] acc = []
          go (x:xs) acc = (acc+x) : go xs (acc+x)

其工作原理如下:

*Main> cumulativeAmount [1,2,5,8,8,0,4,2]
[1,3,8,16,24,24,28,30]

以上代码保持状态变量 acc 以在遇到新数字时累加总和,并将新总和添加到结果列表中。

现在一个很好的练习是用高阶函数替换上面的代码。