根据位置从列表中删除多个元素

时间:2013-09-25 12:52:59

标签: list haskell dictionary

我想从列表中删除一些元素。我到目前为止还有一个删除功能:

deleteElem :: Int -> [a] -> [a]
deleteElem _ [] = []
deleteElem x zs | x > 0 = take (x-1) zs ++ drop x zs
                | otherwise = zs

我想删除两个位置,这些位置存储在元素列表的列表中。 所以这很好用:

map (deleteElem 2) [["hello", "whatever", "foo", "bar"], ["hello", "whatever", "foo", "bar"], ["hello", "whatever", "foo", "bar"], [hello", "whatever", "foo", "bar"]]

我会得到结果:

[["hello", "whatever", "bar"], ["hello", "whatever", "bar"], ["hello", "whatever", "bar"], [hello", "whatever", "bar"]]

但现在我想申请deleteElem [2,3]

2 个答案:

答案 0 :(得分:3)

如果我正确地解释你的问题,你说你想要一种方法将你的函数应用于索引列表而不是一次只有一个索引。

我可以做的最简单的方法是创建另一个名为的函数 deleteElems代替deleteElem(注意尾随s。)

deleteElems的类型为[Int] -> [a] -> [a],它会调用每个索引。

注意:请参阅底部的UPDATE以获取正确的解决方案(我将此部分留在此处,以便其他人可以从我原来尝试解决问题及其不正确的原因中学习。)

这是一种方法:

deleteElems xs zs = foldr (\x z -> deleteElem x z) zs xs

可以缩短为:

deleteElems xs zs = foldr deleteElem zs xs

Prelude> deleteElems [2,3] [1..10]
[1,4,5,6,7,8,9,10]

或来自您的示例:

Prelude> map (deleteElems [2,3]) [["hello", "whatever", "foo", "bar"], ["hello", "whatever", "foo", "bar"], ["hello", "whatever", "foo", "bar"], ["hello", "whatever", "foo", "bar"]]
[["hello","bar"],["hello","bar"],["hello","bar"],["hello","bar"]]

deleteElems使用foldr重复调用deleteElem以从xs中移除zs中的索引。有关foldr的更深入说明,请参阅How does foldr work?

<强>更新

根据评论,deleteElems的上述实现实际上是不正确的,因为当给出索引列表时,比如[2,4,6],它将首先删除索引2,返回一个新列表,然后删除 new 列表中的索引4并返回较新的列表,然后删除较新列表中的索引6 。此过程不是可交换,这意味着更改索引的顺序,或者给deleteElems索引[6,4,2]不会做同样的事情。

我可以使用intersect中的Data.List函数获取预期的行为(从原始列表中删除指定的索引):

deleteElems xs zs = foldr1 intersect $ map ($ zs) $ map deleteElem xs

deleteElems的新版本使用deleteElem中的每个索引应用xs,创建length xsdeleteElem个函数列表的列表具体指数。然后map ($ zs)将每个咖喱deleteElem函数应用于zs,产生一个列表列表,其中每个内部列表仅deleteElem应用于其中一个索引,{ {1}}。最后,我们使用zs中的intersect来查找删除了所有正确元素的列表。

此外,我仍然建议您查看How does foldr work?

答案 1 :(得分:0)

让我们定义删除位置i处的元素作为在位置i处拆分列表,然后重新组合列表而不包含列表第二部分的头元素。无论如何,这都是你实现的。

现在,删除多个元素就像使用相同的过程从列表的第二部分删除元素 - 如果我们可以指定相对于第二部分的头部的其余索引。

这需要deleteElems的简单定义:

deleteElems is = del [i-p | (p:i:_) <- tails $ sort $ 0:is] where
  del [] xs = xs
  del is xs = (r++) $ concatMap tail ss where
     (r:rs,ts) = unzip $ zipWith splitAt is $ xs:ts
     ss = filter (not . null) $ rs ++ [last ts]

此处列表推导构建了相对于先前索引的索引列表。然后del使用zipWith splitAt将列表拆分到指定的位置。注意xs:ts代表所有迭代的“第二部分”列表,r:rs是所有迭代的“第一部分”列表。显然,r是第一个“第一部分”,并且包含完整。其余部分用tail修剪。如果在索引列表中多次指定相同的索引,则需要过滤空列表。