簡體   English   中英

Haskell-將兩個列表轉換為元組列表

[英]Haskell- Two lists into a list of tuples

我正在嘗試實現一個函數(如下所述),它接受兩個列表(每個列表或兩者都可能是無限的)並返回列表之間所有可能的元素對的元組列表

zipInf :: [a] -> [b] -> [(a,b)]

(例如輸出應該是這樣的,但不必完全這樣)

zipInf [0 .. 2] ['A' .. 'C'] ~> [(0,'A'),(1,'A'),(0,'B'),(1,'B'),(0,'C'),(2,'A'),(2,'B'),(1,'C'),(2,'C')]

zipInf [] [0 ..] ~> []

zipInf [0 ..] [] ~> []

take 9 (zipInf ['A'] [0 .. ]) ~> [('A',0),('A',1),('A',2),('A',3),('A',4),('A',5),('A',6),('A',7),('A',8)]

我已經開始像這樣實現它:

zipInf :: [a] -> [b] -> [(a,b)]
zipInf [] _ = []
zipInf _ [] = []
zipInf

我想將列表輸入一個輔助函數來生成列表,但我制作的列表無法編譯並且不知道如何處理無限列表

輔助功能-

oneList :: [a] -> [b] [(a,b)]
oneList [] _ = []
oneList x:xs y:ys = [(x,y)] ++ oneList

這是一個很好的練習!

如果我們將您的輸入對放在一個無限表中:

(0,A)  (1,A)  (2,A)  (3,A) ...
(0,B)  (1,B)  (2,B)  (3,B) ...
(0,C)  (1,C)  (2,C)  (3,C) ...
(0,D)  (1,D)  (2,D)  (3,D) ...
...

訣竅是以向上的斜條紋遍歷表格。 用眼睛追蹤桌子。 該表的條紋是:

(0,A)
(0,B) (1,A)
(0,C) (1,B) (2,A)
(0,D) (1,C) (2,B) (3,A)
...

所有條紋都是有限的,但表中的每個元素都在其中一個中,因此如果將它們連接在一起,則每個元素都將出現在連接結果中的有限位置。

這是我建議的游戲計划:

實現stripes :: [[a]] -> [[a]]像上面一樣從無限數組中提取條紋列表(首先假設所有列表都是無限的,即不要擔心[]情況;一次你有那個工作,糾正它以處理可能有限的列表)。

使用stripes ,實現diagonal :: [[a]] -> [a]連接所有條紋(這是一個單行)。

最后,通過應用特定 2D 表[[(a,b)]] diagonal來實現您的函數,這是我開始這個答案的表(並且可以使用嵌套列表理解等各種方式構建)。

筆記:

  • 名稱 zip 具有誤導性。 這更像是笛卡爾積。

  • 您知道可以匹配模式內的模式,對嗎? 即如果f :: [[a]] -> something

     f ((x:xs):xss) = ...

    給你x作為第一行的第一個元素, xs是第一行的其余部分, xss是表格的其余部分。

雖然這是了解列表和 Haskell 的一個很好的練習,但它也是理解Applicative類的全部內容的一個很好的練習。 特別是Applicative[]實例。 zipInf您想正是liftA2 (,)

λ: :t liftA2
liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
λ: :t (,)
(,) :: a -> b -> (a, b)
λ: :t liftA2 (,)
liftA2 (,) :: Applicative f => f a -> f b -> f (a, b)

我們只需要確保[]是一個Applicative

λ: :i []
...
instance Applicative [] -- Defined in `Control.Applicative'
...

所以它是一個Applicative 如果我們稍微注釋一下我們的類型可能會更容易理解

λ: :t liftA2 (,) `asAppliedTo` []
[a] -> [b] -> [(a, b)]

是的,是同一類型。

λ: liftA2 (,) [0..2] ['A'..'C']
[(0,'A'),(0,'B'),(0,'C'),(1,'A'),(1,'B'),(1,'C'),(2,'A'),(2,'B'),(2,'C')]

看起來它有效! 所以你不需要做任何事情來寫這個,它可以說比遞歸定義更容易理解。 另外,您不必像在推出自己的解決方案時那樣擔心邊緣情況。

您還可以使用<$> (或fmap )和<*>更習慣地編寫它。

λ: (,) <$> [0..2] <*> ['A'..'C']
[(0,'A'),(0,'B'),(0,'C'),(1,'A'),(1,'B'),(1,'C'),(2,'A'),(2,'B'),(2,'C')]
λ: take 9 $ (,) <$> "A" <*> [0..]
[('A',0),('A',1),('A',2),('A',3),('A',4),('A',5),('A',6),('A',7),('A',8)]

或者你可以利用Monad的全部功能(在這種情況下這是非常不必要的):

λ: do {n <- [0..2]; c <- ['A'..'C']; return (n, c)}
[(0,'A'),(0,'B'),(0,'C'),(1,'A'),(1,'B'),(1,'C'),(2,'A'),(2,'B'),(2,'C')]

另外,如果你想知道如何才能從中獲取不同的語義Applicative[]有至少一個其他List實例ApplicativeZipList

λ: :i ZipList
newtype ZipList a = ZipList {getZipList :: [a]}
    -- Defined in `Control.Applicative'
instance Functor ZipList -- Defined in `Control.Applicative'
instance Applicative ZipList -- Defined in `Control.Applicative'

此實例為其Applicative實例提供壓縮樣式語義。

λ: getZipList $ (,) <$> ZipList [0..2] <*> ZipList ['A'..'C']
[(0,'A'),(1,'B'),(2,'C')]

這兩個都是對Applicative類型類的很好的介紹,因為它們隨時可用,相當直觀,有助於防止您產生錯誤,並表明存在單個類型具有多個類型類實例的情況。

