首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >math.combinatorics中的闭包懒惰序列会导致OutOfMemory (OOM)错误

math.combinatorics中的闭包懒惰序列会导致OutOfMemory (OOM)错误
EN

Stack Overflow用户
提问于 2013-04-24 14:36:36
回答 4查看 659关注 0票数 7

math.combinatorics的文档声明所有函数都返回惰性序列。

但是,如果我尝试使用大量数据运行子集

代码语言:javascript
复制
(last (combinatorics/subsets (range 20)))
;OutOfMemoryError Java heap space  clojure.lang.RT.cons (RT.java:559)

我得到了一个OutOfMemory错误。

正在运行

代码语言:javascript
复制
(last (range))

烧毁CPU,但不返回错误。

Clojure似乎不像解释的在这个堆栈溢出问题中那样“按住头”。

为什么会发生这种情况,以及我如何在子集中使用更大的范围?

更新

正如评论所暗示的,它似乎适用于某些人的电脑。所以我会发布我的系统配置

我运行Mac (10.8.3),用自制软件安装克洛尔 (1.5.1)。

我的Java版本是:

代码语言:javascript
复制
% java -version
java version "1.6.0_45"
Java(TM) SE Runtime Environment (build 1.6.0_45-b06-451-11M4406)
Java HotSpot(TM) 64-Bit Server VM (build 20.45-b01-451, mixed mode)

我没有更改任何默认设置。我还通过删除~/.m2文件夹重新安装了所有依赖项。

我的projects.clj

我用的命令是

代码语言:javascript
复制
% lein repl
nREPL server started on port 61774
REPL-y 0.1.10
Clojure 1.5.1
=> (require 'clojure.math.combinatorics)
nil
=> (last (clojure.math.combinatorics/subsets (range 20)))
OutOfMemoryError Java heap space  clojure.lang.RT.cons (RT.java:570)
or
OutOfMemoryError Java heap space  clojure.math.combinatorics/index-combinations/fn--1148/step--1164 (combinatorics.clj:64)

我在一位同事的笔记本上测试了这个问题,他也有同样的问题,但他也是在Mac电脑上。

EN

回答 4

Stack Overflow用户

发布于 2013-04-29 01:44:55

问题是subsets使用mapcat,而mapcat不够懒惰,因为它使用apply来实现和保存一些要连接的元素。见很好的解释。在子集中使用更延迟的mapcat版本的链接应该可以解决以下问题:

代码语言:javascript
复制
(defn my-mapcat
   [f coll]
   (lazy-seq
     (if (not-empty coll)
      (concat
      (f (first coll))
     (my-mapcat f (rest coll))))))

(defn subsets
  "All the subsets of items"
  [items]
  (my-mapcat (fn [n] (clojure.math.combinatorics/combinations items n))
  (range (inc (count items)))))

 (last (subsets (range 50))) ;; this will take hours to compute, good luck with it!
票数 3
EN

Stack Overflow用户

发布于 2013-04-29 21:42:26

你想要计算1000个元素集的幂集吗?你知道那会有2^1000元素,对吧?它太大了,我甚至找不到一个很好的方法来描述它有多大。如果您尝试使用这样的集合,并且可以懒洋洋地这样做,那么您的问题将不是内存:它将是计算时间。假设你有一台拥有无限内存的超级计算机,它能够每毫秒处理1万亿个项目:即每秒处理10^21个项目,或者每年处理10^29个项目。即使是这台超级计算机,也需要比宇宙的生命周期长得多的时间来处理(subsets (range 1000))的项目。

所以我要说的是,不要担心这个集合的内存使用情况,而要研究一种算法,它不需要遍历包含比宇宙中原子更多的元素的序列。

票数 3
EN

Stack Overflow用户

发布于 2013-07-20 10:47:15

问题既不是apply,也不是concat,也不是mapcat

丹妮的回答是他重新实现mapcat的地方,他无意中解决了这个问题,但是背后的推理是不正确的。此外,他的回答指向了一篇文章,其中作者说“我相信问题在于应用”。,这显然是错误的,我将在下面解释。最后,手头的问题与另一个无关,因为非懒惰的计算确实是由apply引起的

如果仔细观察,dAni和那篇文章的作者都实现了mapcat,而不使用map函数。在下一个示例中,我将说明这个问题与map函数的实现方式有关。

要演示该问题与applyconcat都无关,请参阅下面的mapcat实现。它同时使用concatapply,但仍然实现了完全的懒惰:

代码语言:javascript
复制
(defn map
  ([f coll]
     (lazy-seq
      (when-let [s (seq coll)]
        (cons (f (first s)) (map f (rest s)))))))

(defn mapcat [f & colls]
  (apply concat (apply map f colls)))

(defn range-announce! [x]
  (do (println "Returning sequence of" x)
      (range x)))

;; new fully lazy implementation prints only 5 lines
(nth (mapcat range-announce! (range)) 5)

;; clojure.core version still prints 32 lines
(nth (clojure.core/mapcat range-announce! (range)) 5)

以上代码中的完全延迟是通过重新实现map函数来实现的。事实上,mapcatclojure.core一样是实现完全一样的,但是它完全是懒惰的。为了示例起见,上面的map实现稍微简化了一些,因为它只支持一个参数,但是即使用整个可变签名实现它,也是一样的:完全惰性。因此,我们证明了这里的问题既不是apply,也不是concat。此外,我们还表明,真正的问题必须与map函数如何在clojure.core中实现有关。让我们看一看:

代码语言:javascript
复制
(defn map
  ([f coll]
   (lazy-seq
    (when-let [s (seq coll)]
      (if (chunked-seq? s)
        (let [c (chunk-first s)
              size (int (count c))
              b (chunk-buffer size)]
          (dotimes [i size]
              (chunk-append b (f (.nth c i))))
          (chunk-cons (chunk b) (map f (chunk-rest s))))
        (cons (f (first s)) (map f (rest s))))))))

可以看出,clojure.core实现与我们之前的“简化”版本完全相同,但if (chunked-seq? s)表达式的true分支除外。本质上,clojure.core/map有一个特殊的情况来处理输入序列,这些输入序列是块序列。

分块的序列通过对32块而不是严格地一次进行评估来减少懒惰。在评估深度嵌套的分块序列时,这一点非常明显,比如subsets。闭包1.1中引入了块序列,并对许多核心功能进行了升级,以不同的方式识别和处理它们,包括map。引入它们的主要目的是提高在某些流处理场景中的性能,但可以说,它们使得对程序的惰性特性进行推理变得非常困难。您可以阅读分块序列这里这里。还可以查看这个问题,这里

真正的问题是,range返回一个分块的seq,并在内部由subsets使用。大卫·詹姆斯修补程序subsets推荐的修补程序可以在内部取消range创建的序列块。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16194841

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档