【问题标题】:How to extract the same elements from two lists in Haskell?如何从 Haskell 的两个列表中提取相同的元素?
【发布时间】:2014-09-10 08:12:18
【问题描述】:

这是我的问题: 如何从两个相等长度的列表中提取相同的元素到另一个列表? 例如:给定两个列表[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]的长度。有解决办法吗?谢谢。

【问题讨论】:

  • 这里的位置是否相关,如示例所示?如果是这样,您可以将列表压缩到 [(1,1),(3,3),(3,3),(2,2),(4,5)...] 并删除其中的条目fst 不是 snd。
  • 位置与这里无关。我已经改变了这个例子。谢谢。
  • 好的,那么还有一个类似的解决方案。请注意,(\l-> \k-> [x|x<-l,y<-k,x==y]) [2,4,6,3,2,1,3,5] [7,3,3,2,8,8,9,1] 会找到所有匹配项,然后返回 [2,3,3,2,1,3,3]。您可以通过索引第一个列表并最终删除重复出现的值来消除重复计算。
  • NikolajK,或者您可以在结果上使用 Data.List 中的 nub 来删除重复项。
  • @fgv: 不,如果他通过[7,9,7][3,7,7,7],他希望输出为[7,7],参见上面的示例。我的方法会首先找到六对,然后根据索引删除其中四对,得到正确的结果。如果我们只申请nub,那么我们总是只能找到一个。

标签: haskell


【解决方案1】:

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

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)

【讨论】:

  • 很抱歉我举的例子不合适。其实这个位置应该是无关紧要的。
  • @user3928256:请注意,您当前的示例在结果中缺少2
  • 这也可以通过zip 完成:intersectSorted :: Eq a, Ord a =&gt; [a] -&gt; [a] -&gt; [a]; intersectSorted l k = filter (\(x,y) -&gt; x == y) $ zip (sort l) (sort k)
  • @C.Quilley:反例:[0,1,4,5] [1,2,3,4]。已排序列表的zip 没有任何值相等的对,但交集应该是[1,4]
  • @dfeuer 好点。我想我在 14 年有点太喜欢这种毫无意义的风格了。我还替换了uncurry (flip replicate),因为 lambda 更易于阅读。
【解决方案2】:

你可以为此编写一个函数。可能有一个更优雅的版本涉及 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 很重要,否则每次遇到至少出现一次的第一个列表中的删除命令项都会被计算在内。
请注意,输出未排序,因为您只对结果列表的长度感兴趣。

【讨论】:

  • 谢谢!这正是我在 Java 中所做的。我以前不知道 Haskell 中的“删除”。
【解决方案3】:
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]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-23
    • 1970-01-01
    • 2020-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-18
    相关资源
    最近更新 更多