List Haskell列表连接推断类型

List Haskell列表连接推断类型,list,haskell,inferred-type,List,Haskell,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] In

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

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

似乎没有问题,因此不知道如何解决。

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

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

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

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

map _ [] = []
map f (x:xs) = f x : map f xs
除了需要一种方法来跟踪正在搜索的索引,而不是转换每个元素


如果应用于包含0或1个元素的列表,当前函数也将失败,但在更正整个算法后,修复该函数应该很容易。

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

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

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

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

map _ [] = []
map f (x:xs) = f x : map f xs
除了需要一种方法来跟踪正在搜索的索引,而不是转换每个元素


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

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

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

除了模式匹配的问题(我也推荐C.A.McCann的答案),您的程序可能比您预期的效率低,当然也比它可能的效率低

问题是Haskell的列表是简单的单链接列表,不能以方便的O(1)可访问的形式显示其长度。Haskell的
length
必须计算列表节点的数量,这需要O(N)个时间。这意味着一个直接修正的
setelt
(由Nicolas Dudebout的答案提供)将在每个步骤扫描剩余列表,产生O(N^2)最坏情况性能,而不是可能的O(N)

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

最后,如果不清楚的话:标准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

除了模式匹配的问题(我也推荐C.A.McCann的答案),您的程序可能比您预期的效率低,当然也比它可能的效率低

问题是Haskell的列表是简单的单链接列表,不能以方便的O(1)可访问的形式显示其长度。Haskell的
length
必须计算列表节点的数量,这需要O(N)个时间。这意味着一个直接修正的
setelt
(由Nicolas Dudebout的答案提供)将在每个步骤扫描剩余列表,产生O(N^2)最坏情况性能,而不是可能的O(N)

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

最后,如果不清楚的话:标准Haskell列表索引(如
take
drop
!!
)从列表开头的0开始,而不是结尾的1(这可能是您在
setelt
中使用的,并且在上面实现)。如果您的目的是从0开头,那么实现就更容易了:

setelt n ys z = front ++ z:back where
    front = take n ys
    (_:back) = drop n ys
或者,更多