2010-11-22 15 views
5

Das folgende Stück Clojure-Code führt zu java.lang.StackOverflowError, wenn ich es mit (avg-Gebote 4000 10 5) aufrufen. Ich versuche herauszufinden, warum Sum-Gebote als eine tail-rekursive Funktion geschrieben werden, also sollte das funktionieren. Verwenden von Clojure 1.2.StackOverflowError auf Tail-rekursive Funktion

Weiß jemand, warum das passiert?

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

Eine Tail-rekursive Funktion nennt sich selbst als das letzte, was sie tut. So etwas sehe ich in deinem Code nicht. – Gabe

+0

@Gabe: Schleife-Reverse verursacht ein Tail-Rekursion-ähnliches Verhalten. Siehe http://clojure.org/special_forms. – Ralph

+0

Ralph: 'loop-recur' ist ein' for' Loop-Pattern. Wenn Sie "recur" als letztes in Ihrer Funktion aufrufen, wäre das eine Tail-Rekursion, was er nicht tut. – Gabe

Antwort

8

map ist faul, und Sie bauen eine sehr tief verschachtelte Abbildung von Mappings über recur. Der Backtrace ist ein wenig kryptisch, aber genau hinsehen, und Sie können Karte, Karte, Karte ...

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) 

Eine Möglichkeit, es sehen zu beheben ist doall um es zu setzen Faulheit zu besiegen.

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

Danke Brian, das hat den Trick gemacht. Ich denke, ich muss etwas vorsichtiger sein, um mich der Folgen von faulen Sequenzen bewusst zu sein. –

Verwandte Themen