|
31 | 31 | (sequence (map *) (range 5) (range 5) (range 5)) |
32 | 32 | (sequence (map vector) (range 5) (range 5) (range 5)) |
33 | 33 |
|
34 | | -;; How about chaining several transformations together? We can use `eduction`: |
| 34 | +;; How about chaining several transformations together? We can use `eduction` |
| 35 | +;; (but see the caveats below about `eduction`): |
35 | 36 |
|
36 | 37 | (eduction (filter even?) (map inc) (range 10)) |
37 | 38 |
|
|
84 | 85 | (transduce (map inc) + 0 (range 5)) |
85 | 86 | (transduce (map inc) * 1 (range 5)) |
86 | 87 |
|
| 88 | +;; The way `(transduce xf f init coll)` works is pretty much like this: |
| 89 | + |
| 90 | +;; ``` clojure |
| 91 | +;; (let [rf (xf f)] |
| 92 | +;; (rf (reduce rf init coll))) |
| 93 | +;; ``` |
| 94 | + |
| 95 | +;; Note how `xf` and `f` are combined to create a single "reducing function" |
| 96 | +;; (which is a 2-arity function that takes an accumulator and a value), for |
| 97 | +;; the `reduce` and then also applied to the result as a 1-arity function, |
| 98 | +;; which is what `completing` does for us in the above example. |
| 99 | + |
87 | 100 | ;; Now let's circle back to chaining transformations, while also controlling |
88 | 101 | ;; the output type. We can use `comp` for this. As a recap, here's our |
89 | 102 | ;; `eduction` from earlier: |
|
112 | 125 | ;; Because it is a "reducible", it only does work when it is consumed, so it |
113 | 126 | ;; is "lazy" in that sense, but it is not a lazy sequence. We can get a lazy |
114 | 127 | ;; sequence from a transducer using `sequence`, if we want, or we can rely |
115 | | -;; on `into` and `transduce` etc being eager. |
| 128 | +;; on `into` and `transduce` etc being eager. In addition, `eduction` performs |
| 129 | +;; the transformations each time it is consumed: |
| 130 | + |
| 131 | +(let [s (eduction (map #(inc (doto % println))) (range 5))] |
| 132 | + [(into [] s) |
| 133 | + (into [] s)]) |
| 134 | + |
| 135 | +;; That will print 0 1 2 3 4 twice, because the `eduction` is consumed twice. |
| 136 | +;; Compare that behavior to `sequence`, which produces a lazy sequence and |
| 137 | +;; caches its results: |
| 138 | + |
| 139 | +(let [s (sequence (map #(inc (doto % println))) (range 5))] |
| 140 | + [(into [] s) |
| 141 | + (into [] s)]) |
| 142 | + |
| 143 | +;; This will only print 0 1 2 3 4 once. |
116 | 144 |
|
117 | 145 | ;; In conclusion, |
118 | 146 | ;; by separating the transformation from the input and the output, we gain |
|
0 commit comments