我是一个新的Haskell自学者。首先,我想编写一个函数来检查两个元组列表是否相等。每个元组都有一个键和值。
其次,我想要一个函数来合并两个元组列表。
我尝试了几种方法,多次尝试,但好像无法满足我的需求。有人能帮忙吗?提前致谢。
其次,我想要一个函数来合并两个元组列表。
我尝试了几种方法,多次尝试,但好像无法满足我的需求。有人能帮忙吗?提前致谢。
由于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
。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)
lisSum [('a',1), ('a',2)] [('a',3)] == [('a',4)]
,但应该是[('a',6)]
(请参见问题的注释)。 - lambda.xy.xl1
的所有键是否为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]
< p > 编辑:函数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]
lisEqual
函数应该返回false?这意味着它不是自反的,因为对于某些(无效)列表,lisEqual x x
将会是false。也许它应该抛出一个错误(或者忽略重复元素,因为查找函数就是这样做的)。 - Jonas DuregårdlisEq [(1,2),(1,3)] [(1,3),(1,2)]
返回 false,而 lisEq [(1,2),(1,3)] [(1,2),(1,3)]
抛出一个错误,这也不是一个好的行为。 - lambda.xy.xlisSum [] == id
(加入一个空的 map 不会做任何事情)。 - Jonas DuregårdlisSum [] [(1,2),(1,3)]
。我添加了一个 reverse
来恢复每个列表中键都不同的情况。 - lambda.xy.xOrd
类型,并且你按照关键字对两个列表进行排序。为了保持原始性,我没有使用你的别名,但你可以很容易地适应它。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
Ord
约束条件,你无法进行排序。 - Jonas Duregård[1.. length dups]
压缩起来,并使用此映射作为排序,但这会使解决方案不太优雅。 - lambda.xy.xOrd
条件并不是@lamda.x.y.x所指出的问题。此外,根据问题(和修改),实际类型似乎是[(Char, Int)]
而不是任意a
的[(a, Int)]
,但也许我过于假设了。 - lsmor
lisLength
未定义。将你发布的内容加载到一个新的ghci实例中是否有效?(第一步是让你的代码编译通过,然后我们可以看看行为) - lambda.xy.x