如何在Haskell中有效地过滤一对列表?

时间:2013-12-19 20:52:29

标签: haskell

我正在研究Programming Praxis上的一个简单问题:从列表中删除所有重复项而不更改顺序。假设元素属于Ord类,我想出了以下内容:

import Data.Set (Set)  
import qualified Data.Set as Set

buildsets::Ord a => [a] -> [Set a]
buildsets = scanl (flip Set.insert) Set.empty

nub2::Ord a => [a] -> [a]
nub2 thelist = map fst $ filter (not . uncurry Set.member) (zip thelist (buildsets thelist))

正如你所看到的,构建集函数在很大程度上解决了我的问题,但是将所有内容组合在一起的最后一步(nub2)看起来非常可怕。有没有更简洁的方法来实现这一目标?

4 个答案:

答案 0 :(得分:3)

由于我们必须过滤列表,我们应该使用一些集合来保存记录,我们不妨将filterM与状态monad一起使用:

import qualified Data.Set as S
import Control.Monad.State.Strict

nub2 :: Ord a => [a] -> [a]
nub2 = (`evalState` S.empty) . filterM go where
    go x = state $ \s -> if S.member x s
        then (False, s)
        else (True, S.insert x s) 

如果我想对这个功能进行一些打击,我会发现以下内容:

import Control.Arrow (&&&)

nub2 = (`evalState` S.empty) . filterM (\x -> state (S.notMember x &&& S.insert x))

答案 1 :(得分:2)

简单的递归对我来说没问题。

> g xs = go xs S.empty where
>   go [] _ = []
>   go (x:xs) a | S.member x a = go xs a
>               | otherwise =  x:go xs (S.insert x a)

答案 2 :(得分:0)

直接基于Sassa NF的建议,但清洁度略有改变:

g x = catMaybes $ unfoldr go (Set.empty, x)
  where
    go (_,[]) = Nothing
    go (s,(x:xs)) = Just (if Set.member x s then Nothing else Just x,
                          (Set.insert x s, xs))

答案 3 :(得分:0)

有时它会真正清理代码以提取和命名子项。 (在某些方面,这确实是Haskell评论代码的方式)

这比你上面所做的更啰嗦,但我觉得它更容易理解......

首先我从一些定义开始:

type Info=([Int], S.Set Int) --This is the remaining and seen items at a point in the list
item=head . fst --The current item
rest=fst --Future items
seen=snd --The items already seen

然后我添加了两个自描述辅助函数:

itemHasBeenSeen::Info->Bool
itemHasBeenSeen info = item info `S.member` seen info

moveItemToSet::Info->Info
moveItemToSet info = (tail $ rest info, item info `S.insert` seen info)

这个程序变成了:

nub2::[Int]->[Int]
nub2 theList = 
  map item 
  $ filter (not . itemHasBeenSeen) 
  $ takeWhile (not . null . rest) 
  $ iterate moveItemToSet start
    where start = (theList, S.empty)

从下到上阅读(就像数据流一样),您可以轻松地看到它发生了什么:

  1. start=(theList, S.empty),从完整列表开始,以及一个空集。

  2. iterate moveItemToSet start,重复将列表的第一项移动到集合中,将Data中的每次迭代保存在数组中。

  3. takeWhile (not . null . rest) - 当元素用尽时停止迭代。

  4. filter (not . itemHasBeenSeen) - 删除已经看过的项目。

  5. map item - 丢弃帮助者值....