diff --git a/pixie/stdlib.pxi b/pixie/stdlib.pxi index 77b3cede..23a9f7e4 100644 --- a/pixie/stdlib.pxi +++ b/pixie/stdlib.pxi @@ -903,7 +903,7 @@ If further arguments are passed, invokes the method named by symbol, passing the (if (next coll) (recur (next coll)) (first coll)) - + (satisfies? ISeqable coll) (recur (seq coll)))) @@ -1907,7 +1907,7 @@ not enough elements were present." ISeqable (-seq [self] - (reduce conj self))) + (sequence xform coll))) (defn eduction "Returns a reducible/iterable application of the transducers @@ -2106,7 +2106,7 @@ For more information, see http://clojure.org/special_forms#binding-forms"} val not-found))) ISeq - (-first [this] + (-first [this] (when (not= start stop) start)) (-next [this] @@ -2188,6 +2188,20 @@ For more information, see http://clojure.org/special_forms#binding-forms"} ([pred coll] (filter (complement pred) coll))) +(defn sequence + "Returns a lazy sequence of `data`, optionally transforming it using `xform`" + ([coll] + (if (seq? coll) coll + (or (seq coll) ()))) + ([xform coll] + (let [step (defn step [xform acc xs] + (if-let [s (seq xs)] + (let [next-acc ((xform conj) acc (first s))] + (if (= acc next-acc) (step xform next-acc (next s)) + (concat (drop (count acc) next-acc) (step xform next-acc (next s))))) + nil))] + (lazy-seq (step xform [] coll))))) + (defn distinct {:doc "Returns the distinct elements in the collection." :signatures [[] [coll]] @@ -3080,7 +3094,7 @@ ex: (vary-meta x assoc :foo 42)" (deftype Iterate [f x] IReduce (-reduce [self rf init] - (loop [next (f x) + (loop [next (f x) acc (rf init x)] (if (reduced? acc) @acc diff --git a/tests/pixie/tests/test-stdlib.pxi b/tests/pixie/tests/test-stdlib.pxi index 12ad9912..8805cd14 100644 --- a/tests/pixie/tests/test-stdlib.pxi +++ b/tests/pixie/tests/test-stdlib.pxi @@ -595,7 +595,7 @@ (seq))) ;; expanding transducer with nils (t/assert= '(1 2 3 nil 4 5 6 nil) - (eduction cat [[1 2 3 nil] [4 5 6 nil]]))) + (seq (eduction cat [[1 2 3 nil] [4 5 6 nil]])))) (t/deftest test-trace (try @@ -767,3 +767,23 @@ (t/assert= (reduce (fn [a v] (reduced "foo")) 0 (iterate inc 1)) "foo") (t/assert= (reduce (fn [a v] (if (< a 10) (+ a v) (reduced a))) 0 (iterate (partial + 2) 1)) 16)) +(t/deftest test-sequence-empty-sequences + (t/assert= '() (take 1 (sequence (map inc) '()))) + (t/assert= '() (take 1 (sequence (map inc) []))) + (t/assert= '() (take 1 (sequence (map inc) #{}))) + (t/assert= '() (take 1 (sequence (map inc) {})))) + +(t/deftest test-sequence-non-empty-sequences + (t/assert= '(1 3) (take 2 (sequence (comp + (filter even?) + (map inc)) (range 3)))) + (t/assert= '(1) (take 1 (sequence (distinct) (repeat 4 1))))) + +(t/deftest test-sequence-early-terminating-sequences + (t/assert= '() (take 5 (sequence (filter (fn [x] false)) (repeat 8 8)))) + (t/assert= '(1 2) (take 3 (sequence (map identity) [1 2]))) + (t/assert= #{[:a 1] [:b 2]} (into #{} (take 3 (sequence (filter (fn [[k v]] + (keyword? k)) {:a 1 + :b 2 + "c" 3 + "d" 4}))))))