我正在尝试实现一个带有两个列表的函数(如下所述)(每个列表可能是无限的)并返回列表中所有可能元素对的元组列表
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 :(得分:11)
这是一项很棒的练习!
如果我们在无限表中列出你的输入对:
(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表diagonal
的{{1}}来实现您的功能,这是我开始回答的表(可以使用嵌套列表理解,以及其他各种方式构建) )。
注意:
名称zip具有误导性。这更像是笛卡儿的产品。
你知道你可以匹配模式中的模式,对吗?即如果[[(a,b)]]
f :: [[a]] -> something
将f ((x:xs):xss) = ...
作为第一行的第一个元素,x
是第一行的其余部分,xs
是表格的其余部分。
答案 1 :(得分:5)
虽然这对于理解列表和Haskell来说是一个很好的练习,但对于理解Applicative
类的所有内容来说,这也是一个很好的练习。特别是[]
的{{1}}实例。您想要的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
获取不同的语义,[]
至少还有一个其他列表实例:Applicative
ZipList
此实例为其λ: :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')]
类型类的良好介绍,因为它们很容易获得,相当直观,有助于防止您制作错误,并且表明存在单个类型具有多个类型的情况类型类的一个实例。
答案 2 :(得分:4)
这是您发布的帮助函数:
oneList :: [a] -> [b] [(a,b)]
oneList [] _ = []
oneList x:xs y:ys = [(x,y)] ++ oneList
以下是它包含的语法错误:
你在类型注释中遗漏了一个箭头;它应该是
oneList :: [a] -> [b] -> [(a,b)]
你需要在parens中包含非平凡的模式,所以第二个等式应该开始
oneList (x:xs) (y:ys) =
oneList
在返回列表之前接受两个参数,但在第二个等式的rhs中,您尝试将其用作列表而不给它任何参数
(顺便说一句,如果您发布错误消息而不是仅仅说它不能编译它通常会对我们有帮助。将我上面指出的错误与编译器给出的错误消息进行比较。)
但是正如您所说,您的算法是错误的。
我觉得这是作业,所以我只是给你一个提示。
zipInf
应该是
zipInf :: [a] -> [b] -> [(a,b)]
zipInf xs ys = thread (expand xs ys)
thread
和expand
是我要写的两个辅助函数,带有类型签名
expand :: [a] -> [b] -> [[(a,b)]]
thread :: [[c]] -> [c]
expand
非常简单。 thread
是您必须小心按顺序包含元素的地方(因此您不能只说thread zs = concat zs
,即使类型正确)。
答案 3 :(得分:1)
您需要将oneList
应用于xs
和ys
。
oneList :: [a] -> [b] -> [(a, b)]
oneList [] _ = []
oneList (x:xs) (y:ys) = (x, y) : oneList xs ys
无限列表将自动起作用,因为Haskell很懒惰。
答案 4 :(得分:0)
重要提示:请参阅下面的Will Ness评论。
您的问题暗示订单无关紧要。 (但由于列表可能是无限的,订单可能比您想象的更重要!)无论如何,如果订单无关紧要,并且您遇到了 list comprehensions ,那么这是您可以使用的一种方法。这是一个例子。
λ> 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'..]]
但是像这样的人需要一个LOOOOONG时间:
(200000,'a') `elem` [(x,y) | x <- [1..], y <- ['a'..]]
如果订单很重要,或者您没有遇到列表推导,或者不想使用它们,那么luqui的方法可能正是您所寻找的。 p>
答案 5 :(得分:0)
您可以通过使用列表理解非常简单地完成此操作。
zip :: [a] -> [b] -> [(a,b)]
zip [] _ = []
zip _ [] = []
zip as bs = [(a, b) | a <- as, b <- bs]
它与有限列表和无限列表完美配合。当然,您至少希望其中之一是有限的,或者只是head as
和bs
的所有组合都是有问题的。
> 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)]