我正在编写一个函数,该函数采用Int
列表,一般类型a
的列表,并删除Int列表中具有索引的所有元素。
例如:removeEl [1,3,4] [1,2,3,4,5,6]
返回[1,3,6]
或removeEl [1,2] "Firefox"
返回"Fefox"
这是我的尝试:
removeEl :: [Int] -> [a] -> [a]
removeEl [] xs = []
removeEl ns [] = []
removeEl (n:ns) (x:xs) | n == 0 = removeEl ns xs
| n > 0 = x:removeEl (n-1) xs
我意识到(n-1)
是Int
,而不是[Int]
所以它失败了。我是否需要编写辅助功能才能使用?
答案 0 :(得分:3)
您需要传递一个新列表,但该列表中的每个项目都需要递减,因为您已经移动了第二个参数的每个元素的位置。无论您是否实际移除了头元素,都是如此。
removeEl (n:ns) (x:xs) | n == 0 = removeEl (map pred ns) xs
| n > 0 = x:removeEl (map pred (n:ns)) xs
有很多机会重构这一点,但我玩过的任何东西都没那么简单。
此外,您的第一个基本案例是错误的:索引列表中没有项目,您想要返回所有内容,而不是没有:
remove [] xs = xs
(请记住,只有当第一个参数单调递增时,这才有效。)
您还可以使用列表解析来避免显式递归:
removeEl ns xs = [x | (n,x) <- zip [0..] xs, not(n `elem` ns)]
答案 1 :(得分:3)
我相信您的算法是O(n*m)
,其中n
是列表中元素的数量,m
是要删除的索引数。它还有一个不幸的要求,即必须订购指数清单。这是一个~O(n*log(m))
解决方案,不需要订购索引。
import qualified Data.Set as Set
removeE1 indices xs = go 0 xs
where is = Set.fromList indices
go _ [] = []
go n (y:ys) | n `Set.member` is = go (n+1) ys
| otherwise = y : go (n+1) ys
使用哈希映射可以稍微改善一下。