OSDN Git Service

ff54dc49d17cdad6a7524b7a4a2c0f7476d46618
[joypy/Thun.git] / docs / fun_with_scan.rst
1
2 .. code:: ipython2
3
4     from notebook_preamble import D, DefinitionWrapper, J, V, define
5
6 On "Two Exercises Found in a Book on Algorithmics"
7 ==================================================
8
9 Bird & Meertens
10
11 Define ``scan`` in terms of a reduction.
12 ----------------------------------------
13
14     Problem I. The reduction operator ``/`` of APL takes some binary
15     operator ``⨁`` on its left and a vector ``x`` of values on its
16     right. The meaning of ``⨁/x`` for ``x = [a b ... z]`` is the value
17     ``a⨁b⨁...⨁z``. For this to be well-defined in the absence of
18     brackets, the operation ``⨁`` has to be associative. Now there is
19     another operator ``\`` of APL called ``scan``. Its effect is closely
20     related to reduction in that we have:
21
22 ::
23
24     ⨁\x = [a a⨁b a⨁b⨁c ... a⨁b⨁...⨁z]
25
26     The problem is to find some definition of ``scan`` as a reduction.
27     In other words, we have to find some function ``f`` and an operator
28     ``⨂`` so that
29
30 ::
31
32     ⨂\x = f(a)⨂f(b)⨂...⨂f(z)
33
34 Designing the Recursive Function
35 --------------------------------
36
37 Ignoring the exact requirements (finding ``f`` and ``⨂``) can we
38 implement ``scan`` as a hylomorphism in Joy?
39
40 Looking at the forms of hylomorphism, ``H3`` is the one to use:
41
42 ``H3``
43 ~~~~~~
44
45 If the combiner and the generator both need to work on the current value
46 then ``dup`` must be used, and the generator must produce one item
47 instead of two (the b is instead the duplicate of a.)
48
49 ::
50
51     H3 == [P] [pop c] [[G] dupdip] [dip F] genrec
52
53     ... a [G] dupdip [H3] dip F
54     ... a  G  a      [H3] dip F
55     ... a′    a      [H3] dip F
56     ... a′ H3 a               F
57     ... a′ [G] dupdip [H3] dip F a F
58     ... a′  G  a′     [H3] dip F a F
59     ... a″     a′     [H3] dip F a F
60     ... a″ H3  a′              F a F
61     ... a″ [G] dupdip [H3] dip F a′ F a F
62     ... a″  G    a″   [H3] dip F a′ F a F
63     ... a‴       a″   [H3] dip F a′ F a F
64     ... a‴ H3    a″            F a′ F a F
65     ... a‴ pop c a″ F a′ F a F
66     ...        c a″ F a′ F a F
67     ...        d      a′ F a F
68     ...        d′          a F
69     ...        d″
70
71 Initial Definition
72 ~~~~~~~~~~~~~~~~~~
73
74 We're building a list of values so this is an "anamorphism". (An
75 anamorphism uses ``[]`` for ``c`` and ``swons`` for ``F``.)
76
77 ::
78
79     scan == [P] [pop []] [[G] dupdip]      [dip swons] genrec
80
81 Convert to ``ifte``:
82
83 ::
84
85     scan == [P] [pop []] [[G] dupdip [scan] dip swons] ifte
86
87 On the recursive branch ``[G] dupdip`` doesn't cut it:
88
89 ::
90
91     [1 2 3] [G] dupdip [scan] dip swons
92     [1 2 3]  G [1 2 3] [scan] dip swons
93
94 Use ``first``
95 ~~~~~~~~~~~~~
96
97 At this point, we want the copy of ``[1 2 3]`` to just be ``1``, so we
98 use ``first``.
99
100 ::
101
102     scan == [P] [pop []] [[G] dupdip first] [dip swons] genrec
103
104     [1 2 3] [G] dupdip first [scan] dip swons
105     [1 2 3]  G [1 2 3] first [scan] dip swons
106     [1 2 3]  G  1            [scan] dip swons
107
108 ``G`` applies ``⨁``
109 ~~~~~~~~~~~~~~~~~~~
110
111 Now what does ``G`` have to do? Just apply ``⨁`` to the first two terms
112 in the list.
113
114 ::
115
116     [1 2 3] G
117     [1 2 3] [⨁] infra
118     [1 2 3] [+] infra
119     [3 3]
120
121 Predicate ``P``
122 ~~~~~~~~~~~~~~~
123
124 Which tells us that the predicate ``[P]`` must guard against lists with
125 less that two items in them:
126
127 ::
128
129     P == size 1 <=
130
131 Let's see what we've got so far:
132
133 ::
134
135     scan == [P        ] [pop []] [[G]         dupdip first] [dip swons] genrec
136     scan == [size 1 <=] [pop []] [[[F] infra] dupdip first] [dip swons] genrec
137
138 Handling the Last Term
139 ~~~~~~~~~~~~~~~~~~~~~~
140
141 This works to a point, but it throws away the last term:
142
143 .. code:: ipython2
144
145     J('[1 2 3] [size 1 <=] [pop []] [[[+] infra] dupdip first] [dip swons] genrec')
146
147
148 .. parsed-literal::
149
150     [1 3]
151
152
153 Hmm... Let's take out the ``pop`` for a sec...
154
155 .. code:: ipython2
156
157     J('[1 2 3] [size 1 <=] [[]] [[[+] infra] dupdip first] [dip swons] genrec')
158
159
160 .. parsed-literal::
161
162     [6] [1 3]
163
164
165 That leaves the last item in our list, then it puts an empty list on the
166 stack and ``swons``'s the new terms onto that. If we leave out that
167 empty list, they will be ``swons``'d onto that list that already has the
168 last item.
169
170 .. code:: ipython2
171
172     J('[1 2 3] [size 1 <=] [] [[[+] infra] dupdip first] [dip swons] genrec')
173
174
175 .. parsed-literal::
176
177     [1 3 6]
178
179
180 Parameterize ``⨁``
181 ~~~~~~~~~~~~~~~~~~
182
183 So we have:
184
185 ::
186
187     [⨁] scan == [size 1 <=] [] [[[⨁] infra] dupdip first] [dip swons] genrec
188
189 Trivially:
190
191 ::
192
193      == [size 1 <=] [] [[[⨁] infra] dupdip first]                 [dip swons] genrec
194      == [[[⨁] infra] dupdip first]           [size 1 <=] [] roll< [dip swons] genrec
195      == [[⨁] infra]      [dupdip first] cons [size 1 <=] [] roll< [dip swons] genrec
196      == [⨁] [infra] cons [dupdip first] cons [size 1 <=] [] roll< [dip swons] genrec
197
198 And so:
199
200 ::
201
202     scan == [infra] cons [dupdip first] cons [size 1 <=] [] roll< [dip swons] genrec
203
204 .. code:: ipython2
205
206     define('scan == [infra] cons [dupdip first] cons [size 1 <=] [] roll< [dip swons] genrec')
207
208 .. code:: ipython2
209
210     J('[1 2 3 4] [+] scan')
211
212
213 .. parsed-literal::
214
215     [1 3 6 10]
216
217
218 .. code:: ipython2
219
220     J('[1 2 3 4] [*] scan')
221
222
223 .. parsed-literal::
224
225     [1 2 6 24]
226
227
228 .. code:: ipython2
229
230     J('[1 2 3 4 5 6 7] [neg +] scan')
231
232
233 .. parsed-literal::
234
235     [1 1 2 2 3 3 4]
236
237
238 Problem 2.
239 ----------
240
241     Define a line to be a sequence of characters not containing the
242     newline character. It is easy to define a function ``Unlines`` that
243     converts a non-empty sequence of lines into a sequence of characters
244     by inserting newline characters between every two lines.
245
246     Since ``Unlines`` is injective, the function ``Lines``, which
247     converts a sequence of characters into a sequence of lines by
248     splitting on newline characters, can be specified as the inverse of
249     ``Unlines``.
250
251     The problem, just as in Problem 1. is to find a definition by
252     reduction of the function ``Lines``.
253
254 ::
255
256     Unlines = uncons ['\n' swap + +] step
257
258 .. code:: ipython2
259
260     J('["hello" "world"] uncons ["\n" swap + +] step')
261
262
263 .. parsed-literal::
264
265     'hello\nworld'
266
267
268 Again ignoring the actual task let's just derive ``Lines``:
269
270 ::
271
272        "abc\nefg\nhij" Lines
273     ---------------------------
274         ["abc" "efg" "hij"]
275
276 Instead of ``P == [size 1 <=]`` we want ``["\n" in]``, and for the
277 base-case of a string with no newlines in it we want to use ``unit``:
278
279 ::
280
281     Lines == ["\n" in] [unit] [R0]       [dip swons] genrec
282     Lines == ["\n" in] [unit] [R0 [Lines] dip swons] ifte
283
284 Derive ``R0``:
285
286 ::
287
288     "a \n b" R0                    [Lines] dip swons
289     "a \n b" split-at-newline swap [Lines] dip swons
290     "a " " b"                 swap [Lines] dip swons
291     " b" "a "                      [Lines] dip swons
292     " b" Lines "a " swons
293     [" b"]     "a " swons
294     ["a " " b"]
295
296 So:
297
298 ::
299
300     R0 == split-at-newline swap
301
302     Lines == ["\n" in] [unit] [split-at-newline swap] [dip swons] genrec
303
304 Missing the Point?
305 ------------------
306
307 This is all good and well, but in the paper many interesting laws and
308 properties are discussed. Am I missing the point?
309
310 ::
311
312     0 [a b c d] [F] step == 0 [a b] [F] step 0 [c d] [F] step concat
313
314 For associative function ``F`` and a "unit" element for that function,
315 here represented by ``0``.
316
317 For functions that don't have a "unit" we can fake it (the example is
318 given of infinity for the ``min(a, b)`` function.) We can also use:
319
320 ::
321
322     safe_step == [size 1 <=] [] [uncons [F] step] ifte
323
324 Or:
325
326 ::
327
328     safe_step == [pop size 1 <=] [pop] [[uncons] dip step] ifte
329
330        [a b c] [F] safe_step
331     ---------------------------
332        a [b c] [F] step
333
334 To limit ``F`` to working on pairs of terms from its domain.