【问题标题】:StackOverflowError on tail-recursive function尾递归函数上的 StackOverflowError
【发布时间】:2010-11-22 20:28:00
【问题描述】:

当我使用 (avg-bids 4000 10 5) 调用以下 Clojure 代码时,它会导致 java.lang.StackOverflowError。我试图弄清楚为什么,因为 sum-bids 是作为尾递归函数编写的,所以应该可以。使用 Clojure 1.2。

有人知道为什么会这样吗?

(ns fixedprice.core
  (:use (incanter core stats charts)))

(def *bid-mean* 100)

(defn bid [x std-dev]
  (sample-normal x :mean *bid-mean* :sd std-dev))

(defn sum-bids [n offers std-dev]
  (loop [n n sum (repeat offers 0)]
    (if (zero? n)
      sum
      (recur (dec n) (map + sum (reductions min (bid offers std-dev)))))))

(defn avg-bids [n offers std-dev]
  (map #(/ % n) (sum-bids n offers std-dev))) 

【问题讨论】:

  • 尾递归函数调用自己作为它做的最后一件事。我在您的代码中没有看到类似的内容。
  • @Gabe: loop-recur 导致类似尾递归的行为。见clojure.org/special_forms
  • Ralph: loop-recur 是一个 for 循环模式。调用recur 作为函数中的最后一件事将是尾递归,这不是他所做的。
  • recur 抱怨它不在尾部位置。

标签: clojure


【解决方案1】:

map 是懒惰的,您正在通过recur 构建一个非常嵌套的映射映射。回溯有点神秘,但仔细看,可以看到地图、地图、地图……

Caused by: java.lang.StackOverflowError
        at clojure.lang.LazySeq.seq(LazySeq.java:56)
        at clojure.lang.RT.seq(RT.java:450)
        at clojure.core$seq.invoke(core.clj:122)
        at clojure.core$map$fn__3699.invoke(core.clj:2099)
        at clojure.lang.LazySeq.sval(LazySeq.java:42)
        at clojure.lang.LazySeq.seq(LazySeq.java:56)
        at clojure.lang.RT.seq(RT.java:450)
        at clojure.core$seq.invoke(core.clj:122)
        at clojure.core$map$fn__3699.invoke(core.clj:2099)
        at clojure.lang.LazySeq.sval(LazySeq.java:42)
        at clojure.lang.LazySeq.seq(LazySeq.java:56)
        at clojure.lang.RT.seq(RT.java:450)
        at clojure.core$seq.invoke(core.clj:122)
        at clojure.core$map$fn__3699.invoke(core.clj:2099)
        at clojure.lang.LazySeq.sval(LazySeq.java:42)
        at clojure.lang.LazySeq.seq(LazySeq.java:56)
        at clojure.lang.RT.seq(RT.java:450)
        at clojure.core$seq.invoke(core.clj:122)
        at clojure.core$map$fn__3699.invoke(core.clj:2099)

解决它的一种方法是在它周围加上doall 以消除懒惰。

(defn sum-bids [n offers std-dev]
  (loop [n n sum (repeat offers 0)]
    (if (zero? n)
      sum
      (recur (dec n) (doall (map + sum (reductions min (bid offers std-dev))))))))

user> (avg-bids 4000 10 5)
(100.07129114746716 97.15856005697917 95.81372899072466 94.89235550905231 94.22478826109985 93.72441188690516 93.32420819224373 92.97449591314158 92.67155818823753 92.37275046342015)

【讨论】:

  • 谢谢布赖恩,成功了。我想我必须要小心一点,更加了解惰性序列的后果。
猜你喜欢
  • 2011-02-18
  • 2019-09-14
  • 2014-02-03
  • 1970-01-01
  • 2012-12-28
  • 2016-12-20
  • 1970-01-01
  • 2016-06-13
  • 1970-01-01
相关资源
最近更新 更多