Haskell中的懒惰笛卡尔积

时间:2012-04-12 11:44:18

标签: haskell

我想在Haskell中生成一个相当大但有限的笛卡尔积,然后我需要迭代(想想平均场模型的分区函数)。自然而然地使用sequence,如下所示:

l = sequence $ replicate n [0,1,2]

不幸的是,对于大型n,这不适合内存,并且一旦我要求length l,我就会用完堆。我需要一种方法来懒散地做同样的事情。我最终“重新发现”基础3算术,像这样,

nextConfig []     = []
nextConfig (0:xs) = 1:xs
nextConfig (1:xs) = 2:xs
nextConfig (2:xs) = 0:(nextConfig xs)

ll = take (3^n) $ iterate nextConfig $ replicate n 0

(有效)但感觉就像重新发明轮子一样,而且它太具体了。什么是更好的懒惰方式来生成产品?

2 个答案:

答案 0 :(得分:5)

通过与序列相比以相反的顺序进行绑定,获得了更加内存友好的方式,

foo 0 _ = [[]]
foo k xs = [h:t | t <- foo (k-1) xs, h <- xs]

由于共享较少,速度较慢,但​​由于记忆是你的问题,也许这对你来说已经足够了。

答案 1 :(得分:0)

无限列表的模式是从左上角a1然后a2向下到b1,a3向下到c1

a1 a2 a3 a4
b1 b2 b3 b4
c1 c2 c3 c4
d1 d2 d3 d4

也就是说,a1, a2 b1, a3 b2 c1, a4 b3 c2 d1 逗号分隔组中的字母是 a ab abc abcd 和数字 1 21 321 4321

无限列表输出应该首先对角地增长 a1,然后对角地添加 2,然后对角地添加 3,依此类推。

以上单个字母或数字必须逆向进行。

diag xs ys = [(a,b)| (m,n) <- zip (inits xs) (inits ys), (a,b) <- zip m $ reverse n ]

每个 (m,n) 对都有一个反向。我更喜欢一次性全部反转,重复一个操作。我认为,无限列表的反向只能通过连续反向列表来完成。

rsl = tail.scanl (flip (:)) []

take 5 $ rsl [1..]

[[1],[2,1],[3,2,1],[4,3,2,1],[5,4,3,2,1]]

然后依次处理每个列表。 zip 限制每次迭代中的非反转列表。

plr xs ys = [ (a,b) | ls <- rsl xs, (a,b) <- zip ls ys ]

sort.take 10.plr ['a'..] $ [1..]

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

sort,因为对角取值意味着它们不像上面的第一个线性列表那样按顺序排列。 sort 是从 Data.List

导入的

顺便说一句,因为这是遍历对角三角形的数字发挥作用。我在上面使用了 take 10,三角数是:

take 11 [sum ls | ls <- rsl [1..]]

[1,3,6,10,15,21,28,36,45,55,66]