Это версия, которую я предложил в комментариях.Сначала проверьте списки на дубликаты ключей и одинаковую длину, чтобы убедиться, что нам нужно только проверить, все ли ключи 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]