【问题标题】:Delete multiple elements from list based on position根据位置从列表中删除多个元素
【发布时间】:2013-09-25 12:52:58
【问题描述】:

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

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]

【问题讨论】:

  • 以 deleteElem 作为运算符的双重映射。但这也不起作用

标签: list haskell dictionary


【解决方案1】:

如果我对您的问题的解释正确,那么您是说您希望有一种方法可以将您的函数应用于索引列表,而不是一次应用于单个索引。

我能做到的最简单的方法是创建另一个名为 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 反复调用deleteElem 以从zs 中删除xs 中的索引。有关foldr 的更深入解释,请参阅How does foldr work?

更新:

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

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

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

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

另外,我仍然绝对推荐查看How does foldr work?

【讨论】:

  • 试用deleteElems [2, 4, 6] [1..10] == deleteElems [6, 4, 2] [1..10]。是一样的吗?应该一样吗?
  • 你可以将Indices-List降序排列,这样问题也就解决了。
【解决方案2】:

让我们将删除位置 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 修剪。如果在索引列表中多次指定同一索引,则需要过滤空列表。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-09-20
    • 2010-10-04
    • 1970-01-01
    • 2017-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多