這是您發布的輔助函數:

oneList :: [a] -> [b] [(a,b)]
oneList [] _ = []
oneList x:xs y:ys = [(x,y)] ++ oneList

這是它包含的語法錯誤:

  • 您在類型注釋中遺漏了一個箭頭; 它應該是

    oneList :: [a] -> [b] -> [(a,b)]
  • 你需要在括號中包含非平凡的模式,所以第二個等式應該開始

    oneList (x:xs) (y:ys) =
  • oneList在返回列表之前接受兩個參數,但是在第二個等式的 rhs 中,您嘗試將其用作列表而不給它任何參數

(順便說一句,如果您發布錯誤消息而不是僅僅說它無法編譯,它通常會對我們有所幫助。將我上面指出的錯誤與編譯器給您的錯誤消息進行比較。)


但正如您所指出的,您的算法是錯誤的。

我覺得這是作業,所以我只是給你一個提示。

zipInf應該是

zipInf :: [a] -> [b] -> [(a,b)]
zipInf xs ys = thread (expand xs ys)

threadexpand是兩個輔助函數,我讓你寫,帶有類型簽名

expand :: [a] -> [b] -> [[(a,b)]]
thread :: [[c]] -> [c]

expand相當簡單。 thread是你必須小心以正確的順序包含元素的地方(因此你不能只說thread zs = concat zs ,即使類型是正確的)。

您需要將oneList應用於xsys

oneList :: [a] -> [b] -> [(a, b)]
oneList []     _      = []
oneList (x:xs) (y:ys) = (x, y) : oneList xs ys

由於 Haskell 是懶惰的,無限列表將自動工作。

重要提示:請參閱下面的 Will Ness 評論。

您的問題意味着順序無關緊要。 (但由於列表可能是無限的,順序可能比您想象的更重要!)無論如何,如果順序無關緊要,並且您遇到了列表推導式,這是您可以使用的一種方法。 這是一個例子。

λ> let xs = "abcdef"
λ> let ys = "ABCDEFGHI"
λ> [(x,y) | x <- xs, y <- ys]
[('a','A'),('a','B'),('a','C'),('a','D'),('a','E'),('a','F'),('a','G'),('a','H'),('a','I'),('b','A'),('b','B'),('b','C'),('b','D'),('b','E'),('b','F'),('b','G'),('b','H'),('b','I'),('c','A'),('c','B'),('c','C'),('c','D'),('c','E'),('c','F'),('c','G'),('c','H'),('c','I'),('d','A'),('d','B'),('d','C'),('d','D'),('d','E'),('d','F'),('d','G'),('d','H'),('d','I'),('e','A'),('e','B'),('e','C'),('e','D'),('e','E'),('e','F'),('e','G'),('e','H'),('e','I'),('f','A'),('f','B'),('f','C'),('f','D'),('f','E'),('f','F'),('f','G'),('f','H'),('f','I')]

請注意,首先打印所有涉及'a'的元組,然后是涉及'b'的元組,依此類推。 為什么這很重要? 那么假設列表是無限的。 像這樣的查詢將立即返回:

(1,'a') `elem` [(x,y) | x <- [1..], y <- ['a'..]]

但是這樣的操作需要很長時間:

(200000,'a') `elem` [(x,y) | x <- [1..], y <- ['a'..]]

如果順序很重要,或者您還沒有遇到列表推導式,或者不想使用它們,那么 luqui 的方法可能就是您正在尋找的方法。

您可以通過使用列表理解來非常簡單地做到這一點。

zip :: [a] -> [b] -> [(a,b)]
zip [] _ = [] 
zip _ [] = []
zip as bs = [(a, b) | a <- as, b <- bs]

它適用於有限和無限列表。 當然,您希望其中至少一個是有限的,或者它只是head asbs所有組合。

> zip [0..2] ['A'..'C']
[(0,'A'),(0,'B'),(0,'C'),(1,'A'),(1,'B'),(1,'C'),(2,'A'),(2,'B'),(2,'C')]

> take 50 $ zip [0..] ['A'..'C']
[(0,'A'),(0,'B'),(0,'C'),(1,'A'),(1,'B'),(1,'C'),(2,'A'),(2,'B'),(2,'C'),(3,'A'),(3,'B'),(3,'C'),(4,'A'),(4,'B'),(4,'C'),(5,'A'),(5,'B'),(5,'C'),(6,'A'),(6,'B'),(6,'C'),(7,'A'),(7,'B'),(7,'C'),(8,'A'),(8,'B'),(8,'C'),(9,'A'),(9,'B'),(9,'C'),(10,'A'),(10,'B'),(10,'C'),(11,'A'),(11,'B'),(11,'C'),(12,'A'),(12,'B'),(12,'C'),(13,'A'),(13,'B'),(13,'C'),(14,'A'),(14,'B'),(14,'C'),(15,'A'),(15,'B'),(15,'C'),(16,'A'),(16,'B')]

> take 50 $ zip [0..] [1..]
[(0,1),(0,2),(0,3),(0,4),(0,5),(0,6),(0,7),(0,8),(0,9),(0,10),(0,11),(0,12),(0,13),(0,14),(0,15),(0,16),(0,17),(0,18),(0,19),(0,20),(0,21),(0,22),(0,23),(0,24),(0,25),(0,26),(0,27),(0,28),(0,29),(0,30),(0,31),(0,32),(0,33),(0,34),(0,35),(0,36),(0,37),(0,38),(0,39),(0,40),(0,41),(0,42),(0,43),(0,44),(0,45),(0,46),(0,47),(0,48),(0,49),(0,50)]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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