如何比较haskell列表中的内容,看它们是否部分相同?

时间:2014-05-31 08:01:59

标签: list haskell set

这对我来说非常棘手。

鉴于列表非常长,

  

[[100,11,1,0,40,1],[100,12,3,1,22,23],[101,11,1,0,45,1],[101,12 ,3,1,28,30],[102,11,1,0,50,1],[102,12,3,1,50,50],[103,11,1,0,50,1 ],[103,12,3,1,50,50],[104,11,1,0,50,25],[104,12,3,1,50,50],[105,11,1 ,0,50,49],[105,12,3,0,30,30],[106,11,1,0,50,49],[106,12,3,0,25,26] [107,11,1,1,33,20],[107,12,3,0,25,26],[108,11,1,1,2,1],[108,12,3,1 ,20,24],[109,11,1,1,2,1],[109,12,3,1,28,31],[110,11,1,0,40,1],[110 ,12,3,1,22,23] ..]

现在igore每个列表的第一个数字,如果两个列表尽管第一个数字相同,例如

  

[101,11,1,0,50,1] alike [102,11,1,0,50,1]

我们保留后一个列表,直到整个列表全部被检查。理想情况下,结果应该是:

  

[[102,11,1,0,50,1],[103,12,3,1,50,50] ..]

我的想法是使用map取出第一个数字,使用nub和\\来摆脱所有重复的结果,使其成为像

  

[[11,1,0,50,1],[12,3,1,50,50],[12,3,1,50,50],[11,1,0,50,49 ],[12,3,0,25,26],[11,1,1,2,1],[11,1,0,40,1],[12,3,1,22,23] [11,1,0,45,1],[12,3,1,28,30] ..]

并使用Set.isSubsetOf过滤原始列表。

然而,这个想法太复杂,难以实施。由于我是Haskell的初学者,有没有更好的方法对此进行排序?或者我可以使用递归函数(尽管仍然需要努力)?

3 个答案:

答案 0 :(得分:4)

根据您的描述,我认为您希望获得最后列表的列表,以获得每个独特的尾部。你可以这样做:

lastUniqueByTail :: Eq a => [[a]] -> [[a]] 
lastUniqueByTail = reverse . nubBy ((==) `on` tail) . reverse

注意 这仅适用于非空列表的有限列表

您可以在on模块中找到Data.Function,然后在nubBy中找到Data.List

所以这里是每个部分的解释,我们将从内到外工作:

  • ((==) `on` tail)此函数执行两个列表之间的比较,如果它们的尾部相等则确定它们是相等的(即它执行忽略第一个元素的相等性检查)。

    on功能正是这里的大部分魔力。它具有以下类型签名:

    on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
    

    它基本上定义为(f `on` g) x y = f (g x) (g y),因此替换我们提供的函数,我们得到((==) `on` tail) x y = (tail x) == (tail y)

  • 然后,nubBy就像nub,除了你可以提供一个比较器来测试相等性。我们给它上面定义的函数,以便丢弃具有相同尾部的元素。

  • 但是,与nub一样,nubBy会在找到的每个等价类中保留第一个元素。 (即如果它在列表中找到两个相等的元素,它总是选择第一个)。我们想要最后这样的元素,这就是我们必须首先反转的原因(这样,最后一个相等的元素就成了第一个,所以保留了。)
  • 最后,我们在最后反转以获得订单回溯的开始。

答案 1 :(得分:1)

如果您需要通过合并具有相同尾部的所有项目来压缩列表,请尝试使用此代码。

compactList:: [[Int]] -> [[Int]]
compactList list = reverse $ compactList' list [] 

compactList':: [[Int]] -> [[Int]] -> [[Int]]
compactList' [] res = res
compactList' (l:ls) res
    | inList l res
    = compactList' ls res
    | otherwise
    = compactList' ls  (l:res)

inList :: [Int] -> [[Int]] -> Bool
inList [] _ = False
inList _ [] = False
inList val@(x:xs) ((x':xs'):ls)
    | xs == xs'
    = True
    | otherwise
    = inList val ls

如果我们需要"保留后者列表" (我之前错过了),然后只需更改reverse

的位置
compactList list = reverse $ compactList' list []

答案 2 :(得分:0)

你可以找到2个向量之间的欧几里德距离。例如,如果你有两个列表[a0,a1,...,an]和[b0,b1,...,bn],那么它们之间的欧几里德距离的平方将是

sum [ (a - b) ^ 2 | (a, b) <- zip as bs ]

这可以让您了解关闭一个列表与另一个列表的关系。理想情况下你应该采用平方根,但对于你正在做的事情,我不知道是否有必要。

编辑:

抱歉,我误解了你的问题。根据您的定义,alike定义如下:

alike as bs = (tail as) == (tail bs)

如果是这种情况,那么类似下面的内容应该可以解决问题:

xAll [] = []
xAll xa@(x:xaa) = a : xAll b
    where
        a = last $ filter (alike x) xa
        b = filter (\m -> not (alike x m)) xa

但不确定这是否是你要找的......