haskell 列表中的唯一元素
Posted
技术标签:
【中文标题】haskell 列表中的唯一元素【英文标题】:unique elements in a haskell list 【发布时间】:2011-03-07 02:50:02 【问题描述】:好的,这可能会在前奏中出现,但是:是否有标准库函数用于查找列表中的唯一元素?为了澄清,我的(重新)实现是:
has :: (Eq a) => [a] -> a -> Bool
has [] _ = False
has (x:xs) a
| x == a = True
| otherwise = has xs a
unique :: (Eq a) => [a] -> [a]
unique [] = []
unique (x:xs)
| has xs x = unique xs
| otherwise = x : unique xs
【问题讨论】:
你的has
也是标准的;只是flip elem
。
甚至has xs = (`elem` xs)
。
@yatima2975 你为什么使用elem
作为中缀?
@dopatraman 因为elem
的类型为Eq a => a -> [a] -> Bool
,所以将其用作中缀操作部分会使xs
成为第二个参数。 (`elem` xs)
被取消为 (\x -> elem x xs)
这就是我们想要的!
【参考方案1】:
我认为这样就可以了。
unique [] = []
unique (x:xs) = x:unique (filter ((/=) x) xs)
【讨论】:
【参考方案2】:Data.List
中的 nub
函数(不,它实际上不在 Prelude 中)确实可以满足您的需求,但它与您的 unique
函数并不完全相同。它们都保留了元素的原始顺序,但unique
保留了最后一个
每个元素的出现次数,而nub
保留第一次出现。
你可以这样做让nub
的行为与unique
完全一样,如果这很重要的话(虽然我觉得它不重要):
unique = reverse . nub . reverse
另外,nub
仅适用于小型列表。
它的复杂性是二次方的,所以如果您的列表可以包含数百个元素,它就会开始变慢。
如果您将类型限制为具有 Ord 实例的类型,则可以使其扩展得更好。
nub
的这种变体仍然保留了列表元素的顺序,但其复杂性是 O(n * log n)
:
import qualified Data.Set as Set
nubOrd :: Ord a => [a] -> [a]
nubOrd xs = go Set.empty xs where
go s (x:xs)
| x `Set.member` s = go s xs
| otherwise = x : go (Set.insert x s) xs
go _ _ = []
其实一直是proposednubOrd
加Data.Set
。
【讨论】:
可以说最好将它作为一个集合而不是首先使用列表 老实说:nub
不适合任何列表。即使在有 2 个元素的列表中 nubOrd
也是 faster。
这有点像“地图筛”,类似于不纯的“哈希筛”。
函数类型签名中有错字。应该是Ord a
。此外,我发现 nubOrd 出人意料地(或没有)在我的情况下并不比 nub 更好。它甚至更慢。可能是因为没有太多重复值(尽管引入了 nub 将运行时间减少了一半,但 nub ord 比仅 nub 慢了大约 20%)。
@alternative,通过保持项目的顺序,列表上的函数比集合上的等效函数更普遍有用。这可能非常重要。【参考方案3】:
Haskell 中创建唯一列表的算法:
data Foo = Foo id_ :: Int
, name_ :: String
deriving (Show)
alldata = [ Foo 1 "Name"
, Foo 2 "Name"
, Foo 3 "Karl"
, Foo 4 "Karl"
, Foo 5 "Karl"
, Foo 7 "Tim"
, Foo 8 "Tim"
, Foo 9 "Gaby"
, Foo 9 "Name"
]
isolate :: [Foo] -> [Foo]
isolate [] = []
isolate (x:xs) = (fst f) : isolate (snd f)
where
f = foldl helper (x,[]) xs
helper (a,b) y = if name_ x == name_ y
then if id_ x >= id_ y
then (x,b)
else (y,b)
else (a,y:b)
main :: IO ()
main = mapM_ (putStrLn . show) (isolate alldata)
输出:
Foo id_ = 9, name_ = "Name"
Foo id_ = 9, name_ = "Gaby"
Foo id_ = 5, name_ = "Karl"
Foo id_ = 8, name_ = "Tim"
【讨论】:
【参考方案4】:另一种去除重复的方法:
unique :: [Int] -> [Int]
unique xs = [x | (x,y) <- zip xs [0..], x `notElem` (take y xs)]
【讨论】:
【参考方案5】:import Data.Set (toList, fromList)
uniquify lst = toList $ fromList lst
【讨论】:
这会改变元素的顺序。【参考方案6】:我认为 unique 应该返回在原始列表中只出现一次的元素列表;也就是说,原始列表中多次出现的任何元素都不应包含在结果中。
我可以建议一个替代定义,unique_alt:
unique_alt :: [Int] -> [Int]
unique_alt [] = []
unique_alt (x:xs)
| elem x ( unique_alt xs ) = [ y | y <- ( unique_alt xs ), y /= x ]
| otherwise = x : ( unique_alt xs )
以下是一些突出 unique_alt 和 unqiue 之间区别的示例:
unique [1,2,1] = [2,1]
unique_alt [1,2,1] = [2]
unique [1,2,1,2] = [1,2]
unique_alt [1,2,1,2] = []
unique [4,2,1,3,2,3] = [4,1,2,3]
unique_alt [4,2,1,3,2,3] = [4,1]
【讨论】:
这实际上是 Data.List.Unique (唯一)的定义,尽管我个人从未遇到过该用例,而“仅包含一个重复项的壁球列表”功能是许多操作的基础。【参考方案7】:我在Hoogle 上搜索了(Eq a) => [a] -> [a]
。
第一个结果是nub
(从列表中删除重复元素)。
Hoogle 很棒。
【讨论】:
另外,你可以像这样提供你自己的相等函数:nubBy :: (a -> a -> Bool) -> [a] -> [a] 如果 Bart 有时间,我们可能会看到一个 nubOrd,这在性能方面会更合理。 值得一提的是nub
函数来自Data.List
包。
@Thomas:Data.List.Unique 有 sortUniq,这是您请求的“nubOrd”。我宁愿有一个 (Eq a, Hashable a) => [a] -> [a] 这会更合理,性能方面......
直到现在,我一直在努力理解如何有效地使用 Hoogle,从来没有想过要搜索我正在寻找的类型签名以上是关于haskell 列表中的唯一元素的主要内容,如果未能解决你的问题,请参考以下文章