Skip to content

Commit 283e647

Browse files
author
notnew
committed
fix transducer composition problems, add tests
remove `preserving-reduced` from several transducers. This was modifying the reducing function passed to the transducer, causing reduced values to be wrapped in `reduced` twice, so the reducing process wasn't completely unwrapping reduced values when finishing. Reducing functions only need to be wrapped with `preserving-reduced` if they call `reduce` themselves. (Like `cat` and `pixie.io.common/stream-reducer`) Also fixes a problem with the `take` transducer consuming one more element than needed. Add tests for these problems.
1 parent 9c900fc commit 283e647

4 files changed

Lines changed: 110 additions & 47 deletions

File tree

pixie/io.pxi

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,13 @@
6666
(str string))))))
6767
IReduce
6868
(-reduce [this f init]
69-
(let [rrf (preserving-reduced f)]
70-
(loop [acc init]
71-
(if-let [line (-read-line this)]
72-
(let [result (rrf acc line)]
73-
(if (not (reduced? result))
74-
(recur result)
75-
@result))
76-
acc)))))
69+
(loop [acc init]
70+
(if-let [line (-read-line this)]
71+
(let [result (f acc line)]
72+
(if (reduced? result)
73+
@result
74+
(recur result)))
75+
acc))))
7776

7877
(defn line-reader
7978
[input-stream]
@@ -150,14 +149,13 @@
150149
(deftype BufferedInputStream [upstream idx buffer]
151150
IReduce
152151
(-reduce [this f init]
153-
(let [rrf (preserving-reduced f)]
154-
(loop [acc init]
155-
(if-let [next-byte (read-byte this)]
156-
(let [step (rrf acc next-byte)]
157-
(if (reduced? step)
158-
@step
159-
(recur step)))
160-
acc))))
152+
(loop [acc init]
153+
(if-let [next-byte (read-byte this)]
154+
(let [result (f acc next-byte)]
155+
(if (reduced? result)
156+
@result
157+
(recur result)))
158+
acc)))
161159
IByteInputStream
162160
(read-byte [this]
163161
(when (= idx (count buffer))

pixie/stdlib.pxi

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1640,21 +1640,25 @@ The new value is thus `(apply f current-value-of-atom args)`."
16401640
(let [idx (if (neg? i) (+ i (count coll)) i)]
16411641
(nth coll idx not-found)))))
16421642

1643+
(defn ensure-reduced [x]
1644+
(if (reduced? x)
1645+
x
1646+
(reduced x)))
1647+
16431648
(defn take
16441649
{:doc "Takes n elements from the collection, or fewer, if not enough."
16451650
:added "0.1"}
16461651
([n]
16471652
(fn [rf]
1648-
(let [rrf (preserving-reduced rf)
1649-
seen (atom 0)]
1653+
(let [seen (atom 0)]
16501654
(fn
16511655
([] (rf))
16521656
([result] (rf result))
16531657
([result input]
16541658
(let [s (swap! seen inc)]
1655-
(if (<= s n)
1656-
(rrf result input)
1657-
(reduced result))))))))
1659+
(cond (< s n) (rf result input)
1660+
(= s n) (ensure-reduced (rf result input))
1661+
:else (reduced result))))))))
16581662
([n coll]
16591663
(lazy-seq
16601664
(when (pos? n)
@@ -1666,16 +1670,15 @@ The new value is thus `(apply f current-value-of-atom args)`."
16661670
:added "0.1"}
16671671
([n]
16681672
(fn [rf]
1669-
(let [rrf (preserving-reduced rf)
1670-
seen (atom 0)]
1673+
(let [seen (atom 0)]
16711674
(fn
16721675
([] (rf))
16731676
([result]
16741677
(rf result))
16751678
([result input]
16761679
(let [s (swap! seen inc)]
16771680
(if (> s n)
1678-
(rrf result input)
1681+
(rf result input)
16791682
result)))))))
16801683
([n coll]
16811684
(let [s (seq coll)]
@@ -1707,14 +1710,13 @@ The new value is thus `(apply f current-value-of-atom args)`."
17071710
:added "0.1"}
17081711
([pred]
17091712
(fn [rf]
1710-
(let [rrf (preserving-reduced rf)]
1711-
(fn
1712-
([] (rf))
1713-
([result] (rf result))
1714-
([result input]
1713+
(fn
1714+
([] (rf))
1715+
([result] (rf result))
1716+
([result input]
17151717
(if (pred input)
1716-
(rrf result input)
1717-
(reduced result)))))))
1718+
(rf result input)
1719+
(reduced result))))))
17181720
([pred coll]
17191721
(lazy-seq
17201722
(when-let [s (seq coll)]
@@ -1825,13 +1827,12 @@ not enough elements were present."
18251827
:signatures [[f] [f coll]]}
18261828
([f]
18271829
(fn [rf]
1828-
(let [i (atom -1)
1829-
rrf (preserving-reduced rf)]
1830+
(let [i (atom -1)]
18301831
(fn
18311832
([] (rf))
18321833
([result] (rf result))
18331834
([result input]
1834-
(rrf result (f (swap! i inc) input)))))))
1835+
(rf result (f (swap! i inc) input)))))))
18351836
([f coll]
18361837
(let [mapi (fn mapi [i coll]
18371838
(lazy-seq
@@ -1849,8 +1850,7 @@ not enough elements were present."
18491850
:added "0.1"}
18501851
([f]
18511852
(fn [rf]
1852-
(let [iv (atom -1)
1853-
rrf (preserving-reduced rf)]
1853+
(let [iv (atom -1)]
18541854
(fn
18551855
([] (rf))
18561856
([result] (rf result))
@@ -1859,7 +1859,7 @@ not enough elements were present."
18591859
v (f i input)]
18601860
(if (nil? v)
18611861
result
1862-
(rrf result v))))))))
1862+
(rf result v))))))))
18631863
([f coll]
18641864
(let [keepi (fn keepi [i coll]
18651865
(lazy-seq

tests/pixie/tests/test-io.pxi

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,33 @@
33
(require pixie.streams :as st :refer :all)
44
(require pixie.streams.utf8 :as utf8 :refer :all)
55
(require pixie.io :as io)
6+
(require pixie.io-blocking :as blocking)
67
(require pixie.streams.zlib :as zlib))
78

89
(t/deftest test-file-reduction
910
(let [f (io/open-read "tests/pixie/tests/test-io.txt")]
1011
(t/assert= (transduce (map identity)
1112
count-rf
1213
f)
13-
91)))
14+
91))
15+
(let [f (io/open-read "tests/pixie/tests/test-io.txt")]
16+
(t/assert= (transduce (comp (map char) (take 4)) string-builder f)
17+
"This"))
18+
(let [f (blocking/open-read "tests/pixie/tests/test-io.txt")]
19+
(t/assert= (transduce (map identity)
20+
count-rf
21+
f)
22+
91))
23+
(let [f (blocking/open-read "tests/pixie/tests/test-io.txt")]
24+
(t/assert= (transduce (comp (map char) (take 4)) string-builder f)
25+
"This")))
1426

1527
(t/deftest test-process-reduction
1628
(let [f (io/run-command "ls tests/pixie/tests/test-io.txt")]
17-
(t/assert= f "tests/pixie/tests/test-io.txt\n")))
29+
(t/assert= f "tests/pixie/tests/test-io.txt\n"))
30+
(let [pipe (blocking/popen-read "ls tests/pixie/tests/test-io.txt")]
31+
(t/assert= (transduce (comp (map char) (take 6)) string-builder pipe)
32+
"tests/")))
1833

