簡體   English   中英

haskell如何檢查兩個元組列表相等並采取聯合

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM