无限列表的笛卡儿积分haskell

时间:2013-12-11 10:17:59

标签: haskell

我有一个有限列表函数

> kart :: [a] -> [b] -> [(a,b)]
> kart xs ys = [(x,y) | x <- xs, y <- ys]

但如何为无限列表实现它?我听说过康托尔和集合理论......?

我还找到了像

这样的功能
> genFromPair (e1, e2) = [x*e1 + y*e2 | x <- [0..], y <- [0..]]

但是我不确定它是否有帮助,因为拥抱只会在没有中断的情况下发出对。

感谢您的帮助。

3 个答案:

答案 0 :(得分:15)

您的第一个定义kart xs ys = [(x,y) | x <- xs, y <- ys]等同于

kart xs ys = xs >>= (\x ->
             ys >>= (\y -> [(x,y)]))

,其中

(x:xs) >>= g = g x ++ (xs >>= g)
(x:xs) ++ ys = x : (xs ++ ys)

是顺序操作。将它们重新定义为交替操作,

(x:xs) >>/ g = g x +/ (xs >>/ g)
(x:xs) +/ ys = x : (ys +/ xs)
[]     +/ ys = ys

你的定义也应该适合无限列表:

kart_i xs ys = xs >>/ (\x ->
               ys >>/ (\y -> [(x,y)]))

检测,

Prelude> take 20 $ kart_i [1..] [100..]
[(1,100),(2,100),(1,101),(3,100),(1,102),(2,101),(1,103),(4,100),(1,104),(2,102)
,(1,105),(3,101),(1,106),(2,103),(1,107),(5,100),(1,108),(2,104),(1,109),(3,102)]

"The Reasoned Schemer"提供。 (另见conda, condi, conde, condu)。


另一种更明确的方式是创建单独的子流并将它们组合起来:

kart_i2 xs ys = foldr g [] [map (x,) ys | x <- xs]
  where
     g a b = head a : head b : g (tail a) (tail b)

这实际上产生了完全相同的结果。但现在我们可以更好地控制我们如何组合子流。我们可以be more diagonal

kart_i3 xs ys = g [] [map (x,) ys | x <- xs]
  where                                          -- works both for finite 
     g [] [] = []                                --  and infinite lists
     g a  b  = concatMap (take 1) a
                ++ g (filter (not.null) (take 1 b ++ map (drop 1) a))
                     (drop 1 b)

所以现在我们得到了

Prelude> take 20 $ kart_i3 [1..] [100..]
[(1,100),(2,100),(1,101),(3,100),(2,101),(1,102),(4,100),(3,101),(2,102),(1,103)
,(5,100),(4,101),(3,102),(2,103),(1,104),(6,100),(5,101),(4,102),(3,103),(2,104)]

对于某些searching on SO,我还发现answer by Norman Ramsey似乎还有另一种生成序列的方法,将这些子流分成四个区域 - 左上角,左上角,左列,其余的。他的merge与我们的+/相同。


你的第二个定义,

genFromPair (e1, e2) = [x*e1 + y*e2 | x <- [0..], y <- [0..]]

等同于

genFromPair (e1, e2) = [0*e1 + y*e2 | y <- [0..]]

因为列表[0..]是无限的,所以x的任何其他值都无法发挥作用。 这个是上述定义都试图避免的问题。

答案 1 :(得分:0)

Prelude> let kart = (\xs ys -> [(x,y) | ls <- map (\x -> map (\y -> (x,y))  ys)  xs, (x,y) <- ls])
Prelude> :t kart
kart :: [t] -> [t1] -> [(t, t1)]
Prelude> take 10 $ kart [0..] [1..]
[(0,1),(0,2),(0,3),(0,4),(0,5),(0,6),(0,7),(0,8),(0,9),(0,10)]
Prelude> take 10 $ kart [0..] [5..10]
[(0,5),(0,6),(0,7),(0,8),(0,9),(0,10),(1,5),(1,6),(1,7),(1,8)]

答案 2 :(得分:0)

模式

这是另一个人函数的正确输出。

读取斜条纹,从右上到左下,除了左上角:

a1 a2 a3 a4
b1 b2 b3
c1 c2
d1

a1 - a2 b1 - a3 b2 c1 - a4 b3 c2 d1

格局呈上升趋势。首先是一组中的 1 个,然后一组中的 2 个,依此类推。 第一个数字表示同样多。在每一组中,数字是颠倒的。 字母在每组中按顺序排列,而数字在每组中的顺序相反。

给定 1 1 - 2 2 - 3 3 的公式可以反转数字 基数为 3 是 3+1-2nd digit or 3+1-1= 3, 3+1-2= 2, 3+1-3= 1 将第二个数字转换为 1 3 - 2 2 -3 1

所以模式是

[(m,n) |i <-[1..4], (m,n) <-zip (take i "abcd") (reverse $ take i "1234")]

生产 [('a','1'),('a','2'),('b','1'),('a','3'),('b','2') ,('c','1'),('a','4'),('b','3'),('c','2'),('d','1') ]

与此处给出的第一个模式匹配。读取 1 组,然后是 2 组,然后是 3 组,然后是 4 组。

为了使这个通用和无限

diag2 xs ys = [(m,r) |n <-[1..], (m,r) <-zip (take n xs) (reverse $ take n ys)]
take 21 $ diag2 [1..] [1..]

[(1,1),(1,2),(2,1),(1,3),(2,2),(3,1),(1,4),(2,3) ),(3,2),(4,1),(1,5),(2,4),(3,3),(4,2),(5,1),(1,6), (2,5),(3,4),(4,3),(5,2),(6,1)]