我想从列表中删除一些元素。我到目前为止还有一个删除功能:
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]
答案 0 :(得分: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
重复调用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 xs
个deleteElem
个函数列表的列表具体指数。然后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
修剪。如果在索引列表中多次指定相同的索引,则需要过滤空列表。