简体   繁体   中英

Couldn't match expected type `[a]' with actual type `a'

I have the following function which should adds up all numbers in a given list up to the current position. For example, subtotal [1, 2, 3, 4] = [1, 3, 6, 10] , because 1 = 1, 1 + 2 = 3, 1 + 2 + 3 = 6 and 1 + 2 + 3 + 4 = 10.

Here's my code:

subtotal :: Num a => [a] -> [a]
subtotal [] = []
subtotal xs = [y | n <- [1..length xs], y <- sum(take n xs)]

The problem is Im getting this error:

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)]
  |                                              ^^

How can I fix it?

sum(take n xs) is not a list , so you can not iterate over it. In order to add the result of an expression to a list you put the expression in the head of the list comprehension :

subtotal :: Num a => [a] -> [a]
subtotal xs = [ | n <- [1..length xs]]

But you here use length . length is a bit a dangerous function since lists can be infinite, and then your function will simply keep looping, and never return an element. You can use inits however to obtain all the lists:

Prelude Data.List> inits [1,4,2,5]
[[],[1],[1,4],[1,4,2],[1,4,2,5]]

So you can use:



subtotal :: Num a => [a] -> [a]
subtotal xs = [ |  <- ]

But this is still not very effectively: now we calculate the sum of every sublist. But a problem here is that we calculate the sum of every list separately. This is weird since the sum of the i -th result is simply the sum of the i-1 -th result plus the i -th element of xs . We can use scanl1 :: (a -> a -> a) -> [a] -> [a] :

subtotal :: Num a => [a] -> [a]
subtotal xs = scanl1 (+) xs

or even more simple and elegant:

subtotal :: Num a => [a] -> [a]
subtotal = scanl1 (+)

In a list comprehension, the statement y <- ys means that ys is a list and y iterates over the elements of that list. So y <- sum (take n xs) means that sum produces a list and you iterate over that list. But sum only produces a single number, not a list; so you get an error.

You want to use let instead of <- to assign simple expressions:

subtotal xs = [y | n <- [1..length xs], let y = sum(take n xs)]

Or just put the expression directly into the head of the comprehension:

subtotal xs = [sum (take n xs) | n <- [1..length xs]]

PS: Your first case (for the empty list) is redundant. It's just a special case of the second.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM