尝试使用新元素替换给定点的列表中的元素,然后返回该元素。
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
的连接似乎没有问题,因此不确定如何解决。
答案 0 :(得分:8)
您似乎误解了列表构造函数(:)
的作用。 Haskell列表是以空列表(:)
结尾的[]
构造函数序列,模式匹配只是以相同的顺序对这些构造函数进行反汇编。
因此,当您在(yf:(y:yl))
上进行模式匹配时,您真正匹配的是至少两个元素的列表,yf
和y
以及yl
为列表的其余部分。
推断类型不是您所期望的,因为length yf
暗示yf
- 这是输入列表的第一个元素 - 本身就是一个列表。
您需要做的是递归地沿着列表向下走,使用输入的当前元素或替换元素x
在到达正确位置时构建新列表。一般表单看起来应该像标准库函数map
,它实现如下:
map _ [] = []
map f (x:xs) = f x : map f xs
除非您需要一种方法来跟踪您正在搜索的索引,而不是转换每个元素。
如果应用于0或1个元素的列表,您当前的函数也会失败,但在整体修改算法后,修复它应该很容易。
答案 1 :(得分:0)
阅读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))
答案 2 :(得分:0)
除了模式匹配的问题(我也推荐C. A. McCann的答案),你的程序可能效率低于预期,当然效率低于预期。
问题在于Haskell的列表是简单的单链表,它们不以方便的O(1)可访问形式携带它们的长度。 Haskell的length
必须计算列表节点的数量,这需要O(N)时间。这意味着setelt
的直接修正版本(由Nicolas Dudebout的回答提供)将扫描每一步的剩余列表,产生O(N ^ 2)最坏情况的性能,而不是O(N)是可能的。
要解决此问题,请先扫描列表以获取长度。类似于以下实现,即O(N)(尽管使用take
和drop
最终会扫描列表的时间超过严格必要的次数):
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列表索引(由take
,drop
和!!
使用)从列表开头的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