Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commita372b09

Browse files
author
Vincent Cantin
committed
The original blog post with images.
0 parents  commita372b09

File tree

5 files changed

+275
-0
lines changed

5 files changed

+275
-0
lines changed

‎README.md‎

Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
Contains exercises on implementing custom transducers in Clojure.
2+
3+
##Fair notice
4+
5+
Implementing transducers is hard and you may feel confused.
6+
7+
![Cry Kid Meme](img/meme kid cry.gif)
8+
9+
That's normal and to be expected, it happens also to me when I read my own code. Just don't give up.
10+
11+
##No Operation transducer
12+
13+
Implement a transducer that does nothing but passing the data from his input to his output. Functionally speaking, it is an identity transducer.
14+
15+
```clojure
16+
(into [] nop (range3))
17+
; => [0 1 2]
18+
```
19+
20+
Use it as your transducer template for the following.
21+
22+
##Prepare for battle
23+
24+
* Implement the`(debug in out)` transducer that helps to debug.
25+
26+
```clojure
27+
; We use this function instead of `into` for debugging.
28+
; The reason is that this avoids using transient
29+
; structures which do not `print` nicely.
30+
(defnslow-into [to xf from]
31+
(transduce xf conj to from))
32+
33+
(slow-into [] (debug"in""out") (range3))
34+
;; Outputs:
35+
; in 0
36+
; out [0]
37+
; in 1
38+
; out [0 1]
39+
; in 2
40+
; out [0 1 2]
41+
```
42+
43+
* 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.
44+
45+
```clojure
46+
(slow-into []
47+
(comp (debug)
48+
(debug2)
49+
(debug4">""<"))
50+
(debug" >"" <")); 6-spaces prefix
51+
(range3))
52+
;; Outputs:
53+
; > 0
54+
; > 0
55+
; > 0
56+
; > 0
57+
; < [0]
58+
; < [0]
59+
; < [0]
60+
; < [0]
61+
; > 1
62+
; > 1
63+
; > 1
64+
; > 1
65+
; < [0 1]
66+
; < [0 1]
67+
; < [0 1]
68+
; < [0 1]
69+
; > 2
70+
; > 2
71+
; > 2
72+
; > 2
73+
; < [0 1 2]
74+
; < [0 1 2]
75+
; < [0 1 2]
76+
; < [0 1 2]
77+
```
78+
79+
Strange patterns, it reminds me of ...
80+
81+
**The PHP Hadouken !!!**
82+
83+
![PHP Hadouken](php hadouken.jpg)
84+
85+
We are now ready to face real transducer implementations and confront an army of problems.
86+
87+
##May I beg your pardon?
88+
89+
You heard me well, I want you to implement the following transducer.
90+
91+
```clojure
92+
(defbeg-data (list:may:i:beg:your:pardon:?))
93+
94+
(into [] (beg2) beg-data)
95+
; => [:may :may :i :i :beg :beg :your :your :pardon :pardon :? :?]
96+
```
97+
98+
Make sure that you are handling the early termination as well.
99+
100+
```clojure
101+
(into []
102+
(comp (take3)
103+
(beg2))
104+
beg-data)
105+
; => [:may :may :i :i :beg :beg]
106+
```
107+
108+
Test for both sides.
109+
110+
```clojure
111+
(into []
112+
(comp (beg2)
113+
(take3))
114+
beg-data)
115+
; => [:may :may :i]
116+
```
117+
118+
Test with the debug transducer (expect problems and losing some hair).
119+
120+
```clojure
121+
(slow-into []
122+
(comp (debug0)
123+
(beg2)
124+
(debug2)
125+
(take3)
126+
(debug4))
127+
beg-data)
128+
; Output:
129+
; > :may
130+
; > :may
131+
; > :may
132+
; < [:may]
133+
; < [:may]
134+
; > :may
135+
; > :may
136+
; < [:may :may]
137+
; < [:may :may]
138+
; < [:may :may]
139+
; > :i
140+
; > :i
141+
; > :i
142+
; < [:may :may :i]
143+
; < #reduced[{:status :ready, :val [:may :may :i]} 0x8cdcdd1]
144+
; < #reduced[{:status :ready, :val [:may :may :i]} 0x8cdcdd1]
145+
146+
; Result:
147+
; => [:may :may :i]
148+
```
149+
150+
The`beg` transducer should not continue sending data downstream after it receives a reduced result. Fix your implementation if needed.
151+
152+
##All your data are belong to me
153+
154+
![Catducer](img/coco transducer.jpg)
155+
156+
Implement the`my-cat` transducer. For each step, you will need to adapt your implementation to the new requirements described by the test samples.
157+
158+
* Step 1, shapeless cat
159+
160+
It functions similarly to`clojure.core/cat`. Don't handle early termination at the moment.
161+
162+
```clojure
163+
(defcat-data [[12:fish3] [:heat4] [5:sleep6] [7]])
164+
165+
(into [] my-cat cat-data)
166+
; => [1 2 :fish 3 :heat 4 5 :sleep 6 7]
167+
```
168+
169+
* Step 2, hungry cat
170+
171+
As you can see, the transducer is keeping for itself all the fishes and the heat.
172+
173+
```clojure
174+
(into [] my-cat cat-data)
175+
; => [1 2 3 4 5 :sleep 6 7]
176+
```
177+
178+
* Step 3, sleepy cat
179+
180+
That version of the transducer falls asleep an the`:sleep` keyword and do not process any subsequent data.
181+
182+
```clojure
183+
(into [] my-cat cat-data)
184+
; => [1 2 3 4 5]
185+
```
186+
187+
* Step 4, correct cat
188+
189+
At last, we want our transducer to respect the normal early termination (with`reduced?` tested on the downstream result) in a correct manner.
190+
191+
```clojure
192+
(into [] (comp (take2)
193+
my-cat)
194+
cat-data)
195+
; => [1 2 3 4]
196+
197+
(into [] (comp my-cat
198+
(take2))
199+
cat-data)
200+
; => [1 2]
201+
202+
(slow-into [] (comp (debug0)
203+
my-cat; try replacing it with `cat` and compare
204+
(debug2)
205+
(take2)
206+
(debug4))
207+
cat-data)
208+
;; Outputs:
209+
; > [1 2 :fish 3]
210+
; > 1
211+
; > 1
212+
; < [1]
213+
; < [1]
214+
; > 2
215+
; > 2
216+
; < [1 2]
217+
; < #reduced[{:status :ready, :val [1 2]} 0x517a7e8e]
218+
; < #reduced[{:status :ready, :val [1 2]} 0x517a7e8e]
219+
```
220+
221+
* Provide an idiomatic equivalent to`my-cat`.
222+
223+
224+
##A.D.D. transducer
225+
226+
* 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.
227+
228+
The`a-d-d` transducer never loses data.
229+
230+
```clojure
231+
(into [] (a-d-d3) (range10))
232+
; => [0 1 2 3 4 5 6 7 8 9]
233+
234+
; Try:
235+
(slow-into [] (comp (debug0)
236+
(a-d-d3)
237+
(debug2))
238+
(range10))
239+
```
240+
241+
* Change`a-d-d` so that it works similarly to`clojure.core/partition-all`.
242+
243+
```clojure
244+
(into [] (a-d-d3) (range10))
245+
; => [[0 1 2] [3 4 5] [6 7 8] [9]]
246+
```
247+
248+
* Verify that it works well with early termination.
249+
250+
```clojure
251+
(slow-into [] (comp (debug0)
252+
(a-d-d3)
253+
(debug2)
254+
(take2))
255+
(range10))
256+
; => [[0 1 2] [3 4 5]]
257+
```
258+
259+
##A reducer inside a transducer
260+
261+
Implement`serieduce` which provides a transducer which reduces incoming elements and emits all intermediary elements.
262+
263+
```clojure
264+
(into [] (serieduce conj [12]) (range36))
265+
; => [[1 2 3] [1 2 3 4] [1 2 3 4 5]]
266+
267+
(into [] (serieduce +) (range5))
268+
; => [0 1 3 6 10]
269+
```
270+
271+
##Congratulations if you made it that far!
272+
273+
![Success Kid](img/success kid.png)
274+
275+
You are one of a few.

‎img/coco transducer.jpg‎

123 KB
Loading

‎img/meme kid cry.gif‎

9.78 MB
Loading

‎img/php hadouken.jpg‎

42.9 KB
Loading

‎img/success kid.png‎

628 KB
Loading

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp