2

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

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]

4

2 回答 2

3

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

我能做到的最简单的方法是创建另一个函数, deleteElems而不是deleteElem(注意尾随的s.)

deleteElems将是类型[Int] -> [a] -> [a],它将调用每个索引。

注意:请参阅底部的更新以获取正确的解决方案(我将这部分留在此处,以便其他人可以从我最初解决问题的尝试中学习以及为什么它不正确。)

这是一种方法:

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重复调用deleteElemxs从中删除索引zs。有关 的更深入解释foldr,请参阅foldr 如何工作?.

更新:

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

我使用以下函数获得预期行为(从原始列表中删除给定索引)的一种方法:intersectData.List

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

这个新版本deleteElems使用中的deleteElem每个索引来应用xs,创建一个包含length xs多个函数列表的列表,这些deleteElem函数列表是针对每个特定索引的。然后map ($ zs)将每个柯里deleteElem化函数应用于zs,产生列表列表,其中每个内部列表仅deleteElem应用于索引和 之一zs。最后,我们使用intersectfromData.List来查找删除了所有正确元素的列表。

另外,我仍然绝对推荐查看foldr 是如何工作的?.

于 2013-09-25T13:11:37.163 回答
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在指定位置拆分列表。Notexs:ts表示所有迭代中的“第二部分”r:rs列表,并且是所有迭代中的“第一部分”列表。显然,r是第一个“第一部分”,并且是完整包含在内的。其余部分用 修剪tail。如果在索引列表中多次指定相同的索引,则需要过滤空列表。

于 2013-09-25T22:25:48.980 回答