简体   繁体   English

Haskell-将两个列表转换为元组列表

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

I am trying to implement a function (described below) that takes two lists (each or both may be infinite) and return a list of tuples of all the possible pairs of elements between the lists我正在尝试实现一个函数(如下所述),它接受两个列表(每个列表或两者都可能是无限的)并返回列表之间所有可能的元素对的元组列表

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

(eg the output should be like this, but doesn't have to be exactly like this) (例如输出应该是这样的,但不必完全这样)

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)]

I've started implementing it like this:我已经开始像这样实现它:

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

I wanted to feed the list into a helper function to produce the lists but the one I made fails to compile and don't know how to handle infinite lists我想将列表输入一个辅助函数来生成列表,但我制作的列表无法编译并且不知道如何处理无限列表

Helper function-辅助功能-

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

This is a great exercise!这是一个很好的练习!

If we lay out the pairs of your input in an infinite table:如果我们将您的输入对放在一个无限表中:

(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) ...
...

The trick is to traverse the table in upward diagonal stripes.诀窍是以向上的斜条纹遍历表格。 Trace the table with your eyes.用眼睛追踪桌子。 The stripes of this table are:该表的条纹是:

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

All the stripes are finite, yet every element of the table is in one of them, so if you concatenate them together every element will appear at a finite position in the concatenated result.所有条纹都是有限的,但表中的每个元素都在其中一个中,因此如果将它们连接在一起,则每个元素都将出现在连接结果中的有限位置。

Here's the gameplan I'd suggest:这是我建议的游戏计划:

Implement stripes :: [[a]] -> [[a]] which extracts the list of stripes from an infinite array like above (start by assuming that all lists are infinite, ie don't worry about the [] cases; once you have that working, correct it to work on lists that might be finite).实现stripes :: [[a]] -> [[a]]像上面一样从无限数组中提取条纹列表(首先假设所有列表都是无限的,即不要担心[]情况;一次你有那个工作,纠正它以处理可能有限的列表)。

Using stripes , implement diagonal :: [[a]] -> [a] which concatenates all the stripes (this is a one-liner).使用stripes ,实现diagonal :: [[a]] -> [a]连接所有条纹(这是一个单行)。

Finally, implement your function by applying diagonal of a particular 2D table [[(a,b)]] , which is the table I started this answer with (and can be constructed using a nested list comprehension, among other various ways).最后,通过应用特定 2D 表[[(a,b)]] diagonal来实现您的函数,这是我开始这个答案的表(并且可以使用嵌套列表理解等各种方式构建)。

Notes:笔记:

  • The name zip is misleading.名称 zip 具有误导性。 This is more like a cartesian product.这更像是笛卡尔积。

  • You know you can match patterns inside patterns, right?您知道可以匹配模式内的模式,对吗? Ie if f :: [[a]] -> something即如果f :: [[a]] -> something

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

    Gives you x as the first element of the first row, xs is the rest of the first row, and xss is the rest of the table.给你x作为第一行的第一个元素, xs是第一行的其余部分, xss是表格的其余部分。

While this is a great exercise for getting to understand lists and Haskell in general, it is also a great exercise for understanding what the Applicative class is all about.虽然这是了解列表和 Haskell 的一个很好的练习,但它也是理解Applicative类的全部内容的一个很好的练习。 In particular, the [] instance for Applicative .特别是Applicative[]实例。 Your zipInf that you want is exactly liftA2 (,)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)

We just need to make sure that [] is an Applicative .我们只需要确保[]是一个Applicative

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

So it's an Applicative .所以它是一个Applicative It might make it easier to understand if we annotate our type a bit如果我们稍微注释一下我们的类型可能会更容易理解

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

Yep, that's the same type.是的,是同一类型。

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

Looks like it works!看起来它有效! So you didn't have to do anything to write this, and it's arguably more understandable than a recursive definition would be.所以你不需要做任何事情来写这个,它可以说比递归定义更容易理解。 Plus, you didn't have to worry about edge cases like you would when rolling your own solution.另外,您不必像在推出自己的解决方案时那样担心边缘情况。

You can also write it a bit more idiomantically using <$> (or fmap ) and <*> .您还可以使用<$> (或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)]

