- Notifications
You must be signed in to change notification settings - Fork0
Learn how to write your own transducers. A complement from my blog post serie "Build Your Own Transducer and Impress Your Cat".
License
green-coder/transducer-exercises
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
Contains exercises on implementing custom transducers in Clojure.
It is recommended (but not required) to readthis serie of articles first.
The solutions are linked at the end of each section.
Implementing transducers is hard and you may feel confused.
That's normal and to be expected, it happens also to me when I read my own code. Just don't give up.
Implement a transducer that does nothing but passing the data from his input to his output. Functionally speaking, it is an identity transducer.
(into [] identity (range 3)); => [0 1 2]Use it as your transducer template for the following.
- Implement the
(debug in out)transducer that helps to debug.
; We use this function instead of `into` for debugging.; The reason is that this avoids using transient; structures which do not `print` nicely.(defn slow-into [to xf from] (transduce xf conj to from))(slow-into [] (debug "in" "out") (range 3));; Outputs:; in 0; out [0]; in 1; out [0 1]; in 2; out [0 1 2]- Add the
(debug),(debug indent)and(debug indent in out)variants for convenience. They are all calling the(debug in out)transducer under the hood.
(slow-into [] (comp (debug) (debug 2) (debug 4 ">" "<")) (debug " >" " <")) ; 6-spaces prefix (range 3));; Outputs:; > 0; > 0; > 0; > 0; < [0]; < [0]; < [0]; < [0]; > 1; > 1; > 1; > 1; < [0 1]; < [0 1]; < [0 1]; < [0 1]; > 2; > 2; > 2; > 2; < [0 1 2]; < [0 1 2]; < [0 1 2]; < [0 1 2]Strange patterns, it reminds me of ...
The PHP Hadouken !!!
We are now ready to face real transducer implementations and confront an army of problems.
You heard me well, I want you to implement the following transducer.
(def beg-data (list :may :i :beg :your :pardon :?))(into [] (beg 2) beg-data); => [:may :may :i :i :beg :beg :your :your :pardon :pardon :? :?]Make sure that you are handling the early termination as well.
(into [] (comp (take 3) (beg 2)) beg-data); => [:may :may :i :i :beg :beg]Test for both sides.
(into [] (comp (beg 2) (take 3)) beg-data); => [:may :may :i]Test with the debug transducer (expect problems and losing some hair).
(slow-into [] (comp (debug 0) (beg 2) (debug 2) (take 3) (debug 4)) beg-data); Output:; > :may; > :may; > :may; < [:may]; < [:may]; > :may; > :may; < [:may :may]; < [:may :may]; < [:may :may]; > :i; > :i; > :i; < [:may :may :i]; < #reduced[{:status :ready, :val [:may :may :i]} 0x8cdcdd1]; < #reduced[{:status :ready, :val [:may :may :i]} 0x8cdcdd1]; Result:; => [:may :may :i]Thebeg transducer should not continue sending data downstream after it receives a reduced result. Fix your implementation if needed.
Implement themy-cat transducer. For each step, you will need to adapt your implementation to the new requirements described by the test samples.
- Step 1, shapeless cat
It functions similarly toclojure.core/cat. Don't handle early termination at the moment.
(def cat-data [[1 2 :fish 3] [:heat 4] [5 :sleep 6] [7]])(into [] my-cat cat-data); => [1 2 :fish 3 :heat 4 5 :sleep 6 7]- Step 2, hungry cat
As you can see, the transducer is keeping for itself all the fishes and the heat.
(into [] my-cat cat-data); => [1 2 3 4 5 :sleep 6 7]- Step 3, sleepy cat
That version of the transducer falls asleep an the:sleep keyword and do not process any subsequent data.
(into [] my-cat cat-data); => [1 2 3 4 5]- Step 4, correct cat
At last, we want our transducer to respect the normal early termination (withreduced? tested on the downstream result) in a correct manner.
(into [] (comp (take 2) my-cat) cat-data); => [1 2 3 4](into [] (comp my-cat (take 2)) cat-data); => [1 2](slow-into [] (comp (debug 0) my-cat ; try replacing it with `cat` and compare (debug 2) (take 2) (debug 4)) cat-data);; Outputs:; > [1 2 :fish 3]; > 1; > 1; < [1]; < [1]; > 2; > 2; < [1 2]; < #reduced[{:status :ready, :val [1 2]} 0x517a7e8e]; < #reduced[{:status :ready, :val [1 2]} 0x517a7e8e]- Provide an idiomatic equivalent to
my-cat.
Link to the idiomatic solution
- Implement a transducer that daydream during a number of elements. While in the daydream state, it buffers its input. When it stops daydreaming, it processes all of its buffer as a batch, then daydreams again.
Thea-d-d transducer never loses data.
(into [] (a-d-d 3) (range 10)); => [0 1 2 3 4 5 6 7 8 9]; Try:(slow-into [] (comp (debug 0) (a-d-d 3) (debug 2)) (range 10))- Verify that it works well with early termination.
(slow-into [] (comp (debug 0) (a-d-d 3) (debug 2) (take 5)) (range 10)); => [0 1 2 3 4]- Change
a-d-dso that it works similarly toclojure.core/partition-all.
(into [] (a-d-d 3) (range 10)); => [[0 1 2] [3 4 5] [6 7 8] [9]]Link to a partition-all '-ish' solution
- Verify that it works well with early termination.
(slow-into [] (comp (debug 0) (a-d-d 3) (debug 2) (take 2)) (range 10)); => [[0 1 2] [3 4 5]]Implementserieduce which provides a transducer which reduces incoming elements and emits all intermediary elements.
(into [] (serieduce conj [1 2]) (range 3 6)); => [[1 2 3] [1 2 3 4] [1 2 3 4 5]](into [] (serieduce +) (range 5)); => [0 1 3 6 10]You are one of a few.
About
Learn how to write your own transducers. A complement from my blog post serie "Build Your Own Transducer and Impress Your Cat".
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.



