[英]haskell how to check two lists of tuples are equal and take union
我是哈斯克尔的一个新的自我瘦身者。 首先,我想编写一个函数来检查两个元组列表是否相等。 每个元组都有一个键和值
其次,我想要一个函数来联合两个元组列表
我尝试了几种方法并尝试了很多次,但似乎无法满足我的要求。 谁能帮助我? 提前致谢。
由于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
我的建议:从一个从两个列表中提取组合键的函数开始:
allKeys :: Eq a => Lis a -> Lis a -> [a]
所以allKeys [('a',2),('b',2),('c',3)] [('b',2),('a',1),('d',3)]
是['a','b','c','d']
。 提示:从两个列表中提取所有键,将它们合并到一个列表中,然后从该列表中删除重复项(所有这些任务都有标准函数)。
该函数对于检查相等性和计算总和都很有用:
需要考虑的一件事是:列表[('a',0)]
是否与[]
相同? 否则,您应该使用返回Maybe Int
的查找函数,并在第一种情况下为键'a'提供Just 0
,在第二种情况下为Nothing
。
如果这不是作业,请告诉我,我可以给你代码。
编辑:代码! :)
与我通常编写的代码相比,下面的代码略有简化,但不是很多。 可能有几个您不熟悉的库函数,包括从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)
这是我在评论中提出的版本。 首先检查重复键和等长的列表,以确保我们只需要检查l1
所有键是否为l2
键。 然后执行查找并检查计数是否相等:
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)
查找返回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
重复检查只是一个递归
nodups :: Eq a => [a] -> Bool
nodups [] = True
nodups (x:xs) = not (x `elem` xs) && (nodups xs)
一些测试用例
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
可以检查为
*Main> and $ map t [0..3]
True
我有点懒于计算总和,我定义了一个函数lisSum1
,它从列表中收集所有键并相应地总结值。 对于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)
有一些辅助函数:
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
一些测试再次:
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]
编辑 :函数lisEqual
不反身,因为我们最初定义了一个在输入中不需要重复的版本。 这个问题是lisEqual
不是等价关系:
*Main> lisEqual [(1,1),(1,2)] [(1,1),(1,2)]
False
如果我们修正反身性,我们可以删除重复的原始限制并定义:
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
让我们扩展测试用例:
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]
我的解决方案很简单。 为了比较这些列表,您需要先订购它们。 只要密钥是Ord
类型并按键排序两个列表,就可以递归地按密钥求和两个列表。 我没有使用你的别名只是为了保持它原始,但你可以很容易地适应它
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.