我有以下功能,它应该将给定列表中的所有数字相加到当前位置。例如,subtotal [1, 2, 3, 4] = [1, 3, 6, 10]
,因为1 = 1,1 + 2 = 3,1 + 2 + 3 = 6和1 + 2 + 3 + 4 = 10.
这是我的代码:
subtotal :: Num a => [a] -> [a]
subtotal [] = []
subtotal xs = [y | n <- [1..length xs], y <- sum(take n xs)]
问题是我得到了这个错误:
cw.hs:3:46: error:
* Couldn't match expected type `[a]' with actual type `a'
`a' is a rigid type variable bound by
the type signature for:
subtotal :: forall a. Num a => [a] -> [a]
at cw.hs:1:1-31
* In the expression: sum (take n xs)
In a stmt of a list comprehension: y <- sum (take n xs)
In the expression:
[y | n <- [1 .. length xs], y <- sum (take n xs)]
* Relevant bindings include
xs :: [a] (bound at cw.hs:3:10)
subtotal :: [a] -> [a] (bound at cw.hs:2:1)
|
3 | subtotal xs = [y | n <- [1..length xs], y <- sum(take n xs)]
| ^^
我该如何解决?
答案 0 :(得分:7)
sum(take n xs)
不是列表,因此您无法对其进行迭代。为了将表达式的结果添加到列表中,您将表达式放在列表推导的头部:
subtotal :: Num a => [a] -> [a]
subtotal xs = [sum (take n xs) | n <- [1..length xs]]
但你在这里使用length
。 length
有点危险,因为列表可以是无限的,然后你的函数将简单地保持循环,并且永远不会返回一个元素。但是,您可以使用inits
来获取所有列表:
Prelude Data.List> inits [1,4,2,5]
[[],[1],[1,4],[1,4,2],[1,4,2,5]]
所以你可以使用:
import Data.List(inits)
subtotal :: Num a => [a] -> [a]
subtotal xs = [sum sub | sub <- tail (inits xs)]
但这仍然不是很有效:现在我们计算每个子列表的总和。但这里的一个问题是我们分别计算每个列表的总和。这很奇怪,因为 i -th结果的总和只是 i-1 -th结果加上 i -th的总和xs
的元素。我们可以使用scanl1 :: (a -> a -> a) -> [a] -> [a]
:
subtotal :: Num a => [a] -> [a]
subtotal xs = scanl1 (+) xs
甚至更简单优雅:
subtotal :: Num a => [a] -> [a]
subtotal = scanl1 (+)
答案 1 :(得分:4)
在列表推导中,语句y <- ys
表示ys
是一个列表,y
遍历该列表的元素。因此y <- sum (take n xs)
表示sum
生成一个列表,您迭代该列表。但sum
只生成一个数字,而不是列表;所以你得到一个错误。
您希望使用let
代替<-
来分配简单表达式:
subtotal xs = [y | n <- [1..length xs], let y = sum(take n xs)]
或者只是将表达式直接放入理解的头部:
subtotal xs = [sum (take n xs) | n <- [1..length xs]]
PS:你的第一个案例(对于空列表)是多余的。这只是第二种情况的一个特例。