如何计算一个非常大的惰性序列中的元素数量?

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 万长列表中占据首位。我怀疑这是你的问题。

这个故事的寓意是,惰性序列有时很有用,但很棘手且容易出错。然后不是奇迹,经常会导致意想不到的错误,我更喜欢尽可能使用具体的向量。

如果没有其他选择,我建议只使用惰性序列。

【讨论】:

以上是关于如何计算一个非常大的惰性序列中的元素数量?的主要内容,如果未能解决你的问题,请参考以下文章

如何通过 Swift 中的闭包设置惰性计算属性的值?

Python函数-5 生成器

如何计算 NumPy bool 数组中真实元素的数量

如何使用 Diesel 计算数组列中不同元素的数量?

Selenium WebDriver:如何计算列表中 DIV 元素的数量

如何解决获取惰性列表的第一个元素的问题?