【问题标题】:Lazy cartesian product in HaskellHaskell中的惰性笛卡尔积
【发布时间】:2012-04-24 18:11:36
【问题描述】:

我想在 Haskell 中生成一个相当大但有限的笛卡尔积,然后我需要对其进行迭代(想想平均场模型的分区函数)。理所当然的使用sequence,像这样:

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

不幸的是,对于大的n,这不适合内存,例如,我一请求length l,就会用完堆。我需要一种方法来懒惰地做同样的事情。我最终“重新发现”了 base-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

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

【问题讨论】:

  • 你关心结果中元素的顺序吗?
  • 没有,只要没有重复即可。
  • n 需要多大?
  • 20 或 30 之类的;我现在并不真正关心计算时间,但肯定3^n 超出了 RAM 大小。

标签: haskell lazy-evaluation cartesian-product


【解决方案1】:

与序列相比,以相反的顺序绑定可以获得更内存友好的方式,

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

由于共享较少,速度较慢,但​​由于内存是您的问题,也许它对您来说已经足够了。

【讨论】:

  • 酷!我将不得不更多地调查它为什么有效,但它确实有效。我将其修改为foo (l:ls) = [h:t | t &lt;- foo2 ls, h &lt;- l](谁知道我是否总是需要一个立方体),它也可以正常工作。谢谢!
  • 为什么列表推导比列表的 do-notation 更有效(在 sequence 中使用)?我可以从 Haskell2010 报告中看到他们都脱糖到concatMap?
  • @brence:请参阅 Duncan Coutts 对此 reddit 问题的回答:Why are guards in the list comprehension faster than in the do-notation?
  • 从那里看来,列表推导式被脱糖成foldr。奇怪的是,一种幼稚的foldr 处理笛卡尔积的方法(至少我尝试过的方法)打破了像sequence 那样的懒惰......
  • @danr,几年前我将concatMap 和/或Monad 实例的定义更改为[],所以它现在的编译应该与列表理解版本基本相同。我记得,我们现在定义 concatMap f xs = [f x | x &lt;- xs] 或类似名称。
【解决方案2】:

无限列表的模式是从左上角 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]

【讨论】:

  • 嗨,关于那个非常古老的问题的一些活动!但我不确定您的回复与它有何关系,您是否打算将其用于另一个问题?
  • diag 是一个懒惰的cartesion产品。试试吧。正如我所说,我更喜欢一个惰性函数来实现反向列表,即惰性rslplr 是另一个使用惰性rsl 的惰性笛卡尔积函数。如果您不将take 与所有这些功能一起使用,它们将永远运行。
猜你喜欢
  • 1970-01-01
  • 2011-09-18
  • 2016-05-07
  • 1970-01-01
  • 2011-05-06
  • 2015-12-05
  • 2016-02-15
  • 2013-12-29
相关资源
最近更新 更多