簡體   English   中英

從沒有elem的Haskell列表中刪除重復項

[英]Removing duplicates from a list in Haskell without elem

我正在嘗試定義一個從列表中刪除重復項的函數。 到目前為止,我有一個有效的實現:

rmdups :: Eq a => [a] -> [a]
rmdups [] = []
rmdups (x:xs)   | x `elem` xs   = rmdups xs
                | otherwise     = x : rmdups xs

但是,我想在不使用elem的情況下對其進行返工。 最好的方法是什么?

我想使用我自己的函數而不是nubnubBy來做到這一點。

您的代碼和nub都具有O(N^2)復雜性。

您可以將復雜度提高到O(N log N) ,並通過排序、分組和僅取每個組的第一個元素來避免使用elem

從概念上講,

rmdups :: (Ord a) => [a] -> [a]
rmdups = map head . group . sort

假設您從列表[1, 2, 1, 3, 2, 4]開始。 通過排序,你得到[1, 1, 2, 2, 3, 4] ; 通過分組,你得到[[1, 1], [2, 2], [3], [4]] ; 最后,通過獲取每個列表的頭部,你得到[1, 2, 3, 4]

上述的完整實現只涉及擴展每個功能。

請注意,這需要對列表元素有更強的Ord約束,並且還會更改它們在返回列表中的順序。

更容易。

import Data.Set 
mkUniq :: Ord a => [a] -> [a]
mkUniq = toList . fromList

O(n)時間內將集合轉換為元素列表:

 toList :: Set a -> [a]

O(n log n)時間內從元素列表創建一個集合:

 fromList :: Ord a => [a] -> Set a

在 python 中也不例外。

def mkUniq(x): 
   return list(set(x)))

與@scvalex 的解決方案相同,以下具有O(n * log n)復雜性和Ord依賴性。 與它不同的是,它保留了順序,保留了項目的第一次出現。

import qualified Data.Set as Set

rmdups :: Ord a => [a] -> [a]
rmdups = rmdups' Set.empty where
  rmdups' _ [] = []
  rmdups' a (b : c) = if Set.member b a
    then rmdups' a c
    else b : rmdups' (Set.insert b a) c

基准測試結果

基准測試結果

如您所見,基准測試結果證明此解決方案是最有效的。 您可以在此處找到此基准測試的來源。

我認為如果沒有elem (或您自己重新實現它),您將無法做到這一點。

但是,您的實現存在語義問題。 當元素重復時,您將保留最后一個。 就個人而言,我希望它保留第一個重復項並刪除其余項。

*Main> rmdups "abacd"
"bacd"

解決方案是將“可見”元素作為狀態變量貫穿。

removeDuplicates :: Eq a => [a] -> [a]
removeDuplicates = rdHelper []
    where rdHelper seen [] = seen
          rdHelper seen (x:xs)
              | x `elem` seen = rdHelper seen xs
              | otherwise = rdHelper (seen ++ [x]) xs

這或多或少是如何在標准庫中實現nub的(在此處閱讀源代碼)。 nub實現的微小差異確保它是非嚴格的,而上面的removeDuplicates是嚴格的(它在返回之前消耗整個列表)。

如果您不擔心嚴格性,這里的原始遞歸實際上是多余的。 removeDuplicates可以用foldl在一行中實現:

removeDuplicates2 = foldl (\seen x -> if x `elem` seen
                                      then seen
                                      else seen ++ [x]) []

現在回答這個問題為時已晚,但我想分享我的原創解決方案,不使用elem並且不要假設Ord

rmdups' :: (Eq a) => [a] -> [a]
rmdups' [] = []
rmdups' [x] = [x]
rmdups' (x:xs) = x : [ k  | k <- rmdups'(xs), k /=x ]

該解決方案在輸入結束時刪除重復項,而問題實現在開始時刪除。 例如,

rmdups "maximum-minimum"
-- "ax-nium"

rmdups' "maximum-minimum"
-- ""maxiu-n"

此外,此代碼復雜度為 O(N*K),其中 N 是字符串的長度,K 是字符串中唯一字符的數量。 N >= K 因此,在最壞的情況下它將是 O(N^2) 但這意味着字符串中沒有重復,這與您嘗試刪除字符串中的重復項不同。

使用遞歸方案

import Data.Functor.Foldable

dedup :: (Eq a) => [a] -> [a]
dedup = para pseudoalgebra
    where pseudoalgebra Nil                 = []
          pseudoalgebra (Cons x (past, xs)) = if x `elem` past then xs else x:xs

雖然這肯定更先進,但我認為它非常優雅,並展示了一些有價值的函數式編程范例。

Graham Hutton在 p 上有一個rmdups函數。 86 的Haskell 編程 它保持秩序。 如下。

rmdups :: Eq a => [a] -> [a]
rmdups [] = []
rmdups (x:xs) = x : filter (/= x) (rmdups xs)
rmdups "maximum-minimum"

“maxiu-n”

這一直困擾着我,直到我看到赫頓的功能。 然后,我又試了一次。 有兩個版本,第一個保留最后一個副本,第二個保留第一個。

rmdups ls = [d|(z,d)<- zip [0..] ls, notElem d $ take z ls]
rmdups "maximum-minimum"

“maxiu-n”

如果您想獲取列表的第一個而不是最后一個重復元素,就像您嘗試做的那樣,只需更改takedrop函數並將枚舉zip [0..]更改為zip [1..]

我想在@fp_mora 的回答中補充說,在 Haskell 編程的第 136 頁上,還有另一個略有不同的實現:

rmdups :: Eq a => [a] -> [a]
rmdups [] = []
rmdups (x : xs) = x : rmdups (filter (/= x) xs)

我更容易把頭繞在這個上面。

您也可以使用此壓縮功能。

cmprs ::Eq a=>[a] -> [a]
--cmprs [] = [] --not necessary
cmprs (a:as) 
    |length as == 1 = as
    |a == (head as) = cmprs as
    |otherwise = [a]++cmprs as

使用 dropWhile 也可以,但請記住在使用它之前對列表進行排序

rmdups :: (Eq a) => [a] -> [a]
rmdups [] = []
rmdups (x:xs) = x : (rmdups $ dropWhile (\y -> y == x) xs)
remdups xs = foldr (\y ys -> y:filter (/= y) ys) [] xs

這將函數應用於第一個元素和以相同方式遞歸構造的列表。 在第一次迭代中,基本上你創建一個列表,你只知道第一個元素,列表的其余部分以相同的方式構造(將元素添加到列表中),然后過濾以刪除特定循環的項目添加。

所以每次迭代都會向列表中添加一個元素(稱為X )並過濾列表,刪除所有元素 = X

...或通過使用來自 Data.List 的函數 union 應用於自身:

import Data.List

unique x = union x x
remove_duplicates (x:xs)
  | xs == []       = [x]
  | x == head (xs) = remove_duplicates xs
  | otherwise      = x : remove_duplicates xs

你可以嘗試這樣做。 我只是用我自己的實現替換了“elem”。 這個對我有用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM