繁体   English   中英

Haskell列表串联推断类型

[英]Haskell List concatenation Inferred Type

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

 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的串联似乎没有问题,因此不确定如何解决。

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

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

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

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

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

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

如果将当前函数应用于0或1个元素的列表,该函数也会失败,但是在整体上校正算法后,修复起来应该很容易。

阅读CA 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))

除了模式匹配的问题(我也建议CA 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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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