如何从Haskell中的两个列表中提取相同的元素?

时间:2014-09-10 08:12:19

标签: haskell

这是我的问题: 如何从两个相等长度列表中提取相同的元素到另一个列表? 例如:给定两个列表[2,4,6,3,2,1,3,5][7,3,3,2,8,8,9,1],答案应为[1,2,3,3]。请注意,订单并不重要。我实际上使用了返回列表的长度。

我试过了:

sameElem as bs = length (nub (intersect as bs))

但问题是nub删除了所有重复项。将我的函数用于前一个示例的结果是3[1,3,2]而不是4 [1,3,3,2]的长度。有解决方案吗?谢谢。

3 个答案:

答案 0 :(得分:3)

由于该位置似乎无关紧要,您可以事先对列表进行排序,然后遍历两个列表:

import Data.List (sort)

intersectSorted :: Ord a => [a] -> [a] -> [a]
intersectSorted (x:xs) (y:ys) 
 | x == y    = x : intersectSorted xs ys
 | x < y     = intersectSorted xs (y:ys)
 | x > y     = intersectSorted (x:xs) ys
intersectSorted _ _ = []

intersect :: Ord a => [a] -> [a] -> [a]
intersect xs ys = intersectSorted (sort xs) (sort ys)

请注意,使用Map

也可以实现此目的
import Data.Map.Strict (fromListWith, assocs, intersectionWith, Map)

type Counter a = Map a Int 

toCounter :: Ord a => [a] -> Counter a
toCounter = fromListWith (+) . flip zip (repeat 1)

intersectCounter :: Ord a => Counter a -> Counter a -> Counter a
intersectCounter = intersectionWith min

toList :: Counter a -> [a]
toList = concatMap (\(k,c) -> replicate c k) . assocs

intersect :: Ord a => [a] -> [a] -> [a]
intersect xs ys = toList $ intersectCounter (toCounter xs) (toCounter ys)

答案 1 :(得分:3)

你可以为此编写一个函数。可能有一个更优雅的版本涉及lambda或折叠,但这确实适用于你的例子:

import Data.List

same (x:xs) ys = if x `elem` ys
                 then x:same xs (delete x ys)
                 else same xs ys
same [] _ = []
same _ [] = []

then子句中的delete x ys很重要,如果没有删除,那么每次遇到第一个列表中至少出现一次的删除命令项将被计算在内。
请注意,输出未排序,因为您只对结果列表的长度感兴趣。

答案 2 :(得分:0)

import Data.List (delete)

mutuals :: Eq a => [a] -> [a] -> [a]
mutuals []       _                = []
mutuals (x : xs) ys | x `elem` ys = x : mutuals xs (delete x ys)
                    | otherwise   = mutuals xs ys

给出

mutuals [2,4,6,3,2,1,3,5] [7,3,3,2,8,8,9,1]  ==  [2,3,1,3]