Hi all, I'm toying around with the lazy, tail-recursive quick-sort implementation Michael Fogus and Chris Houser present in their book The Joy of Clojure:
--8<---------------cut here---------------start------------->8--- (in-ns 'user) (defn sort-parts "Lazy, tail-recursive, incremental quicksort. Works against and creates partitions based on the pivot, defined as 'work'." [work] (lazy-seq (loop [[part & parts] work] (if-let [[pivot & xs] (seq part)] (let [smaller? #(< % pivot)] (recur (list* (filter smaller? xs) pivot (remove smaller? xs) parts))) (when-let [[x & parts] parts] (cons x (sort-parts parts))))))) (defn qsort [xs] (sort-parts (list xs))) --8<---------------cut here---------------end--------------->8--- It works fine for smaller seqs: --8<---------------cut here---------------start------------->8--- user> (time (first (qsort (take 1000 (iterate dec 500))))) "Elapsed time: 142.61696 msecs" -499 --8<---------------cut here---------------end--------------->8--- Well, except that being lazy is about 20 times faster than the standard sort, although taking only the first (smallest) element of a possibly large collection is the exact use-case for the lazy-sort, isn't it? --8<---------------cut here---------------start------------->8--- user> (time (first (sort (take 1000 (iterate dec 500))))) "Elapsed time: 7.298069 msecs" -499 --8<---------------cut here---------------end--------------->8--- But that's only the minor issue. The major issue is that I get a StackOverflowError when the seq passed to qsort becomes too large. And that error is not in the sorting code, but in clojure itself! --8<---------------cut here---------------start------------->8--- user> (first (qsort (take 100000 (iterate dec 50000)))) ; Evaluation aborted. No message. [Thrown class java.lang.StackOverflowError] Restarts: 0: [QUIT] Quit to the SLIME top level Backtrace: 0: clojure.core$take$fn__3836.invoke(core.clj:2500) 1: clojure.lang.LazySeq.sval(LazySeq.java:42) 2: clojure.lang.LazySeq.seq(LazySeq.java:60) 3: clojure.lang.RT.seq(RT.java:466) 4: clojure.core$seq.invoke(core.clj:133) 5: clojure.core$filter$fn__3830.invoke(core.clj:2469) 6: clojure.lang.LazySeq.sval(LazySeq.java:42) 7: clojure.lang.LazySeq.seq(LazySeq.java:60) 8: clojure.lang.RT.seq(RT.java:466) 9: clojure.core$seq.invoke(core.clj:133) 10: clojure.core$filter$fn__3830.invoke(core.clj:2469) 11: clojure.lang.LazySeq.sval(LazySeq.java:42) 12: clojure.lang.LazySeq.seq(LazySeq.java:60) 13: clojure.lang.RT.seq(RT.java:466) 14: clojure.core$seq.invoke(core.clj:133) 15: clojure.core$filter$fn__3830.invoke(core.clj:2469) 16: clojure.lang.LazySeq.sval(LazySeq.java:42) 17: clojure.lang.LazySeq.seq(LazySeq.java:60) 18: clojure.lang.RT.seq(RT.java:466) 19: clojure.core$seq.invoke(core.clj:133) 20: clojure.core$filter$fn__3830.invoke(core.clj:2469) --more-- --8<---------------cut here---------------end--------------->8--- Is that a bug? It occurs in both clojure 1.2.1 and a 1.3 snapshot. The standard sort function works, though: --8<---------------cut here---------------start------------->8--- user> (first (sort (take 100000 (iterate dec 50000)))) -49999 --8<---------------cut here---------------end--------------->8--- Bye, Tassilo -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en