在 Haskell 中将元素插入到给定索引的列表中



函数必须像这样:insertElemAt :: a -> [Int] -> [a] -> [a].

例子:

insertElemAt 0 [2,5,9] [1..10] 
= [1, 0, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9, 10]
insertElemAt 0 [1,2,4,8] [0,1,0,0,1,1,0,1] 
= [0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1]

我只知道初学者Haskell(if管道|和递归),但我尽力解决这个问题,但它从未奏效。这是我最近的尝试:

insertElemAt x [0] ys = ys
insertElemAt x [1,2] ys = x:x:ys
insertElemAt x [] ys = ys
insertElemAt x xs [] = []
insertElemAt x (n:ns) (y:ys) = y:insertElemAt x (n-1:ns) ys  

我也尝试过这样的东西,但这似乎很混乱,我认为第一个更好:

insertElemAt :: a -> [Int] -> [a]-> [a] 
insertElemAt x [0] ys = ys
insertElemAt x [1,2] ys = x:x:ys
insertElemAt x (n:ns) (y:ys) = y:insertElemAt x (map reduc (n:ns)) ys 
reduc (n:ns) = n-1 : reduc ns

也许我的模式不好?我试图用很多方式来写它们。

我还必须能够使用这个函数并在一个名为insertElemsAt (:: [(a, Int)] -> [a] -> [a])的函数中使用它,它必须是上述函数的"通用"版本。所以我必须能够给出我想插入什么样的元素的位置。

由于我做不到第一个,所以我对这个更加迷茫。下面是示例。我不知道如何使用管道if-s 和递归来做到这一点:

insertElemsAt (zip [0,1,1] [2,5,9]) [1..10] 
= [1, 0, 2, 3, 1, 4, 5, 6, 1, 7, 8, 9, 10]
insertElemsAt (zip [0,1,1,1] [1,2,4,8]) [0,1,0,0,1,1,0,1] 
= [0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1]

有人可以向我解释如何以最简单的方式做到这一点吗?提前感谢您的任何帮助!

假设索引列表是排序的,就像@AndyG所说的那样,如果你跟踪当前索引可能会有所帮助。因此,您可以实现如下函数:

insertElemAt :: a -> [Int] -> [a] -> [a]
insertElemAt x = go 0
where go _ [] ys = ys         -- (1)
go i ns [] = …          -- (2)
go i (n:ns) (y:ys) = …  -- (3)

您需要填写部分的地方。

因此,这里的go是一个使用递归插入元素的函数。基本情况 (1) 是要插入元素的索引列表用尽的地方,在这种情况下,我们可以返回列表本身。

如果列表本身已用尽,情况 (2),但仍有项目需要填写,您将需要创建一个列表,在其中重复x您仍然需要插入的项目数。

最后一种情况(3)您必须检查索引i与需要插入元素的第一个索引的比较n。如果它相等,那么您将不得不插入元素x并进行递归(您应该在其中调用go)。如果索引大于当前索引,则生成元素y,并在列表尾部递归并递增 acculator 索引。

你可以从你已经知道的东西开始:

insertElemAt 0 [
2,       5,          9] 
[1,    2, 3,    4, 5, 6,    7, 8, 9, 10] 
= [1, 0, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9, 10]

这是一个完全有效的定义,即使对于一个非常具体的情况,它甚至会产生正确的结果,并且在其他任何地方都有所不同。

但是我们可以稍微概括一下,因为

insertElemAt _0 [
2,         5,           9] 
[1,     2, 3,     4, 5, 6,     7, 8, 9, 10] 
= [1, _0, 2, 3, _0, 4, 5, 6, _0, 7, 8, 9, 10]

还有更多,

insertElemAt _0 [
2,         5,           9] 
[a,     b, c,     d, e, f,     g, h, i, j ] 
= [a, _0, b, c, _0, d, e, f, _0, g, h, i, j ]

我们可以添加一些显然也必须成立的特殊情况,例如

insertElemAt _0 [
2,         5] 
[a,     b, c,     d, e, f,     g, h, i, j ] 
= [a, _0, b, c, _0, d, e, f,     g, h, i, j ]

insertElemAt _0 [
2] 
[a,     b, c,     d, e, f,     g, h, i, j ] 
= [a, _0, b, c,     d, e, f,     g, h, i, j ]

甚至

insertElemAt _0 [
] 
[a,     b, c,     d, e, f,     g, h, i, j ] 
= [a,     b, c,     d, e, f,     g, h, i, j ]

我们可以进一步概括这一点,如

insertElemAt _0 [] xs
= xs

很明显,

insertElemAt _0 [
2] 
[a,     b, c,     d, e, f,     g, h, i, j ] 
=
a : insertElemAt _0 [
1]
[b, c,     d, e, f,     g, h, i, j ]

insertElemAt _0 [
2,         5] 
[a,     b, c,     d, e, f,     g, h, i, j ] 
=
a : insertElemAt _0 [
1,         4]
[b, c,     d, e, f,     g, h, i, j ]

总的来说

insertElemAt _0 (i:is) (x:xs)
| i > 1  =  x : insertElemAt _0 (minus1 (i:is)) xs

必须成立,那么我们为什么不把它也作为我们定义的一部分;

-- minus1 is = [i-1 | i <- is]
minus1 []      = _____
minus1 (i:is) = ( ____ - 1) : ______ is

(填空)。所以对于i==1的情况,

insertElemAt _0 (i:is) (x:xs)
| i == 1  =  x : _0 : insertElemAt _0 (minus1 (_____)) xs
-- or is it
--             _0 : x : ........   ?

所以你已经走到了一半。剩下要做的就是添加几个边缘情况,你就完成了。

现在它不会是最有效的版本,因为它会反复从索引列表的每个成员中减去1;你可以通过添加一个新的辅助参数来简化这部分,这将消除从索引列表的每个元素中减去的需要,并用一个加法替换它,向上计数而不是向下倒计时。若要使用该附加参数,必须定义并使用辅助的嵌套函数定义。

最新更新