【发布时间】:2014-12-11 08:45:12
【问题描述】:
这是一个给我StackOverflowError的sn-p代码(从我的代码库中的一个实际示例中总结出来):
( ->> (range 3000)
(mapcat #(concat [0] (take 100 (repeat %))))
(reduce (constantly nil))
(count))
(注意:此代码的设计目的是做除了演示问题或返回零以外的任何事情。)
我可以通过以下任何步骤“拯救”它:
- 删除
reduce行 - 将
[0]更改为'(0) - 在
mapcat和count之间的任意点添加(take 100000000)(或任何整数)。
我基本上对这种行为感到困惑(尤其是#2)。我会很感激任何意见。
(我感觉这可能与 Why does reduce give a StackOverflowError in Clojure? 有关,但我不能完全确定如何 - 所以如果它是相关的,我会很感激解释原因。)
【问题讨论】:
-
假设返回 0 吗?有趣的是,如果我将其更改为这样,我不会得到堆栈溢出: ( ->> (range) (mapcat (fn [p] (concat [0] (take 100 (repeat nil))))) (take 3000) (减少(始终为零))(计数))
-
是的,它“应该”返回 0。(我的意思是,除了以我能找到的最短方式演示此行为之外,代码实际上不应该做任何事情。)
count只是为了证明使用序列本身不会导致错误。在您的示例中,@firthh,如果您愿意,您可以将 (take 3000) 更改为 (take 3000000),它仍然有效。您也可以在reduce步骤之后放置(取3000)... -
reduce 是严格的,并且使用恒定的堆栈空间 - concat / mapcat 调用更有可能在被强制之前堆叠未评估导致问题
-
@noisesmith 不过,这并不是真正的解释。为什么删除
reduce后count会成功?为什么'(0)的行为不同?为什么不包装 concat(或 mapcat)导致(doall)帮助?
标签: clojure stack-overflow lazy-evaluation thunk