在Haskell中如何在列表中后退?

在Haskell中如何在列表中后退?,haskell,Haskell,我需要为我的一个赋值编写一个简单的函数,该函数应该删除给定列表中的所有重复项,除了列表中第一次出现的元素。 以下是我写的: remDup :: [Int]->[Int] remDup []=[] remDup (x:xs) | present x xs==True = remDup xs | otherwise = x:remDup xs where present :: Int->[Int]->Bool present x [] = False

我需要为我的一个赋值编写一个简单的函数,该函数应该删除给定列表中的所有重复项,除了列表中第一次出现的元素。 以下是我写的:

remDup :: [Int]->[Int]
remDup []=[]
remDup (x:xs)
   | present x xs==True = remDup xs
   | otherwise = x:remDup xs
   where
   present :: Int->[Int]->Bool
   present x [] = False
   present x (y:ys)
       | x==y =True
       | otherwise = present x ys 
但是,除了元素的最后一次出现之外,此代码将删除重复项。 也就是说,如果给定的列表是
[1,2,3,3,2]
,它将生成
[1,3,2]
,而不是
[1,2,3]

如何以另一种方式执行此操作?

您可以将其反转,运行当前的复制删除程序,然后反转结果。

这个想法如何:

remDup [] = []
remDup (x:xs) = x :  remDup ( remove x xs )

其中
remove x xs
从列表xs中删除所有出现的x(实现留作练习)。

对于遇到的每个元素,您只需检查以前是否遇到过它;建立一组遇到的元素,并使用这些元素检查是否应该删除某个元素

remDup :: [Int] -> [Int]
remDup xs = helper S.empty xs
  where
    helper s [] = []
    helper s (x:xs) | S.elem x s = helper xs
                    | otherwise = x:helper (S.insert x s) xs

这就是我在遵循用户5402的建议后最终得出的结论

remDup1 [] = []
remDup1 (x:xs) = x:remDup1(remove x xs)

remove x []=[]
remove x (y:ys) 
   | x==y = remove x ys
   | x/=y = y:(remove x ys)

如果您关心效率,那么应该考虑使用
HashSet
作为辅助数据结构。这样,我们可以得到O(n logn)的平均案例复杂度,实际上是O(n)

我刚刚快速编写了一个程序,将此解决方案的性能与得票最高的解决方案进行比较:

import Data.List
import Data.Hashable
import Data.HashSet (HashSet)
import qualified Data.HashSet as HashSet

import Data.Time.Clock
import Control.DeepSeq

main :: IO ()
main = do
  let a = [1..20000] :: [Int]
  putStrLn "Test1: 20000 different values"
  test "remDup"    $ remDup a
  test "remDupSet" $ remDupSet a
  putStrLn ""
  let b = replicate 20000 1 :: [Int]
  putStrLn "Test2: one value repeted 20000 times"
  test "remDup"    $ remDup b
  test "remDupSet" $ remDupSet b

test :: (NFData a) => String -> a -> IO ()
test s a = do time1 <- getCurrentTime
              time2 <- a `deepseq` getCurrentTime
              putStrLn $ s ++ ": " ++ show (diffUTCTime time2 time1)

remDup :: (Eq a) => [a] -> [a]
remDup [] = []
remDup (x:xs) = x : remDup (delete x xs)

remDupSet :: (Hashable a, Eq a) => [a] -> [a]
remDupSet l = remDupSetAux HashSet.empty l
  where remDupSetAux :: (Hashable a, Eq a) => HashSet a -> [a] -> [a]
        remDupSetAux _ [] = []
        remDupSetAux s (x:xs) = if x `HashSet.member` s
                                  then remDupSetAux s xs
                                  else x : remDupSetAux (HashSet.insert x s) xs

旁注:
present x xs==True
相当于
present x xs
Why
reverse。核心。反向
nub
正是Abhinav Jain试图写的东西。不能按照赋值说明使用内置函数,这就是为什么试图通过简单函数和递归实现它。@recursion.ninja但他想要
[1,2,3]
——换句话说,他想要
nub
。我同意<代码>相反。核心。反向匹配他编写的函数,但他显然不喜欢他编写的函数的行为。@DanielWagner哦,我误读了他的问题,我认为问题不仅仅是简单的递归……我知道这会起作用,但必须有更有效的方法来正确执行。还有,对于Int,可以使用一个专门的高效函数。我检查了Data.List.nub,令人惊讶的是它也是O(n^2)。这里有一篇有趣的文章:。不幸的是,
Hashable
是邪恶和错误的。值得注意的是,散列
Int
(非常、非常、非常愚蠢)非常愚蠢,但它也会在不同的体系结构上进行不同大小的散列。你为什么这么说?我在源代码中看到的是
实例Hashable Int,其中hash=id
,这与Java没有什么不同。关于为什么“
Hashable
是邪恶的和错误的”?您是否有值得一读的参考资料?问题是,哈希表、哈希映射和哈希集的效率取决于密钥的分布是否尽可能均匀。使用identity函数进行“hashing”在这方面做得很糟糕。当然,您依赖于由hashing函数引起的密钥分布,但是它们通常比它们的树对应项快。甚至,在这个例子中,我开始使用
Set
,这是用树实现的,但是我改变了,因为
HashSet
更快。也因为说O(n)比O(n logn)看起来更好:-)你永远都不可能得到O(1)。哈希映射是基于树的。我相信分枝因子很大,所以树通常都很浅。你最后的守卫应该是微不足道的,以表明你没有错过任何案例。这通常是通过
来完成的,否则
,定义为
True
import Data.List
import Data.Hashable
import Data.HashSet (HashSet)
import qualified Data.HashSet as HashSet

import Data.Time.Clock
import Control.DeepSeq

main :: IO ()
main = do
  let a = [1..20000] :: [Int]
  putStrLn "Test1: 20000 different values"
  test "remDup"    $ remDup a
  test "remDupSet" $ remDupSet a
  putStrLn ""
  let b = replicate 20000 1 :: [Int]
  putStrLn "Test2: one value repeted 20000 times"
  test "remDup"    $ remDup b
  test "remDupSet" $ remDupSet b

test :: (NFData a) => String -> a -> IO ()
test s a = do time1 <- getCurrentTime
              time2 <- a `deepseq` getCurrentTime
              putStrLn $ s ++ ": " ++ show (diffUTCTime time2 time1)

remDup :: (Eq a) => [a] -> [a]
remDup [] = []
remDup (x:xs) = x : remDup (delete x xs)

remDupSet :: (Hashable a, Eq a) => [a] -> [a]
remDupSet l = remDupSetAux HashSet.empty l
  where remDupSetAux :: (Hashable a, Eq a) => HashSet a -> [a] -> [a]
        remDupSetAux _ [] = []
        remDupSetAux s (x:xs) = if x `HashSet.member` s
                                  then remDupSetAux s xs
                                  else x : remDupSetAux (HashSet.insert x s) xs
Test1: 20000 different values
remDup: 15.79859s
remDupSet: 0.007725s

Test2: one value repeted 20000 times
remDup: 0.001084s
remDupSet: 0.00064s