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

时间:2015-04-03 01:44:48

标签: clojure stack-overflow list-comprehension

我很难跟踪我的Bron-Kerbosch algorithm的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。根据注释掉的print语句的输出来判断(clique总是打印为EmptyList)我假设我的列表理解也可能是问题。

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

我的整体问题是导致这种情况的原因?虽然这有些过于开放,但另一个可能导致我回答的问题是我如何在第一行使用print语句。

取消注释print语句时,它会产生输出

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中。在尝试再次调用该函数后,我得到了#34;无法将长期投射到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语句停止后(不知道为什么我知道为什么我把它们放进去了)我发现大多数(如果不是全部)语句

的印刷线
"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而不是删除它们。这会导致P不断增长。这很可能是我的堆栈溢出的原因。虽然还存在一些问题。我还在创建一个stackoverflow,但是又一次。

一旦我解决了继续加入P的问题,我的问题就是当我concat loop-cnt它不是,我想说,评估为一个值,但它仍然是一个变量名loop-cnt。我怀疑我的堆栈溢出现在在于我的引导条件未得到满足,因为如果loop-cnt未被评估为数字,则无法满足它。

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

2 个答案:

答案 0 :(得分:0)

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

这种连接是否需要保持懒惰?如果没有,请在concat的来电中将来电置于doall。这将使串联变得急切,这减少了实现最终结果所需的调用堆栈的大小,从而消除了StackOverflowError。

此外,打印lazy-seq并查看内容的正确方法是prn,您可以使用pr-str获取pr或{{1}值的格式如果需要,它将用作字符串。

答案 1 :(得分:0)

我认为你滥用引用名单。

例如,在(defn Bron-Kerbosch1 [ ... ] ... )中,'(cliques)评估为包含符号 cliques的列表,而不是包含参数的列表 cliques作为其中的一个元素。你想要(list cliques)

类似的案例比比皆是。