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]
我尝试使用 map
和 scanl
功能,但我没有得到我想要的,因为我添加了所有元素。
这正是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
以在遇到新数字时累加总和,并将新总和添加到结果列表中。
现在一个很好的练习是用高阶函数替换上面的代码。
我需要创建或了解 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]
我尝试使用 map
和 scanl
功能,但我没有得到我想要的,因为我添加了所有元素。
这正是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
以在遇到新数字时累加总和,并将新总和添加到结果列表中。
现在一个很好的练习是用高阶函数替换上面的代码。