简体   繁体   English

haskell如何检查两个元组列表相等并采取联合

[英]haskell how to check two lists of tuples are equal and take union

I am a new self-leaner in Haskell. 我是哈斯克尔的一个新的自我瘦身者。 firstly, I want to write a function to check if two lists of tuples are equal. 首先,我想编写一个函数来检查两个元组列表是否相等。 Each tuple has a key and value 每个元组都有一个键和值

secondly, I want a function to union two lists of tuples 其次,我想要一个函数来联合两个元组列表

I tried several ways and tried many times but seems couldn't meet my requirements. 我尝试了几种方法并尝试了很多次,但似乎无法满足我的要求。 could anyone help me? 谁能帮助我? thanks in advance. 提前致谢。

Since a is only a member of Eq , sorting or grouping is not an option. 由于a只是Eq的成员,因此不能选择排序或分组。

import Data.List(nub, (\\))
import Data.Monoid(getSum)

type Times = Int
type Lis a = [(a,Times)]

lisEqual :: Eq a => Lis a -> Lis a -> Bool
lisEqual xs xs' = length xs == length xs' && xs \\ xs' == []

lisSum :: Eq a => Lis a-> Lis a-> Lis a
lisSum xs xs' = fmap f $ getKeys l 
  where
    f x = (,) x (getSum . foldMap (pure . snd) . filter ((x ==) . fst) $ l)                         
    l = xs ++ xs'
    getKeys = nub . fst . unzip

My suggestion: Start with a function that extracts the combined keys from two lists: 我的建议:从一个从两个列表中提取组合键的函数开始:

allKeys :: Eq a => Lis a -> Lis a -> [a]

So allKeys [('a',2),('b',2),('c',3)] [('b',2),('a',1),('d',3)] is ['a','b','c','d'] . 所以allKeys [('a',2),('b',2),('c',3)] [('b',2),('a',1),('d',3)]['a','b','c','d'] Hint: Extract all keys from both lists, merge them to one list, then remove duplicates from that list (there are standard functions for all these tasks). 提示:从两个列表中提取所有键,将它们合并到一个列表中,然后从该列表中删除重复项(所有这些任务都有标准函数)。

The function is useful both for checking equality and computing the sums: 该函数对于检查相等性和计算总和都很有用:

  • To check equality, just check that looking up each key in the first list gives the same result as looking it up in the second list. 要检查相等性,只需检查在第一个列表中查找每个键给出与在第二个列表中查找它相同的结果。
  • To compute the sums, just pair every key up with the sum of lookups in both original lists. 要计算总和,只需将每个键与两个原始列表中的查找总和相对应。

One thing to consider: is the list [('a',0)] is ment to be identical to [] ? 需要考虑的一件事是:列表[('a',0)]是否与[]相同? Otherwise you should use a lookup function that returns Maybe Int and gives Just 0 for key 'a' in the first case and Nothing in the second case. 否则,您应该使用返回Maybe Int的查找函数,并在第一种情况下为键'a'提供Just 0 ,在第二种情况下为Nothing

Let me know if this is not homework and I can give you code. 如果这不是作业,请告诉我,我可以给你代码。

Edit: Code! 编辑:代码! :) :)

The code below is slightly simplified compared to how I would normally write it, but not by much. 与我通常编写的代码相比,下面的代码略有简化,但不是很多。 There are probably several library functions you are not familiar with, including nub (for removing duplicates) which is imported from Data.List. 可能有几个您不熟悉的库函数,包括从Data.List导入的nub(用于删除重复项)。

import Data.List(nub)

type Times = Int
type Lis a = [(a,Times)] 

count :: Eq a => Lis a -> a -> Times
count xs x = case lookup x xs of
  Nothing -> 0 -- x is not in the list
  Just n  -> n -- x is in the list associated with n

-- Extract all keys by taking the first value in each pair
keys :: Lis a -> [a]
keys xs = map fst xs 

-- Extract the union of all keys of two lists
allKeys :: Eq a => Lis a -> Lis a -> [a]
allKeys xs ys = nub (keys xs ++ keys ys)

lisEquals :: Eq a=> Lis a -> Lis a -> Bool
lisEquals xs ys = all test (allKeys xs ys) 
  where
    -- Check that a key maps to the same value in both lists
    test k = count xs k == count ys k

lisSum :: Eq a => Lis a -> Lis a -> Lis a
lisSum xs ys = map countBoth (allKeys xs ys)
  where
    -- Build a new list element from a key
    countBoth k = (k,count xs k + count ys k)

This is the version I proposed in the comments. 这是我在评论中提出的版本。 First check the lists for duplicate keys and equal length to ensure that we only need to check if all keys of l1 are keys of l2 . 首先检查重复键和等长的列表,以确保我们只需要检查l1所有键是否为l2键。 Then do the lookup and check if the counts are equal: 然后执行查找并检查计数是否相等:

lisEqual l1 l2 =
  (nodups $ map fst l1) &&
  (nodups $ map fst l2) &&
  length l1 == length l2 &&
  and (map (\ (x,k) -> case (occOfA x l2) of
                    Just n -> n == k
                    Nothing -> False
                  ) l1)

The lookup returns Maybe b to indicate a failed lookup with Nothing . 查找返回Maybe b以指示Nothing查找失败。

