删除重复但保留订单

时间:2014-04-21 07:14:06

标签: list haskell functional-programming

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]注意,我不想使用内置函数。

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)

如果你之前看到过,那么你想忽略那些后来出现的元素,然后你需要记录你所看到的内容,看起来像foldlfoldl'就是你想要的。 / 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