【问题标题】:Haskell List concatenation Inferred TypeHaskell 列表连接推断类型
【发布时间】:2013-04-18 17:24:00
【问题描述】:

尝试用新元素替换列表中给定点的元素,然后返回该元素。

 setelt :: Int -> [a] -> a -> [a]
 setelt x (yf:(y:yl)) z
   | x == (length yf) = (yf:(z:yl))

导致此错误:

Inferred type is not general enough
Expression    : setelt
Expected type : Int -> [a] -> a -> [a]
Inferred type : Int -> [[a]] -> [a] -> [[a]]

yf:y:yl 的串联似乎没有问题所以不知道如何解决。

【问题讨论】:

  • 嗯,我找不到以另一种方式连接它们的方法。不知道如何实现++
  • 据我了解,(x:y...:z:xs)xyz 只能是单个元素(相对于 xs),不能是列表; xs 是一个列表

标签: list haskell inferred-type


【解决方案1】:

您似乎误解了列表构造函数(:) 的作用。 Haskell 列表是以空列表[] 结尾的(:) 构造函数序列,模式匹配只是以相同的顺序反汇编这些构造函数。

因此,当您在 (yf:(y:yl)) 上进行模式匹配时,您真正匹配的是至少包含两个元素的列表,yfy,以及 yl 作为列表的其余部分。

推断的类型不是您所期望的,因为length yf 暗示yf(输入列表的第一个元素)本身就是一个列表。

您需要做的是递归地遍历列表,当您到达正确的位置时,使用输入的当前元素或替换元素 x 构建一个新列表。一般形式应该类似于标准库函数map,其实现方式如下:

map _ [] = []
map f (x:xs) = f x : map f xs

除了您需要一种方法来跟踪您正在搜索的索引,而不是转换每个元素。

如果应用于包含 0 或 1 个元素的列表,您当前的函数也会失败,但在更正整个算法后应该很容易解决这个问题。

【讨论】:

    【解决方案2】:

    阅读 C. A. McCann 的回答以获得更多见解,特别是对于列表太短的问题。无需修改您的算法,您可以通过以下简单更改来修复您的代码:

     setelt :: Int -> [a] -> a -> [a]
     setelt x (yf:(y:yl)) z
       | x == (length (y:yl)) = (yf:(z:yl))
    

    可以简洁地重写:

     setelt :: Int -> [a] -> a -> [a]
     setelt x (yf:ys@(_:yl)) z
       | x == (length ys) = (yf:(z:yl))
    

    【讨论】:

      【解决方案3】:

      除了模式匹配的问题(我也推荐 C. A. McCann 的答案),您的程序可能没有您预期的效率,而且肯定没有它可能的效率。

      问题在于 Haskell 的列表是简单的单链表,它们不能以方便的、O(1) 可访问的形式携带它们的长度。 Haskell 的length 必须统计列表节点的数量,这需要 O(N) 时间。这意味着setelt 的直接更正版本(由 Nicolas Dudebout 的回答提供)将在每一步扫描剩余列表,产生 O(N^2) 最坏情况下的性能,而不是 O (N) 这是可能的。

      要解决此问题,请先扫描列表以获取长度。类似于以下实现,即 O(N)(尽管使用 takedrop 最终扫描列表的次数超过了严格必要的次数):

      setelt :: Int -> [a] -> a -> [a]
      setelt n ys z = front ++ z:back where
        count = length ys - n
        front = take count ys
        (_:back) = drop count ys
      

      最后,如果不清楚:标准的 Haskell 列表索引(由 takedrop!! 使用)从列表头部的 0 开始,而不是尾部的 1 (看起来可能是您对setelt 的意图,并在上面实现)。如果您的意图是从 0 开头,则实现更容易:

      setelt n ys z = front ++ z:back where
          front = take n ys
          (_:back) = drop n ys
      

      或者,更有效:

      setelt 0 (y:ys) z = z:ys
      setelt n (y:ys) z = y:setelt (n-1) ys z
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多