在Clojure程序中跟踪StackOverflow,包含SSCCE



我很难找到由Bron-Kerbosch算法的clojure实现产生的stackoverflow错误。

下面是我对算法本身的实现,这里有一个SSCCE的链接,概述了这个问题http://pastebin.com/gtpTT4B4.

(defn Bron-Kerbosch [r p x graph cliques]
  ;(println (str "R:" r " P:" p " X:" x " Clq:" cliques))
  (cond (and (empty? p) (empty? x)) (concat cliques r)
        :else
        (let [neigh (neighV graph (dec (count p)))]
          (loop [loop-clq '(cliques)
                 loop-cnt (dec (count p))
                 loop-p '(p)
                 loop-x '(x)]
            (cond (= -1 loop-cnt) loop-clq
                  :else
                  (recur (concat loop-clq 
                                 (Bron-Kerbosch (concat r loop-cnt) 
                                                (concat p neigh) 
                                                (filter (set x) neigh) 
                                                graph cliques))
                         (dec loop-cnt)
                         (filter (set p) loop-cnt)
                         (concat x loop-cnt)))))))

我不得不假设问题显然存在于我的两个引导条件(cond (and (empty? p) (empty? x))(cond (= -1 loop-cnt)中的一个之内,因为函数算法是递归的。

尽管这是假设我正在正确地构建列表x r p。从注释输出的打印声明(派系总是作为EmptyList打印)的输出来看,我认为我的列表理解可能也是问题所在。

同样,我可以看到的另一个问题是,我实际上没有在BK-Call函数(在SSCCEE中)中正确调用算法。

我的总体问题是,是什么导致了这种情况?尽管这有点过于开放,但另一个可能让我找到答案的问题是,我如何在第一行使用打印语句。

当取消对打印语句的注释时,它会产生输出

R:clojure.lang.LazySeq@2044e0b9 P:clojure.lang.LazySeq@7f9700a5 X:clojure.lang.LazySeq@1 Clq:clojure.lang.PersistentList$EmptyList@1

我想,如果我能看到x r p在每次调用中,我可能就能看到算法哪里出了问题。

有人能给我指正确的方向吗?

编辑:SSCCE 的neighV功能

(defn neighV [graph nodenum]
  (let [ret-list (for [i (range (count graph)) :when (contains? (graph i) nodenum)] i)]
    ret-list))

EDIT2:Noisesmith的回答让我更接近解决方案,对我来说也很有意义。我把所有的concat都封装在doall中。在再次尝试调用该函数后,我遇到了"Cannot cast Long to Seq"错误,我认为这些错误源于尝试将concat loop-cnt添加到函数中的列表中

fptests.core> (BK-Call (sanity1))
IllegalArgumentException Don't know how to create ISeq from: java.lang.Long  clojure.lang.RT.seqFrom (RT.java:505)
fptests.core> (concat 1 '(2 3))
IllegalArgumentException Don't know how to create ISeq from: java.lang.Long  clojure.lang.RT.seqFrom (RT.java:505)

因此,我将每个loop-cnt封装在'()中,在它成为concat 之前将其转换为列表

fptests.core> (concat '(1) '(2 3))
(1 2 3)

在我做了所有这些更改之后,我又回到了堆栈溢出。。这是带有所有编辑的新Bron-Kerbosch函数。我想我现在有和以前一样的问题。。

尽管新的是,我是否正确地实现了我应该实现的更改,但使用'()来解决在实现noisesmith的更改后出现的问题有意义吗?

(defn Bron-Kerbosch1 [r p x graph cliques]
  (cond (and (empty? p) (empty? x)) (doall (concat cliques r))
        :else
        (let [neigh (neighV graph (dec (count p)))]
          (loop [loop-clq '(cliques)
                 loop-cnt (dec (count p))
                 loop-p '(p)
                 loop-x '(x)]
            (cond (= -1 loop-cnt) loop-clq
                  :else
                  (recur (doall (concat loop-clq 
                                        (Bron-Kerbosch1 (doall (concat r '(loop-cnt))) 
                                                       (doall (concat p neigh)) 
                                                       (filter (set x) neigh) 
                                                       graph cliques)))
                         (dec loop-cnt)
                         (filter (set p) '(loop-cnt))
                         (doall (concat x '(loop-cnt)))))))))

EDIT3:在耐心等待我的prn语句停止后(不确定我知道它在SO中时为什么要把它们放进去),我发现打印的大多数语句(如果不是所有的话)都是按照的行

"R:" (loop-cnt loop-cnt loop-cnt loop-cnt loop-cnt loop-cnt loop-cnt ...)
"P:" (range (count graph) 0 2 3) " X:" () " Clq:" ()

在检查了一些之后,我意识到我没有正确地递归调用函数。我一直在将项目统一到P,而不是删除它们。这导致CCD_ 18持续生长。这很可能是我的堆栈溢出的原因。尽管仍然存在一些问题。我仍然在创造一个堆叠的流动,再一次。

一旦我解决了继续联合到p的问题,我的问题是,当我concatloop-cnt时,我想说,它不会被求值为一个值,但它仍然是一个变量名loop-cnt。我怀疑我的堆栈溢出现在是因为我的引导条件没有得到满足,因为如果loop-cnt没有被计算为一个数字,它就不能得到满足。

所以我认为我现在的问题在于concat loop-cnt作为一个数字而不是一个变量。

concat是懒惰的。在concat之上构建concat的递归调用,没有实现任何先前的惰性层,每个调用都增加了实现惰性seq所需的调用堆栈的大小。

这种串联需要懒惰吗?如果没有,请将对concat的调用封装在对doall的调用中。这将使串联变得迫切,从而减少了实现最终结果所需的调用堆栈的大小,从而消除了StackOverflowError。

此外,打印懒惰序列并查看内容的正确方法是prn,如果需要,可以使用pr-str获取prprn将用作字符串的值的形式。

我认为您滥用了带引号的列表。

例如,在(defn Bron-Kerbosch1 [ ... ] ... )中,'(cliques)的计算结果为包含符号cliques的列表,而不是包含自变量cliques作为其一个元素的列表。你需要(list cliques)

类似的案例比比皆是。

最新更新