summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChouser <chouser@n01se.net>2009-08-23 21:36:41 -0400
committerChouser <chouser@n01se.net>2009-08-24 16:17:40 -0400
commit14316ae2110a779ffc8ac9c3da3f1c41852c4289 (patch)
tree11d06ae269bf989cdbe08100a142199c58745223
parent4af9fcf218c04db9b38fb4ce35591f1ff17a4f15 (diff)
Add support for chunked seqs to 'for'. Refs #1
-rw-r--r--src/clj/clojure/core.clj49
-rw-r--r--test/clojure/test_clojure/for.clj5
2 files changed, 48 insertions, 6 deletions
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index 2a17e788..992989e5 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -2858,7 +2858,7 @@
binding-forms. Supported modifiers are: :let [binding-form expr ...],
:while test, :when test.
- (take 100 (for [x (range 100000000) y (range 1000000) :while (< y x)] [x y]))"
+ (take 100 (for [x (range 100000000) y (range 1000000) :while (< y x)] [x y]))"
[seq-exprs body-expr]
(assert-args for
(vector? seq-exprs) "a vector for its binding"
@@ -2890,11 +2890,48 @@
(recur (rest ~gxs))))
:else `(cons ~body-expr
(~giter (rest ~gxs)))))]
- `(fn ~giter [~gxs]
- (lazy-seq
- (loop [~gxs ~gxs]
- (when-first [~bind ~gxs]
- ~(do-mod mod-pairs)))))))]
+ (if next-groups
+ #_"not the inner-most loop"
+ `(fn ~giter [~gxs]
+ (lazy-seq
+ (loop [~gxs ~gxs]
+ (when-first [~bind ~gxs]
+ ~(do-mod mod-pairs)))))
+ #_"inner-most loop"
+ (let [gi (gensym "i__")
+ gb (gensym "b__")
+ do-cmod (fn do-cmod [[[k v :as pair] & etc]]
+ (cond
+ (= k :let) `(let ~v ~(do-cmod etc))
+ (= k :while) `(when ~v ~(do-cmod etc))
+ (= k :when) `(if ~v
+ ~(do-cmod etc)
+ (recur
+ (unchecked-inc ~gi)))
+ (keyword? k)
+ (err "Invalid 'for' keyword " k)
+ :else
+ `(do (chunk-append ~gb ~body-expr)
+ (recur (unchecked-inc ~gi)))))]
+ `(fn ~giter [~gxs]
+ (lazy-seq
+ (loop [~gxs ~gxs]
+ (when-let [~gxs (seq ~gxs)]
+ (if (chunked-seq? ~gxs)
+ (let [c# (chunk-first ~gxs)
+ size# (int (count c#))
+ ~gb (chunk-buffer size#)]
+ (if (loop [~gi (int 0)]
+ (if (< ~gi size#)
+ (let [~bind (.nth c# ~gi)]
+ ~(do-cmod mod-pairs))
+ true))
+ (chunk-cons
+ (chunk ~gb)
+ (~giter (chunk-rest ~gxs)))
+ (chunk-cons (chunk ~gb) nil)))
+ (let [~bind (first ~gxs)]
+ ~(do-mod mod-pairs)))))))))))]
`(let [iter# ~(emit-bind (to-groups seq-exprs))]
(iter# ~(second seq-exprs)))))
diff --git a/test/clojure/test_clojure/for.clj b/test/clojure/test_clojure/for.clj
index d8ebed26..6d9dc59e 100644
--- a/test/clojure/test_clojure/for.clj
+++ b/test/clojure/test_clojure/for.clj
@@ -121,3 +121,8 @@
'([0 1 1] [1 0 1] [1 2 3] [2 1 3])))
(is (= (for [x (range 6) :let [y (rem x 2)] :when (even? y) z [8 9]] [x z])
'([0 8] [0 9] [2 8] [2 9] [4 8] [4 9]))))
+
+; :while must skip all subsequent chunks as well as the remainder of
+; the current chunk:
+(deftest-both Chunked-While
+ (is (= (for [x (range 100) :while (even? x)] x) '(0))))