(另一个)Clojure中循环重现的堆栈溢出 [英] (Another) Stack overflow on loop-recur in Clojure

查看:58
本文介绍了(另一个)Clojure中循环重现的堆栈溢出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

类似的问题:一个两个三个



I被彻底弄糊涂了。我正在使用循环重复表单,我正在使用 doall ,但对于大型循环,我仍然会出现堆栈溢出。我的Clojure版本是1.5.1。



上下文:我正在训练一个神经网络来模拟XOR。函数 xor 是前馈函数,负责权重和输入并返回结果;函数 b-xor 是向后传播函数,根据上次调用 xor 的结果,该函数将返回更新的权重。



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

 (loop [res 1;<-初始值无所谓
权重xorw ;<-初始伪随机权重
k 0];<-计数
(如果(= k 1000000)
res
(let [n(rand-int 4)
r(doall(xor权重(first(nth xorset n))))]
(递归(doall r)
(doall(b-xor权重r(second(nth xorset n))) ))
(inc k)))))

但是,当然,我最后一次运行的结果。显然,我想知道已经训练了哪些重量来获得该结果!以下循环,除了返回值更改外,什么都没有发生:

 (循环[res 1 
权重xorw
k 0]
(如果(= k 1000000)
权重;<-新的返回值
(let [n(rand-int 4)
r(doall(xor权重(第一个(nth xorset n))))]
(递归(doall r)
(doall(b-xor权重r(second(nth xorset n)))) )
(inc k)))))

对我来说这没有意义。每次调用 xor 都会使用全部 weights 。那么,为什么我可以在内部使用 weights 而不将其打印到REPL?



如您所见,我在各种地方都卡住了 doall ,这比我想的要多。 XOR是一个玩具示例,因此权重 xorset 都很小。我认为溢出不是由于执行 xor b-xor 而发生的,而是在REPL尝试打印 weights ,有两个原因:



(1)此循环最多可以达到1500,而不会导致堆栈溢出。 / p>

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



(3)编辑:而且,如果我将循环包装在(def w ... ),则没有堆栈溢出。

  user =>尝试偷看结果变量确实可以。 (clojure.stacktrace / e)
java.lang.StackOverflowError:null
在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)

懒惰序列在哪里?



如果您有更好的建议(这只是我的实时REPL代码),很好,但是我真的在寻找这种情况下发生情况的解释。






编辑2:肯定(?)REPL有问题。



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

  user => (def ww(循环等)。)
#’user / ww
user => (def x(第一个ww))
#’用户/ 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(循环等)。)
#’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))


解决方案

如果在包含更多惰性序列的序列上调用 doall ,则 doall 不会递归地遍历子序列。在这种特殊情况下,返回值 b-xor 包含根据从先前的空列表懒惰定义的先前的空列表懒惰地定义的空列表,依此类推。我要做的就是在 map 中添加一个 doall 来生成空列表(在 b-xor ),问题消失了。此循环(删除了所有doall)永远不会溢出:

 (循环[res 1 
weights xorw
k 0]
(如果(= k 1000000)
权重
(让[n(rand-int 4)
r(xor权重(first(nth xorset n)))) ]
(递归r
(b-xor权重r(第二(nth xorset n)))
(inc k)))))

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



这仍然使我对REPL存有疑问,但它可能应该在另一个问题下,这样它就不会包含这个问题的全部内容。您可以在上面的问题中看到,正确评估了空列表。为什么第一次打印它们会引发异常?我将对此做些尝试,如果我无法解决……新问题!


Similar questions: One, Two, Three.

I am thoroughly flummoxed here. I'm using the loop-recur form, I'm using doall, and still I get a stack overflow for large loops. My Clojure version is 1.5.1.

Context: I'm training a neural net to mimic XOR. The function xor is the feed-forward function, taking weights and input and returning the result; the function b-xor is the back-propagation function that returns updated weights given the results of the last call to xor.

The following loop runs just fine, runs very fast, and returns a result, and based off of the results it returns, it is training the weights perfectly:

(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)))))

But of course, that only gives me the result of the very last run. Obviously I want to know what weights have been trained to get that result! The following loop, with nothing but the return value changed, overflows:

(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)))))

This doesn't make sense to me. The entirety of weights gets used in each call to xor. So why could I use weights internally but not print it to the REPL?

And as you can see, I've stuck doall in all manner of places, more than I think I should need. XOR is a toy example, so weights and xorset are both very small. I believe the overflow occurs not from the execution of xor and b-xor, but when the REPL tries to print weights, for these two reasons:

(1) this loop can go up to 1500 without overflowing the stack.

(2) the time the loop runs is consistent with the length of the loop; that is, if I loop to 5000, it runs for half a second and then prints a stack overflow; if I loop to 1000000, it runs for ten seconds and then prints a stack overflow -- again, only if I print weights and not res at the end.

(3) EDIT: Also, if I just wrap the loop in (def w ... ), then there is no stack overflow. Attempting to peek at the resulting variable does, though.

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

Where is the lazy sequence?

If you have suggestions for better ways to do this (this is just my on-the-fly REPL code), that'd be great, but I'm really looking for an explanation as to what is happening in this case.


EDIT 2: Definitely (?) a problem with the REPL.

This is bizarre. weights is a list containing six lists, four of which are empty. So far, so good. But trying to print one of these empty lists to the screen results in a stack overflow, but only the first time. The second time it prints without throwing any errors. Printing the non-empty lists produces no stack overflow. Now I can move on with my project, but...what on earth is going on here? Any ideas? (Please pardon the following ugliness, but I thought it might be helpful)

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))

解决方案

If you call doall on a sequence that contains more lazy sequences, doall does not recursively iterate through the subsequences. In this particular case, the return value of b-xor contained empty lists that were defined lazily from previous empty lists defined lazily from previous empty lists, and so on. All I had to do was add a single doall to the map that produced the empty lists (in b-xor), and the problem disappeared. This loop (with all of the doall's removed) never overflows:

(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)))))

Okay. So I have an answer. I hope this is helpful to some other poor soul who thought he'd solved his lazy sequencing issues with a badly-placed doall.

This still leaves me with a question about the REPL, but it should probably go under a different question so it won't have all of the baggage of this problem with it. You can see in my question above that the empty lists were evaluated correctly. Why did printing them the first time throw an exception? I'm going to experiment a bit with this, and if I can't figure it out...new question!

这篇关于(另一个)Clojure中循环重现的堆栈溢出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