Or you could leverage the full power of Monad (which is quite unnecessary in this case):或者你可以利用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')]

Also, if you're wondering how you could get different semantics from Applicative for [] there is at least one other List instance for Applicative : ZipList另外,如果你想知道如何才能从中获取不同的语义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'

This instance provides zipping style semantics for its Applicative instance.此实例为其Applicative实例提供压缩样式语义。

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

Both of these are good introductions to the Applicative typeclass, because they're readily available, fairly intuitive, help prevent you from making bugs, and show that there are cases where a single type has more than one instance of a typeclass.这两个都是对Applicative类型类的很好的介绍,因为它们随时可用,相当直观,有助于防止您产生错误,并表明存在单个类型具有多个类型类实例的情况。

Here's the helper function you posted:这是您发布的辅助函数:

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

And here are the syntax errors it contains:这是它包含的语法错误:

  • you left out an arrow in the type annotation;您在类型注释中遗漏了一个箭头; it should be它应该是

    oneList :: [a] -> [b] -> [(a,b)]
  • you need to enclose non-trivial patterns in parens, so the second equation should start你需要在括号中包含非平凡的模式,所以第二个等式应该开始

    oneList (x:xs) (y:ys) =
  • oneList takes two arguments before giving back a list, but in the rhs of the second equation you try to use it as a list without giving it any arguments oneList在返回列表之前接受两个参数,但是在第二个等式的 rhs 中,您尝试将其用作列表而不给它任何参数

(Btw, it usually helps us if you post the error messages instead of just saying it doesn't compile. Compare the errors I've point out above to the error messages the compiler gave you.) (顺便说一句,如果您发布错误消息而不是仅仅说它无法编译,它通常会对我们有所帮助。将我上面指出的错误与编译器给您的错误消息进行比较。)


But as you note, your algorithm is wrong.但正如您所指出的,您的算法是错误的。

I sense this is homework, so I am only going to give you a hint.我觉得这是作业,所以我只是给你一个提示。

zipInf should be zipInf应该是

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

thread and expand are two helper functions I am leaving you to write, with type signatures threadexpand是两个辅助函数,我让你写,带有类型签名

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

expand is fairly simple. expand相当简单。 thread is where you have to be careful to include elements in the right order (hence you can't just say thread zs = concat zs , even though the type is right). thread是你必须小心以正确的顺序包含元素的地方(因此你不能只说thread zs = concat zs ,即使类型是正确的)。

You need to apply oneList to xs and ys .您需要将oneList应用于xsys

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

Infinite lists will automatically work since Haskell is lazy.由于 Haskell 是懒惰的,无限列表将自动工作。

IMPORTANT: See Will Ness' comment below.重要提示:请参阅下面的 Will Ness 评论。

Your question implies that the order doesn't matter.您的问题意味着顺序无关紧要。 (But since the lists may be infinite, the order may be more important than you think!) Anyway, if the order doesn't matter, and you have encountered list comprehensions , that's one approach you could use. (但由于列表可能是无限的,顺序可能比您想象的更重要!)无论如何,如果顺序无关紧要,并且您遇到了列表推导式,这是您可以使用的一种方法。 Here's an example.这是一个例子。

λ> 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')]

Note that all of the tuples involving 'a' are printed first, then those involving 'b' , and so on.请注意,首先打印所有涉及'a'的元组,然后是涉及'b'的元组,依此类推。 Why does that matter?为什么这很重要? Well suppose the list is infinite.那么假设列表是无限的。 A query like this will return instantly:像这样的查询将立即返回:

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

But one like this will take a LOOOOONG time:但是这样的操作需要很长时间:

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

If order matters, or you haven't encountered list comprehensions, or don't want to use them, luqui's approach is probably what you're looking for.如果顺序很重要,或者您还没有遇到列表推导式,或者不想使用它们,那么 luqui 的方法可能就是您正在寻找的方法。

You can do this very simply by using list comprehension.您可以通过使用列表理解来非常简单地做到这一点。

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

It works perfectly with both finite and infinite lists.它适用于有限和无限列表。 Of course you want at least one of it to be finite or it's goint to be just all the combinations of head as and 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