我正在尝试定义一个从列表中删除重复项的函数。到目前为止,我有一个有效的实施方案:
rmdups :: Eq a => [a] -> [a]
rmdups [] = []
rmdups (x:xs) | x `elem` xs = rmdups xs
| otherwise = x : rmdups xs
但是我想在不使用elem
的情况下重做这件事。对此最好的方法是什么?
我想使用自己的功能,而不是nub
或nubBy
。
答案 0 :(得分:53)
您的代码和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
约束,并且还会在返回的列表中更改它们的顺序。
答案 1 :(得分:39)
更容易。
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)))
答案 2 :(得分:25)
与@ 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
如您所见,基准测试结果证明此解决方案最有效。 您可以找到此基准here的来源。
答案 3 :(得分:21)
如果没有elem
(或者您自己重新实施),我认为您无法做到这一点。
但是,您的实现存在语义问题。当元素重复时,你将保留最后一个。就个人而言,我希望它保留第一个重复的项目,然后放弃其余项目。
*Main> rmdups "abacd"
"bacd"
解决方案是将'see'元素作为状态变量进行处理。
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
在标准库中的实现方式(阅读源here)。 nub
实现中的微小差异确保它是non-strict,而上面的removeDuplicates
是严格的(它在返回之前会占用整个列表)。
如果你不担心严格,原始递归在这里实际上是过度的。可以使用removeDuplicates
:
foldl
removeDuplicates2 = foldl (\seen x -> if x `elem` seen
then seen
else seen ++ [x]) []
答案 4 :(得分:1)
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
虽然这肯定更先进,但我认为它非常优雅,并展示了一些有价值的函数式编程范例。
答案 5 :(得分:1)
现在回答这个问题为时已晚,但我希望在不使用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),但这意味着字符串中没有重复,这与您尝试删除字符串中的重复项不同。
答案 6 :(得分:1)
您也可以使用此压缩功能。
cmprs ::Eq a=>[a] -> [a]
--cmprs [] = [] --not necessary
cmprs (a:as)
|length as == 1 = as
|a == (head as) = cmprs as
|otherwise = [a]++cmprs as
答案 7 :(得分:0)
Graham Hutton 在p上有rmdups
个函数。 在Haskell编程中的86。它保留了秩序。它如下。
rmdups :: Eq a => [a] -> [a]
rmdups [] = []
rmdups (x:xs) = x : filter (/= x) (rmdups xs)
rmdups "maximum-minimum"
&#34; maxiu正&#34;
在看到赫顿的功能之前,这一直困扰着我。然后,我再次尝试。有两个版本,第一个保留最后一个副本,第二个保留第一个。
rmdups ls = [d|(z,d)<- zip [0..] ls, notElem d $ take z ls]
rmdups "maximum-minimum"
&#34; maxiu正&#34;
如果您想要获取列表中的第一个而不是最后一个重复元素,就像您尝试的那样,只需将take
更改为函数中的drop
并更改枚举{{1转到zip [0..]
。
答案 8 :(得分:0)
使用dropWhile也可以,但是请记住在使用此功能之前对列表进行排序
rmdups :: (Eq a) => [a] -> [a]
rmdups [] = []
rmdups (x:xs) = x : (rmdups $ dropWhile (\y -> y == x) xs)
答案 9 :(得分:0)
remove_duplicates (x:xs) | xs == [] = [x] | x == head (xs) = remove_duplicates xs | otherwise = x: remove_duplicates xs
您可以尝试执行此操作。我只是用自己的实现替换了“ elem”。它对我有用。
答案 10 :(得分:0)
我想在@fp_mora 的回答中补充说,在 Haskell 编程 的 第 136 页 上还有另一个稍微不同的实现:
rmdups :: Eq a => [a] -> [a]
rmdups [] = []
rmdups (x : xs) = x : rmdups (filter (/= x) xs)
我更容易围绕这一点。
答案 11 :(得分:-1)
...或者使用应用于自身的Data.List中的函数union:
@Entity
@Table(name = "office")
public class Office extends AppModel{
@Id
public Long id;
@Constraints.Required(message = "asdfasfd")
public String name;
...
}