1934
(t/deftest test-read-into-buffer
2035
(let [f (io/open-read "tests/pixie/tests/test-io.txt")]
@@ -38,6 +53,14 @@
3853
(t/assert= (io/read-line f) "Second line.")
3954
(t/assert= (io/read-line f) nil)))
4055

56+
(t/deftest test-line-reader
57+
(let [lines (io/line-reader (io/open-read "tests/pixie/tests/test-io.txt"))]
58+
(t/assert= (transduce (map count) conj [] lines)
59+
[77 12]))
60+
(let [lines (io/line-reader (io/open-read "tests/pixie/tests/test-io.txt"))]
61+
(t/assert= (transduce (comp (map count) (take 1)) conj [] lines)
62+
[77])))
63+
4164
(t/deftest test-line-seq
4265
(let [f (io/buffered-input-stream (io/open-read "tests/pixie/tests/test-io.txt"))
4366
s (io/line-seq f)]
@@ -58,7 +81,11 @@
5881
(t/assert= (char (io/read-byte f)) \T)
5982
(t/assert= (char (io/read-byte f)) \h)
6083
(t/assert= (char (io/read-byte f)) \i)
61-
(t/assert= (char (io/read-byte f)) \s)))
84+
(t/assert= (char (io/read-byte f)) \s)
85+
(t/assert= (transduce (comp (map char) (take 5)) string-builder f)
86+
" is a")
87+
(t/assert= (transduce (comp (map char) (take 5)) string-builder f)
88+
" test")))
6289

