我很难跟踪我的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
列表作为数字而不是变量。
答案 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)
。
类似的案例比比皆是。