occOfA :: Eq a => a -> [(a,b)] -> Maybe b
occOfA a []   = Nothing
occOfA a ((x,n):xs) =
  if a == x then Just n
            else occOfA a xs

The duplicate checking is just a recursion 重复检查只是一个递归

nodups :: Eq a => [a] -> Bool
nodups [] = True
nodups (x:xs) = not (x `elem` xs) && (nodups xs)

Some test cases 一些测试用例

t :: Int -> Bool
t 0 = lisEqual [(2,3), (1,2)] [(1,2), (2,3)] == True
t 1 = lisEqual [(2,3), (1,2)] [(1,3), (2,3)] == False
t 2 = lisEqual [(2,3), (1,2), (1,3)] [(1,3), (2,3)] == False
t 3 = lisEqual [(2,3)] [(1,3), (2,3)] == False

can be checked as 可以检查为

*Main> and $ map t [0..3]
True

I'm a bit lazy for computing the sums, I define a function lisSum1 that collects all keys from list and sums up the values accordingly. 我有点懒于计算总和,我定义了一个函数lisSum1 ,它从列表中收集所有键并相应地总结值。 For lisSum I just need to concatenate the two lists: 对于lisSum我只需要连接两个列表:

lisSum l1 l2 = lisSum1 $ l1 ++ l2

lisSum1 :: Eq a => [(a,Int)] -> [(a,Int)]
lisSum1 list =
   reverse $ foldl (\acc k ->  (k, sumList $ map snd (select k list) ) : acc ) -- create pairs (k, ksum) where ksum is the sum of all values with key k
   [] (rdups $ map fst list)

With some helper functions: 有一些辅助函数:

rdups :: Eq a => [a] -> [a]
rdups [] = []
rdups (x:xs) = x : rdups (filter (/= x) xs)

sum l = foldl (+) 0 l

select k list = filter (\ (x,_) -> k == x) list

Some tests again: 一些测试再次:

s :: Int -> Bool
s 0 = lisSum [('a',1), ('a',2)] [('a',3)] == [('a',6)]
s 1 = lisSum [(1,2), (2,3)] [(2,4),(3,1)] == [(1,2),(2,7),(3,1)]
s 2 = lisSum [(1,2), (2,3), (2,4), (3,1)] [] == [(1,2),(2,7),(3,1)]
s 3 = lisSum [(1,2), (2,3), (3,1)] [] == [(1,2),(2,3),(3,1)]


*Main> map s [0..3]
[True,True,True,True]

Edit : The function lisEqual is not reflexive because we originally defined a version that requires no duplicates in the input. 编辑 :函数lisEqual不反身,因为我们最初定义了一个在输入中不需要重复的版本。 The problem with this is that lisEqual is no equivalence relation: 这个问题是lisEqual不是等价关系:

*Main> lisEqual [(1,1),(1,2)] [(1,1),(1,2)]
False

If we fix the reflexivity, we can just remove the original restriction on duplicates and define: 如果我们修正反身性,我们可以删除重复的原始限制并定义:

lisEqualD [] []    = True
lisEqualD (_:_) [] = False
lisEqualD [] (_:_) = False
lisEqualD (x:xs) ys =
    case (remFirst x ys) of
        Nothing -> False
        Just zs -> lisEqualD xs zs

remFirst x [] = Nothing
remFirst x (y:ys) =
  if x == y then Just ys
            else case (remFirst x ys) of
                    Just zs -> Just (y:zs)
                    Nothing -> Nothing

Let's extend the test cases: 让我们扩展测试用例:

t :: Int -> Bool
t 0 = lisEqualD [(2,3), (1,2)] [(1,2), (2,3)] == True
t 1 = lisEqualD [(2,3), (1,2)] [(1,3), (2,3)] == False
t 2 = lisEqualD [(2,3), (1,2), (1,3)] [(1,3), (2,3)] == False
t 3 = lisEqualD [(2,3)] [(1,3), (2,3)] == False
t 4 = lisEqualD [(2,3), (1,2), (2,3)] [(1,2), (2,3),(2,3)] == True
t 5 = lisEqualD [(1,1),(1,2)] [(1,1),(1,2)] == True


*Main> map t [0..5]
[True,True,True,True,True,True]

My solution is pretty straightforward. 我的解决方案很简单。 In order to compare such lists you need to order them first. 为了比较这些列表,您需要先订购它们。 Sum two list by key can be done recursively as long as the key is of type Ord and you order by key both lists. 只要密钥是Ord类型并按键排序两个列表,就可以递归地按密钥求和两个列表。 I am not using your aliases just to keep it primitive, but you can easily adapt it 我没有使用你的别名只是为了保持它原始,但你可以很容易地适应它

eqList xs vs = xs' == vs' 
                 where xs' = sortOn fst xs
                       vs' = sortOn fst vs

sumKeyValue' :: [(Char, Integer)] -> [(Char, Integer)] -> [(Char, Integer)]
sumKeyValue' [] v  = v
sumKeyValue' x  [] = x
sumKeyValue' x@((a, c):xs) v@((b,d):vs) 
  | a == b = (a, c + d):sumKeyValue xs vs
  | a < b  = (a,c):sumKeyValue xs v
  | a > b  = (b,d):sumKeyValue x vs

sumKeyValue xs vs = sumKeyValue' xs' vs' 
  where xs' = sortOn fst xs
        vs' = sortOn fst vs

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM