如何使用模式匹配基于Haskell中的类似键值来首次出现元组?

时间:2017-05-07 09:31:10

标签: haskell pattern-matching

我试图根据元组的第一个值放弃第一次出现的元组。 我的计划是遍历元组列表,看看密钥是否存在于列表的其余部分,如果它存在,则返回列表的其余部分并查找其他重复项,否则将元素添加到返回列表中。

duplicate [(x,_):xs]= if (elem (x,_) xs ) 
                      then xs 
                      else x ++ duplicate xs 

我希望这是有道理的,谢谢你的帮助。

2 个答案:

答案 0 :(得分:1)

对于一个相对简单但效率低下的(二次时间)解决方案,您可以执行以下操作:

removeFirstDuplicate :: Eq a => [(a, b)] -> [(a, b)]                                                                                                                                                    
removeFirstDuplicate [] = []
removeFirstDuplicate (hd@(x, _):tl) =
    let
        rest = removeFirstDuplicate tl
    in
        if x `elem` (map rest tl) then rest else hd:rest

它表示删除空列表的第一个副本是空列表。删除带有fst x的元组的第一个副本需要删除尾部的第一个副本,然后根据它是否在尾部,将第一个元组附加到结果中。

完整代码:

removeFirstDuplicate :: Eq a => [(a, b)] -> [(a, b)]
removeFirstDuplicate [] = []
removeFirstDuplicate xs@(hd@(x, _):tl) =
    let
        rest = removeFirstDuplicate tl
    in
        if x `elem` (map fst rest) then rest else hd:rest                                                                                                                                               


main =
    let
        l = [(1, 'a'), (2, 'b'), (1, 'c'), (1, 'd'), (2, 'f')]
    in
        do
            putStrLn $ show $ removeFirstDuplicate l

输出:

$ ghc duplicates.hs && ./duplicates 
[(1,'d'),(2,'f')]

当然,应该可以在线性时间内强制求解,或者用平衡树在Θ(n log(n))中求解:首先迭代元组,并映射每个{ {1}}项到它出现的最后一个索引。然后再次迭代,并仅保留fst项目在最后记录的索引的元组。

答案 1 :(得分:1)

我是Haskell的新手,所以如果我错了请纠正我。在这种类型的问题中,我的JavaScript本能告诉我使用Map或Hash。但是,当我检查Data.Map库时,我注意到它是一个树实现,查找就像O(log n)。所以如果我使用sortOn代替,我认为它的性能可能相似。任何进一步的信息都非常感谢。

所以我的解决方案是;

dropFirstDupe :: (Ord a, Eq a) => [(a,b)] -> [(a,b)]
dropFirstDupe []  = []
dropFirstDupe [t] = [t]
dropFirstDupe ts  | fst t1 == fst t2 = dropFirstDupe (t2:tr)
                  | otherwise        = t1 : dropFirstDupe (t2:tr)
                  where (t1:t2:tr)   = sortOn fst ts

*Main> dropFirstDupe [(1,2),(3,6),(8,7),(1,5),(3,3),(7,9)]
[(1,5),(3,3),(7,9),(8,7)]

注意:如果存在超过2个重复项,则它将保留最后一个。

嗯,我想在上面的代码片段中,我在递归的每个回合中都运行sortOn函数,这是完全没必要的。因此,上述代码的更高效版本应该是;

dropFirstDupe :: (Ord a, Eq a) => [(a,b)] -> [(a,b)]
dropFirstDupe []  = []
dropFirstDupe ts  = dfd (sortOn fst ts)
                    where dfd [t]        = [t]
                          dfd (t1:t2:tr) | fst t1 == fst t2 = dfd (t2:tr)
                                         | otherwise        = t1 : dfd (t2:tr)