6390
(t/deftest test-buffered-input-streams-throws-on-non-input-streams
6491
(let [f (io/buffered-input-stream (io/open-read "tests/pixie/tests/test-io.txt"))]

tests/pixie/tests/test-stdlib.pxi

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,28 @@
2828
(t/assert= (keep-indexed (constantly nil) []) [])
2929
(t/assert= (keep-indexed (fn [i x] [i x]) [:a :b]) [[0 :a] [1 :b]])
3030

31-
(t/assert= (transduce (keep-indexed (constantly true)) conj []) [])
32-
(t/assert= (transduce (keep-indexed (constantly nil)) conj []) [])
33-
(t/assert= (transduce (keep-indexed (fn [i x] [i x])) conj [:a :b]) [[0 :a] [1 :b]]))
31+
(t/assert= (transduce (keep-indexed (constantly true)) conj [:a :b]) [true true])
32+
(t/assert= (transduce (keep-indexed (constantly nil)) conj [:a :b]) [])
33+
(t/assert= (transduce (keep-indexed (fn [i x] [i x])) conj [:a :b]) [[0 :a] [1 :b]])
34+
35+
(let [even-index? (fn [i x]
36+
(when (even? i)
37+
x))]
38+
(t/assert= (transduce (keep-indexed even-index?) conj [:a :b :c :d])
39+
[:a :c])
40+
(t/assert= (transduce (map-indexed even-index?) conj [:a :b :c :d])
41+
[:a nil :c nil])
42+
43+
(t/assert= (transduce (comp (keep-indexed even-index?)
44+
(take 3))
45+
conj
46+
[:a :b :c :d :e :f])
47+
[:a :c :e])
48+
(t/assert= (transduce (comp (map-indexed even-index?)
49+
(take 3))
50+
conj
51+
[:a :b :c :d])
52+
[:a nil :c])))
3453

3554
(t/deftest test-reductions
3655
(t/assert= (reductions + nil)
@@ -542,7 +561,21 @@
542561
(t/assert= (transduce (take 0) conj [1 2 3 4]) [])
543562
(t/assert= (transduce (take 1) conj [1 2 3 4]) [1])
544563
(t/assert= (transduce (take 2) conj [1 2 3 4]) [1 2])
545-
(t/assert= (transduce (take 3) conj [1 2 3 4]) [1 2 3]))
564+
(t/assert= (transduce (take 3) conj [1 2 3 4]) [1 2 3])
565+
(t/assert= (transduce (take 10) conj [1 2 3 4]) [1 2 3 4])
566+
(t/assert= (transduce (comp (take 2) (take 1)) conj [1 2 3 4]) [1])
567+
(t/assert= (transduce (comp (take 1) (take 2)) conj [1 2 3 4]) [1])
568+
569+
(let [call-count (atom 0)
570+
inc-call-count! (fn [x]
571+
(swap! call-count inc)
572+
x)]
573+
(t/assert= (transduce (comp (map inc-call-count!) (take 2)) conj (range 10))
574+
[0 1])
575+
(t/assert= @call-count 2)
576+
(t/assert= (transduce (comp (take 2) (map inc-call-count!)) conj (range 10))
577+
[0 1])
578+
(t/assert= @call-count 4)))
546579

547580
(t/deftest test-drop
548581
(t/assert= (drop 0 [1 2 3 4]) [1 2 3 4])
@@ -552,21 +585,26 @@
552585
(t/assert= (transduce (drop 0) conj [1 2 3 4]) [1 2 3 4])
553586
(t/assert= (transduce (drop 1) conj [1 2 3 4]) [2 3 4])
554587
(t/assert= (transduce (drop 2) conj [1 2 3 4]) [3 4])
555-
(t/assert= (transduce (drop 3) conj [1 2 3 4]) [4]))
588+
(t/assert= (transduce (drop 3) conj [1 2 3 4]) [4])
589+
(t/assert= (transduce (drop 10) conj [1 2 3 4]) [])
590+
(t/assert= (transduce (comp (drop 1) (take 2)) conj [1 2 3 4]) [2 3])
591+
(t/assert= (transduce (comp (take 2) (drop 1)) conj [1 2 3 4]) [2]))
556592

557593
(t/deftest test-take-while
558594
(t/assert= (take-while pos? [1 2 3 -1]) [1 2 3])
559595
(t/assert= (take-while pos? [-1 2]) ())
560596
(t/assert= (transduce (take-while even?) conj [2 4 6 7 8]) [2 4 6])
561597
(t/assert= (transduce (take-while even?) conj [0 2] [1 4 6]) [0 2])
562-
(t/assert= (transduce (take-while even?) conj [1 3] [2 4 6 7 8]) [1 3 2 4 6]))
598+
(t/assert= (transduce (take-while even?) conj [1 3] [2 4 6 7 8]) [1 3 2 4 6])
599+
(t/assert= (transduce (comp (take-while even?) (take 2)) conj [1 3] [2 4 6 7 8]) [1 3 2 4]))
563600

564601
(t/deftest test-drop-while
565602
(t/assert= (drop-while pos? [1 2 3 -1]) [-1])
566603
(t/assert= (drop-while pos? [-1 2]) [-1 2])
567604
(t/assert= (transduce (drop-while even?) conj [2 4 6 7 8]) [7 8])
568605
(t/assert= (transduce (drop-while even?) conj [0 2] [1 4 6]) [0 2 1 4 6])
569-
(t/assert= (transduce (drop-while even?) conj [0 2] [2 4 6 7 8]) [0 2 7 8]))
606+
(t/assert= (transduce (drop-while even?) conj [0 2] [2 4 6 7 8]) [0 2 7 8])
607+
(t/assert= (transduce (comp (drop-while even?) (take 2)) conj [0 2] [2 4 6 7 8]) [0 2 7 8]))
570608

571609
(t/deftest test-cycle
572610
(t/assert= (cycle ()) ())

0 commit comments

Comments
 (0)