【问题标题】:Couldn't match expected type `[a]' with actual type `a'无法将预期类型“[a]”与实际类型“a”匹配
【发布时间】:2018-04-05 09:11:03
【问题描述】:

我有以下函数,它应该将给定列表中的所有数字加到当前位置。例如,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)]
  |                                              ^^

我该如何解决?

【问题讨论】:

  • 只需在sum(take n xs) 周围加上括号,就像在y &lt;- [sum(take n xs)] 中一样,你就可以了。另见thisthisthis

标签: list haskell types list-comprehension


【解决方案1】:

sum(take n xs)不是一个列表,所以你不能迭代它。为了将表达式的结果添加到列表中,您将表达式放在列表理解的头部

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

但是你在这里使用lengthlength 是一个有点危险的函数,因为列表可以是无限的,然后你的函数将继续循环,并且永远不会返回一个元素。但是,您可以使用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 个结果的总和只是第 i-1 个结果加上第 i 个结果的总和xs 的元素。我们可以使用scanl1 :: (a -&gt; a -&gt; a) -&gt; [a] -&gt; [a]:

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

甚至更简单优雅:

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

【讨论】:

    【解决方案2】:

    在列表推导中,y &lt;- ys 语句意味着 ys 是一个列表,y 迭代该列表的元素。所以y &lt;- sum (take n xs) 意味着sum 生成一个列表,然后您遍历该列表。但是sum 只产生一个数字,而不是一个列表;所以你得到一个错误。

    您想使用let 而不是&lt;- 来分配简单的表达式:

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

    或者直接把表达式放到推导式的头部:

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

    PS:您的第一个案例(对于空列表)是多余的。这只是第二种的特例。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-21
      • 2015-04-15
      相关资源
      最近更新 更多