在Clojure中获得意外的序列实现

在Clojure中获得意外的序列实现,clojure,jvm,out-of-memory,Clojure,Jvm,Out Of Memory,在尝试对复杂计算进行故障排除时注意到一些奇怪的情况。我有一个奇怪的错误,所以我开始逐步建立计算,很早就发现一个非常长的seq被意外地实现了。我有一系列的列表,如下所示: (0 1 2 3) (0 1 2 4) (0 1 2 5) 对于n-choose-k,对于n=143和k=4,有点别出心裁地命名为“combos”。我计划将其作为一个seq进行操作,以保持内存消耗合理,但这失败了: (def combos (combinations 4 143)) (def semantically-also

在尝试对复杂计算进行故障排除时注意到一些奇怪的情况。我有一个奇怪的错误,所以我开始逐步建立计算,很早就发现一个非常长的seq被意外地实现了。我有一系列的列表,如下所示:

(0 1 2 3)
(0 1 2 4)
(0 1 2 5)
对于n-choose-k,对于n=143和k=4,有点别出心裁地命名为“combos”。我计划将其作为一个seq进行操作,以保持内存消耗合理,但这失败了:

(def combos (combinations 4 143))
(def semantically-also-combos
  (filter nil? (map identity combos)))

;; this prints instantly and uses almost no memory, as expected
(println (first combos)) ; prints (0 1 2 3)

;; this takes minutes and runs the JVM out of memory
;; without printing anything
(println (first semantically-also-combos))
根据
type
的说法,它们都是
clojure.lang.LazySeq
,但其中一个按预期工作,另一个使流程崩溃。为什么整个seq只需通过identity函数运行并检查它是否为nil就可以实现

要复制的完整代码
我对示例进行了如下修改:

(ns tst.demo.core
  (:use tupelo.core tupelo.test))

(def cnt (atom 0))

; Copied from rosetta code
(defn combinations
  "If m=1, generate a nested list of numbers [0,n)
   If m>1, for each x in [0,n), and for each list in the recursion on [x+1,n), cons the two"
  [m n]
  (let [comb-aux (fn comb-aux
                   [m start]
                   (swap! cnt inc)
                   (when (zero? (mod @cnt 100))
                     (print \.)
                     (flush))
                   (when (zero? (mod @cnt 10000))
                     (newline)
                     (print @cnt "  "))
                   (if (= 1 m)
                     (for [x (range start n)]
                       (list x))
                     (for [x  (range start n)
                           xs (comb-aux (dec m) (inc x))]
                       (cons x xs))))]
    (comb-aux m 0)))

(println "Generating combinations...")
(def combos (combinations 4 143))

(def should-also-be-combos
  (filter nil? (map identity combos)))
以及调用:

(dotest
  (newline)
  (spyx (type combos))
  (spyx (type should-also-be-combos))
  (newline)
  (println :1)
  (println (first combos))
  (newline)
  (println :2)
  (println (first should-also-be-combos))
  (newline))
结果:

-------------------------------
   Clojure 1.10.2    Java 15
-------------------------------

Testing tst.demo.core

(type combos)                  => clojure.lang.LazySeq
(type should-also-be-combos)   => clojure.lang.LazySeq

:1
(0 1 2 3)

:2
....................................................................................................
10000   ....................................................................................................
20000   ....................................................................................................
30000   ....................................................................................................
40000   ....................................................................................................
50000   ....................................................................................................
60000   ....................................................................................................
70000   ....................................................................................................
80000   ....................................................................................................
90000   ....................................................................................................
100000   ....................................................................................................
110000   ....................................................................................................
120000   ....................................................................................................
130000   ....................................................................................................
140000   ....................................................................................................
150000   ....................................................................................................
160000   ....................................................................................................
170000   ....................................................................................................
180000   ....................................................................................................
190000   ....................................................................................................
200000   ....................................................................................................
210000   ....................................................................................................
220000   ....................................................................................................
230000   ....................................................................................................
240000   ....................................................................................................
250000   ....................................................................................................
260000   ....................................................................................................
270000   ....................................................................................................
280000   ....................................................................................................
290000   ....................................................................................................
300000   ....................................................................................................
310000   ....................................................................................................
320000   ....................................................................................................
330000   ....................................................................................................
340000   ....................................................................................................
350000   ....................................................................................................
360000   ....................................................................................................
370000   ....................................................................................................
380000   ....................................................................................................
390000   ....................................................................................................
400000   ....................................................................................................
410000   ....................................................................................................
420000   ....................................................................................................
430000   ....................................................................................................
440000   ....................................................................................................
450000   ....................................................................................................
460000   ....................................................................................................
470000   ....................................................................................................
480000   ..........................................................................nil
在我的台式计算机上计时(5年前,8核,32Gb RAM):

你可以看到这个函数被调用了大约1/2百万次,在我的电脑上需要5.3秒


我相信您正在看到类似于中描述的内容。它的递归形式也让我想起了著名的例子,这是一个看似无害的函数如何“爆炸”的例子,即使输入值很小。

我对这个例子做了如下修改:

(ns tst.demo.core
  (:use tupelo.core tupelo.test))

(def cnt (atom 0))

; Copied from rosetta code
(defn combinations
  "If m=1, generate a nested list of numbers [0,n)
   If m>1, for each x in [0,n), and for each list in the recursion on [x+1,n), cons the two"
  [m n]
  (let [comb-aux (fn comb-aux
                   [m start]
                   (swap! cnt inc)
                   (when (zero? (mod @cnt 100))
                     (print \.)
                     (flush))
                   (when (zero? (mod @cnt 10000))
                     (newline)
                     (print @cnt "  "))
                   (if (= 1 m)
                     (for [x (range start n)]
                       (list x))
                     (for [x  (range start n)
                           xs (comb-aux (dec m) (inc x))]
                       (cons x xs))))]
    (comb-aux m 0)))

(println "Generating combinations...")
(def combos (combinations 4 143))

(def should-also-be-combos
  (filter nil? (map identity combos)))
以及调用:

(dotest
  (newline)
  (spyx (type combos))
  (spyx (type should-also-be-combos))
  (newline)
  (println :1)
  (println (first combos))
  (newline)
  (println :2)
  (println (first should-also-be-combos))
  (newline))
结果:

-------------------------------
   Clojure 1.10.2    Java 15
-------------------------------

Testing tst.demo.core

(type combos)                  => clojure.lang.LazySeq
(type should-also-be-combos)   => clojure.lang.LazySeq

:1
(0 1 2 3)

:2
....................................................................................................
10000   ....................................................................................................
20000   ....................................................................................................
30000   ....................................................................................................
40000   ....................................................................................................
50000   ....................................................................................................
60000   ....................................................................................................
70000   ....................................................................................................
80000   ....................................................................................................
90000   ....................................................................................................
100000   ....................................................................................................
110000   ....................................................................................................
120000   ....................................................................................................
130000   ....................................................................................................
140000   ....................................................................................................
150000   ....................................................................................................
160000   ....................................................................................................
170000   ....................................................................................................
180000   ....................................................................................................
190000   ....................................................................................................
200000   ....................................................................................................
210000   ....................................................................................................
220000   ....................................................................................................
230000   ....................................................................................................
240000   ....................................................................................................
250000   ....................................................................................................
260000   ....................................................................................................
270000   ....................................................................................................
280000   ....................................................................................................
290000   ....................................................................................................
300000   ....................................................................................................
310000   ....................................................................................................
320000   ....................................................................................................
330000   ....................................................................................................
340000   ....................................................................................................
350000   ....................................................................................................
360000   ....................................................................................................
370000   ....................................................................................................
380000   ....................................................................................................
390000   ....................................................................................................
400000   ....................................................................................................
410000   ....................................................................................................
420000   ....................................................................................................
430000   ....................................................................................................
440000   ....................................................................................................
450000   ....................................................................................................
460000   ....................................................................................................
470000   ....................................................................................................
480000   ..........................................................................nil
在我的台式计算机上计时(5年前,8核,32Gb RAM):

你可以看到这个函数被调用了大约1/2百万次,在我的电脑上需要5.3秒


我相信您正在看到类似于中描述的内容。它的递归形式也让我想起了著名的例子,这是一个看似无害的函数如何“爆炸”的例子,即使输入值很小。

为什么不检查
(filter nil?(map identity combos))
是否实际返回了传递给
组合的更小参数所期望的结果?我做到了。这是
组合
返回的参数2和5:

(combinations 2 5)
;; => ((0 1) (0 2) (0 3) (0 4) (1 2) (1 3) (1 4) (2 3) (2 4) (3 4))
这是我们通过示例中的额外延迟序列操作得到的结果:

(filter nil? (map identity (combinations 2 5)))
;; => ()
(filter nil?…)
所做的是保留满足谓词的所有元素。并且输入序列中没有一个元素是
nil?
,因此它将扫描整个输入序列,并且不会找到任何元素。也许您想使用
(remove nil?…)
,这将删除满足谓词的元素

(remove nil? (map identity (combinations 2 5)))
;; => ((0 1) (0 2) (0 3) (0 4) (1 2) (1 3) (1 4) (2 3) (2 4) (3 4))
回到原始示例,这是我使用
remove
得到的结果:

(first (combinations 4 143))
;; => (0 1 2 3)

(first (remove nil? (map identity (combinations 4 143))))
;; => (0 1 2 3)

我的一般建议是先用“较小”的数据(如2和5)测试函数,然后再使用“较大”的数据(如4和143)。

为什么不检查
(filter nil?(map identity combos))
是否实际返回传递给
组合的更小参数的结果?我做到了。这是
组合
返回的参数2和5:

(combinations 2 5)
;; => ((0 1) (0 2) (0 3) (0 4) (1 2) (1 3) (1 4) (2 3) (2 4) (3 4))
这是我们通过示例中的额外延迟序列操作得到的结果:

(filter nil? (map identity (combinations 2 5)))
;; => ()
(filter nil?…)
所做的是保留满足谓词的所有元素。并且输入序列中没有一个元素是
nil?
,因此它将扫描整个输入序列,并且不会找到任何元素。也许您想使用
(remove nil?…)
,这将删除满足谓词的元素

(remove nil? (map identity (combinations 2 5)))
;; => ((0 1) (0 2) (0 3) (0 4) (1 2) (1 3) (1 4) (2 3) (2 4) (3 4))
回到原始示例,这是我使用
remove
得到的结果:

(first (combinations 4 143))
;; => (0 1 2 3)

(first (remove nil? (map identity (combinations 4 143))))
;; => (0 1 2 3)

我的一般建议是,先用“较小”的数据(如2和5)测试你的功能,然后再使用“较大”的数据(如4和143)。

是的,它在大约60秒的时间内耗尽了我微薄的macbook air的内存。那篇文章似乎是一个很好的开始,谢谢!是的,它在大约60秒内就把我那微不足道的macbook air的内存用完了。那篇文章似乎是一个很好的开始,谢谢!你是对的,通缉
(不是(nil?x))
,愚蠢的错误。在REPL中尝试东西有助于发现这些错误。你是对的,通缉
(不是(nil?x))
,愚蠢的错误。在REPL中尝试东西有助于发现这些错误。