如何计算一个非常大的惰性序列中的元素数量?
Posted
技术标签:
【中文标题】如何计算一个非常大的惰性序列中的元素数量?【英文标题】:How to count the number of elements in an extremely large lazy seq? 【发布时间】:2021-12-14 18:32:44 【问题描述】:我有以下(非常大)lazy-seq
:
(def lazy-list (partition-all 100000 (take 10000000000000000000 (repeat 1))))
我想计算其中的元素数量,为此我正在执行以下操作:
(time
(loop [ll lazy-list
c 0]
(if-not (seq (take 1 ll))
c
(recur (drop 1 ll)
(inc c)))))
如果我运行它,我会收到以下错误:
Execution error (OutOfMemoryError) at user/eval2043$fn (REPL:1).
Java heap space
但如果我没有在任何地方持有head
,为什么我会看到这个OOM
问题?
【问题讨论】:
【参考方案1】:但是如果我没有在任何地方握住头部......?序列的头部由lazy-list
持有。
【讨论】:
【参考方案2】:我尝试了一个稍微不同的版本,它成功了:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(dotest
(let [obj (vec (repeat 100000 2)) ]
(loop [ll (repeat 1e8 obj)
cnt 0]
; (spyx (type ll)) ; first is "clojure.lang.Repeat", rest are "clojure.lang.LazySeq"
(if-not (seq (take 1 ll))
(spyx cnt)
(recur (drop 1 ll)
(inc cnt))))
))
结果
-------------------------------
Clojure 1.10.3 Java 17
-------------------------------
Testing tst.demo.core
cnt => 100000000
Ran 2 tests containing 0 assertions.
0 failures, 0 errors.
Passed all tests
Finished at 11:42:52.954 (run time: 10.706s)
回到你的例子,我试过了:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(def take-item (take 1e7 (repeat 1)))
(def lazy-list (partition-all 1e4 take-item ))
(dotest
(spyx (type take-item))
(spyx (type lazy-list))
(time
(loop [ll lazy-list
c 0]
(if-not (seq (take 1 ll))
(spyx c)
(recur (drop 1 ll)
(inc c)))))
)
结果
-------------------------------
Clojure 1.10.3 Java 17
-------------------------------
Testing tst.demo.core
(type take-item) => clojure.lang.LazySeq
(type lazy-list) => clojure.lang.LazySeq
c => 1000
"Elapsed time: 2787.211076 msecs"
Ran 2 tests containing 0 assertions.
0 failures, 0 errors.
Passed all tests
Finished at 11:50:42.880 (run time: 2.811s)
但请注意,take-item
在 1000 万长列表中占据首位。我怀疑这是你的问题。
这个故事的寓意是,惰性序列有时很有用,但很棘手且容易出错。然后不是奇迹,经常会导致意想不到的错误,我更喜欢尽可能使用具体的向量。
如果没有其他选择,我建议只使用惰性序列。
【讨论】:
以上是关于如何计算一个非常大的惰性序列中的元素数量?的主要内容,如果未能解决你的问题,请参考以下文章