Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
List 在没有元素的情况下从Haskell中的列表中删除重复项_List_Haskell_Recursion - Fatal编程技术网

List 在没有元素的情况下从Haskell中的列表中删除重复项

List 在没有元素的情况下从Haskell中的列表中删除重复项,list,haskell,recursion,List,Haskell,Recursion,我试图定义一个函数,它将从列表中删除重复项。到目前为止,我有一个有效的实施方案: rmdups :: Eq a => [a] -> [a] rmdups [] = [] rmdups (x:xs) | x `elem` xs = rmdups xs | otherwise = x : rmdups xs 但是,我想在不使用elem的情况下对其进行返工。最好的方法是什么 我想用我自己的函数来做这件事,而不是nub或nubBy如果没有el

我试图定义一个函数,它将从列表中删除重复项。到目前为止,我有一个有效的实施方案:

rmdups :: Eq a => [a] -> [a]
rmdups [] = []
rmdups (x:xs)   | x `elem` xs   = rmdups xs
                | otherwise     = x : rmdups xs
但是,我想在不使用
elem
的情况下对其进行返工。最好的方法是什么


我想用我自己的函数来做这件事,而不是
nub
nubBy

如果没有
elem
(或您自己的重新实现),我认为您无法做到这一点

但是,您的实现存在语义问题。复制元素时,保留最后一个元素。就我个人而言,我希望它保留第一个重复的项目,而放弃其余的项目

*Main> rmdups "abacd"
"bacd"
解决方案是将“seen”元素作为状态变量贯穿其中

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
则是严格的(它在返回之前会消耗整个列表)

如果您不担心严格性的话,这里的原始递归实际上是过度使用了<代码>移除的副本可以在一行中使用
foldl实现:

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

您的代码和
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
约束,并更改它们在返回列表中的顺序。

与@scvalex的解决方案相同,以下解决方案具有
O(n*logn)
复杂性和
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
基准结果

如您所见,基准测试结果证明此解决方案是最有效的。 你可以找到这个基准的来源。

甚至更容易

import Data.Set 
mkUniq :: Ord a => [a] -> [a]
mkUniq = toList . fromList
在O(n)时间内将集合转换为元素列表:

在O(n log n)时间内从元素列表创建一个集合:

在python中也一样

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

…或使用函数union from Data.List应用于自身:

import Data.List

unique x = union x x
使用:


虽然这肯定更高级,但我认为它相当优雅,展示了一些有价值的函数式编程范例。

回答这个问题为时已晚,但我想与大家分享我的解决方案,它是不使用
elem
的原创解决方案,并且不假设
Ord

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

此外,代码复杂度为O(N*K),其中N是字符串的长度,K是字符串中唯一字符的数量。N>=K因此,在最坏的情况下,它将是O(N^2),但这意味着字符串中没有重复,这与尝试删除字符串中的重复项不同。

在p上有一个
rmdups
函数。在Haskell中编程的86个。它维持秩序。具体如下

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"

rmdups ls=[d |(z,d)您也可以使用这个压缩函数

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)

您可以尝试这样做。我只是用自己的实现替换了“elem”。它对我很有用。

我想在@fp_mora中添加一个答案,在Haskell编程的第136页上,有另一个稍微不同的实现:

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

这对我来说更容易理解。

非常好,但请注意,这对列表元素设置了
Ord
限制,而不仅仅是
Eq
,并且顺序没有保留。这一点很好。注意了这一点以及语义上的其他更改。提供了一个拷贝粘贴就绪、稳定(保留顺序)的功能@scvalex建议使用
Ord
的变体。它还包含
\`、
union`和
intersect
@BradStevenson的类似物。这些解决方案基于非常低效的操作,
元素
函数和
(++)
都有一个
O(n)
列表中的复杂性。虽然Haskell的惰性保护了算法在每个周期上执行
(++)
,但与其他答案中给出的替代实现相比,这些实现仍然存在很大差距。请参阅。我认为
RemovedUpplicates3=foldr(\x seen->if x`elem`seen然后seen else x:seen)[
运行速度比移除的副本2快,如
(:)
操作是恒定的。+1.
Set
对于大输入来说绝对是一种更有效的数据结构。我想看看标准库的
nub
在这个图中的位置-懒惰对性能有影响吗?嘿。我本来打算用
Set
插入编写并提交一个版本,但你打败了我o it.很好的展示。优雅,但我不认为Set保留了顺序。是的,他在他的OP中没有提到顺序保留。顺便说一句,我误点击了向下投票(我想向上投票),现在我的投票被锁定,因为我之前上传了它,但它没有显示…编辑答案(用任何东西)所以我可以给你投票。当我再次在谷歌上搜索时,链接到…“第一个列表的重复项和元素将从第二个列表中删除,
rmdups ls = [d|(z,d)<- zip [0..] ls, notElem d $ take z ls]
rmdups "maximum-minimum"
cmprs ::Eq a=>[a] -> [a]
--cmprs [] = [] --not necessary
cmprs (a:as) 
    |length as == 1 = as
    |a == (head as) = cmprs as
    |otherwise = [a]++cmprs as
rmdups :: (Eq a) => [a] -> [a]
rmdups [] = []
rmdups (x:xs) = x : (rmdups $ dropWhile (\y -> y == x) xs)
remove_duplicates (x:xs)
  | xs == []       = [x]
  | x == head (xs) = remove_duplicates xs
  | otherwise      = x : remove_duplicates xs