[英]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: 该函数对于检查相等性和计算总和都很有用:
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.