(另一个)Clojure中循环重复的堆栈溢出

gal*_*dre 7 clojure

类似的问题:,,.

我在这里彻底搞砸了.我正在使用循环重复形式,我正在使用doall,但我仍然得到大循环的堆栈溢出.我的Clojure版本是1.5.1.

背景:我正在训练神经网络来模仿XOR.该功能xor是前馈功能,接受权重和输入并返回结果; 该函数b-xor是反向传播函数,它根据最后一次调用的结果返回更新的权重xor.

以下循环运行得很好,运行速度非常快,并返回结果,并根据返回的结果,完美地训练权重:

(loop [res 1        ; <- initial value doesn't matter
       weights xorw ; <- initial pseudo-random weights
       k 0]         ; <- count
  (if (= k 1000000)
      res
      (let [n (rand-int 4)
            r (doall (xor weights (first (nth xorset n))))]
        (recur (doall r)
               (doall (b-xor weights r (second (nth xorset n))))
               (inc k)))))
Run Code Online (Sandbox Code Playgroud)

但是,当然,这只能给我最后一次运行的结果.显然我想知道为了获得这个结果而训练了多少重量!以下循环,只返回值更改,溢出:

(loop [res 1
       weights xorw
       k 0]
  (if (= k 1000000)
      weights              ; <- new return value
      (let [n (rand-int 4)
            r (doall (xor weights (first (nth xorset n))))]
        (recur (doall r)
               (doall (b-xor weights r (second (nth xorset n))))
               (inc k)))))
Run Code Online (Sandbox Code Playgroud)

这对我来说没有意义.weights每个调用中使用的全部内容xor.那么为什么我可以在weights内部使用但不将它打印到REPL?

正如你所看到的,我已经陷入doall各种各样的地方,比我认为我需要的更多.XOR是一个玩具例子,所以weightsxorset都非常小.我认为溢出不是因为执行xorb-xor,而是在REPL尝试打印时weights,出于以下两个原因:

(1)这个循环可以达到1500而不会溢出堆栈.

(2)循环运行的时间与循环的长度一致; 也就是说,如果我循环到5000,它运行半秒然后打印堆栈溢出; 如果我循环到1000000,它运行十秒然后打印堆栈溢出 - 再次,只有我打印weights而不是res在结尾.

(3)编辑:另外,如果我只是将循环包装进去(def w ... ),那么就没有堆栈溢出.但是,试图查看结果变量的确如此.

user=> (clojure.stacktrace/e)
java.lang.StackOverflowError: null
 at clojure.core$seq.invoke (core.clj:133)
    clojure.core$map$fn__4211.invoke (core.clj:2490)
    clojure.lang.LazySeq.sval (LazySeq.java:42)
    clojure.lang.LazySeq.seq (LazySeq.java:60)
    clojure.lang.RT.seq (RT.java:484)
    clojure.core$seq.invoke (core.clj:133)
    clojure.core$map$fn__4211.invoke (core.clj:2490)
    clojure.lang.LazySeq.sval (LazySeq.java:42)
nil
Run Code Online (Sandbox Code Playgroud)

懒惰序列在哪里?

如果你有更好的方法来做这个建议(这只是我的动态REPL代码),那就太棒了,但是我真的在寻找关于这种情况下发生了什么的解释.


编辑2:肯定(?)REPL的问题.

这很奇怪.weights是一个包含六个列表的列表,其中四个是空的.到现在为止还挺好.但是尝试将其中一个空列表打印到屏幕会导致堆栈溢出,但这只是第一次.第二次打印而不会丢失任何错误.打印非空列表不会产生堆栈溢出.现在我可以继续我的项目了,但是......到底发生了什么?有任何想法吗?(请原谅以下丑陋,但我认为这可能有帮助)

user=> (def ww (loop etc. etc. ))
#'user/ww
user=> (def x (first ww))
#'user/x
user=> x
StackOverflowError   clojure.lang.RT.seq (RT.java:484)
user=> x
()
user=> (def x (nth ww 3))
#'user/x
user=> x
(8.47089879874061 -8.742792338501289 -4.661609290853221)
user=> (def ww (loop etc. etc. ))
#'user/ww
user=> ww
StackOverflowError   clojure.core/seq (core.clj:133)
user=> ww
StackOverflowError   clojure.core/seq (core.clj:133)
user=> ww
StackOverflowError   clojure.core/seq (core.clj:133)
user=> ww
StackOverflowError   clojure.core/seq (core.clj:133)
user=> ww
(() () () (8.471553034351501 -8.741870954507117 -4.661171802683782) () (-8.861958958234174 8.828933147027938 18.43649480263751 -4.532462509591159))
Run Code Online (Sandbox Code Playgroud)

gal*_*dre 9

如果调用doall包含更多延迟序列的序列,doall则不会递归迭代子序列.在这种特殊情况下,b-xor包含的空列表的返回值是从以前的空列表中懒惰地定义的,这些空列表是从之前的空列表中延迟定义的,依此类推.我所要做的就是doallmap生成空列表(in b-xor)的位置添加一个,然后问题就消失了.这个循环(删除了所有doall)从不溢出:

(loop [res 1
       weights xorw
       k 0]
  (if (= k 1000000)
      weights 
      (let [n (rand-int 4)
            r (xor weights (first (nth xorset n)))]
        (recur r
               (b-xor weights r (second (nth xorset n)))
               (inc k)))))
Run Code Online (Sandbox Code Playgroud)

好的.所以我有一个答案.我希望这对其他一些可怜的灵魂有所帮助,他们认为他已经解决了他的懒惰排序问题doall.

这仍然让我有一个关于REPL的问题,但它可能会在一个不同的问题下进行,所以它不会有这个问题的所有包袱.您可以在上面的问题中看到空列表正确评估.为什么第一次打印它们会抛出异常?我将尝试一下这个,如果我无法弄明白......新问题!