rmdup :: [Int] -> [Int]
rmdup [] = []
rmdup (x:xs) | x `elem` xs = rmdup xs
| otherwise = x: rmdup xs
上面的代码从Integer列表中删除了副本,但它删除了第一个匹配项,并保留了第二个匹配项。例如:
rmdup [1,2,3,1,4]
将导致:
[2,3,1,4]
如何更改它以保持订单并产生此结果:[1,2,3,4]
? 注意,我不想使用内置函数。
答案 0 :(得分:4)
以下怎么样?这避免了疯狂低效的acc ++ [x]
,也避免了两次反转给定列表:
rmdup :: Eq a => [a] => [a]
rmdup xs = rmdup' [] xs
where
rmdup' acc [] = []
rmdup' acc (x:xs)
| x `elem` acc = rmdup' acc xs
| otherwise = x : rmdup' (x:acc) xs
答案 1 :(得分:2)
实现所需要的一种方法是以相反的顺序传递输入列表,并在计算完成后再传递一次,然后再次反转结果。虽然,这种解决方案效率不高。
rmdup :: [Int] -> [Int]
rmdup xs = reverse $ rmdup' (reverse xs)
where
rmdup' [] = []
rmdup' (x:xs) | x `elem` xs = rmdup' xs
| otherwise = x: rmdup' xs
演示:
ghci> rmdup [1,2,3,1,4]
[1,2,3,4]
答案 2 :(得分:1)
如果你之前看到过,那么你想忽略那些后来出现的元素,然后你需要记录你所看到的内容,看起来像foldl
或foldl'
就是你想要的。 / p>
这是一个可能的实现:
import Data.List (foldl')
rmdup :: (Eq a) => [a] -> [a]
rmdup = foldl' step []
where step acc x
| x `elem` acc = acc
| otherwise = acc++[x]
答案 3 :(得分:1)
由于elem
是O(n),因此基于使用它来检查每个元素的解决方案是O(n ^ 2)。
复制问题的“标准”有效解决方案是在检查重复之前对列表进行排序。在这里,由于我们需要保留元素,我们必须更加小心。
import Data.List
import Data.Ord
rmdupSorted :: Eq b => [(a,b)] -> [(a,b)]
rmdupSorted (x@(_,xb):xs@((_,yb):_)) | xb == yb = rmdupSorted xs
| otherwise = x : rmdupSorted xs
rmdupSorted xs = xs -- 0 or 1 elements
rmdup :: Ord a => [a] -> [a]
rmdup = map snd . sort . rmdupSorted . sortBy (comparing snd) . zip [0..]
main = print $ rmdup [1,2,3,4,5,4,6,1,7]
假设sortBy
函数是稳定排序,rmdup
函数将删除任何元素的所有重复出现,但对于最后出现的元素。如果sortBy
不稳定,则rmdup
将删除所有匹配项,但对于未指定的匹配项(即rmdup [1,2,1]
可能会返回[1,2]
而不是[2,1]
。)
复杂性现在是O(n log n)。
我们现在需要在没有库函数的情况下重写上面的内容,就像OP请求的那样。我将此作为练习留给读者。 :-P