OSDN Git Service

Py 3 handles exception propagation a little differently?
[joypy/Thun.git] / docs / Advent_of_Code_2017_December_2nd.rst
1 Advent of Code 2017
2 ===================
3
4 December 2nd
5 ------------
6
7 For each row, determine the difference between the largest value and the
8 smallest value; the checksum is the sum of all of these differences.
9
10 For example, given the following spreadsheet:
11
12 ::
13
14     5 1 9 5
15     7 5 3
16     2 4 6 8
17
18 -  The first row's largest and smallest values are 9 and 1, and their
19    difference is 8.
20 -  The second row's largest and smallest values are 7 and 3, and their
21    difference is 4.
22 -  The third row's difference is 6.
23
24 In this example, the spreadsheet's checksum would be 8 + 4 + 6 = 18.
25
26 .. code:: ipython3
27
28     from notebook_preamble import J, V, define
29
30 I'll assume the input is a Joy sequence of sequences of integers.
31
32 ::
33
34     [[5 1 9 5]
35      [7 5 3]
36      [2 4 6 8]]
37
38 So, obviously, the initial form will be a ``step`` function:
39
40 ::
41
42     AoC2017.2 == 0 swap [F +] step
43
44 This function ``F`` must get the ``max`` and ``min`` of a row of numbers
45 and subtract. We can define a helper function ``maxmin`` which does
46 this:
47
48 .. code:: ipython3
49
50     define('maxmin [max] [min] cleave')
51
52 .. code:: ipython3
53
54     J('[1 2 3] maxmin')
55
56
57 .. parsed-literal::
58
59     3 1
60
61
62 Then ``F`` just does that then subtracts the min from the max:
63
64 ::
65
66     F == maxmin -
67
68 So:
69
70 .. code:: ipython3
71
72     define('AoC2017.2 [maxmin - +] step_zero')
73
74 .. code:: ipython3
75
76     J('''
77     
78     [[5 1 9 5]
79      [7 5 3]
80      [2 4 6 8]] AoC2017.2
81     
82     ''')
83
84
85 .. parsed-literal::
86
87     18
88
89
90 ...find the only two numbers in each row where one evenly divides the
91 other - that is, where the result of the division operation is a whole
92 number. They would like you to find those numbers on each line, divide
93 them, and add up each line's result.
94
95 For example, given the following spreadsheet:
96
97 ::
98
99     5 9 2 8
100     9 4 7 3
101     3 8 6 5
102
103 -  In the first row, the only two numbers that evenly divide are 8 and
104    2; the result of this division is 4.
105 -  In the second row, the two numbers are 9 and 3; the result is 3.
106 -  In the third row, the result is 2.
107
108 In this example, the sum of the results would be 4 + 3 + 2 = 9.
109
110 What is the sum of each row's result in your puzzle input?
111
112 .. code:: ipython3
113
114     J('[5 9 2 8] sort reverse')
115
116
117 .. parsed-literal::
118
119     [9 8 5 2]
120
121
122 .. code:: ipython3
123
124     J('[9 8 5 2] uncons [swap [divmod] cons] dupdip')
125
126
127 .. parsed-literal::
128
129     [8 5 2] [9 divmod] [8 5 2]
130
131
132 ::
133
134     [9 8 5 2] uncons [swap [divmod] cons F] dupdip G
135       [8 5 2]            [9 divmod]      F [8 5 2] G
136
137 .. code:: ipython3
138
139     V('[8 5 2] [9 divmod] [uncons swap] dip dup [i not] dip')
140
141
142 .. parsed-literal::
143
144                                           • [8 5 2] [9 divmod] [uncons swap] dip dup [i not] dip
145                                   [8 5 2] • [9 divmod] [uncons swap] dip dup [i not] dip
146                        [8 5 2] [9 divmod] • [uncons swap] dip dup [i not] dip
147          [8 5 2] [9 divmod] [uncons swap] • dip dup [i not] dip
148                                   [8 5 2] • uncons swap [9 divmod] dup [i not] dip
149                                   8 [5 2] • swap [9 divmod] dup [i not] dip
150                                   [5 2] 8 • [9 divmod] dup [i not] dip
151                        [5 2] 8 [9 divmod] • dup [i not] dip
152             [5 2] 8 [9 divmod] [9 divmod] • [i not] dip
153     [5 2] 8 [9 divmod] [9 divmod] [i not] • dip
154                        [5 2] 8 [9 divmod] • i not [9 divmod]
155                                   [5 2] 8 • 9 divmod not [9 divmod]
156                                 [5 2] 8 9 • divmod not [9 divmod]
157                                 [5 2] 1 1 • not [9 divmod]
158                             [5 2] 1 False • [9 divmod]
159                  [5 2] 1 False [9 divmod] • 
160
161
162 Tricky
163 ------
164
165 Let's think.
166
167 Given a *sorted* sequence (from highest to lowest) we want to \* for
168 head, tail in sequence \* for term in tail: \* check if the head % term
169 == 0 \* if so compute head / term and terminate loop \* else continue
170
171 So we want a ``loop`` I think
172 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
173
174 ::
175
176     [a b c d] True [Q] loop
177     [a b c d] Q    [Q] loop
178
179 ``Q`` should either leave the result and False, or the ``rest`` and
180 True.
181
182 ::
183
184        [a b c d] Q
185     -----------------
186         result 0
187
188        [a b c d] Q
189     -----------------
190         [b c d] 1
191
192 This suggests that ``Q`` should start with:
193
194 ::
195
196     [a b c d] uncons dup roll<
197     [b c d] [b c d] a
198
199 Now we just have to ``pop`` it if we don't need it.
200
201 ::
202
203     [b c d] [b c d] a [P] [T] [cons] app2 popdd [E] primrec
204     [b c d] [b c d] [a P] [a T]                 [E] primrec
205
206 --------------
207
208 ::
209
210     w/ Q == [% not] [T] [F] primrec
211
212             [a b c d] uncons
213             a [b c d] tuck
214     [b c d] a [b c d] uncons
215     [b c d] a b [c d] roll>
216     [b c d] [c d] a b Q
217     [b c d] [c d] a b [% not] [T] [F] primrec
218
219     [b c d] [c d] a b T
220     [b c d] [c d] a b / roll> popop 0
221
222     [b c d] [c d] a b F                   Q
223     [b c d] [c d] a b pop swap uncons ... Q
224     [b c d] [c d] a       swap uncons ... Q
225     [b c d] a [c d]            uncons ... Q
226     [b c d] a c [d]                   roll> Q
227     [b c d] [d] a c Q
228
229     Q == [% not] [/ roll> popop 0] [pop swap uncons roll>] primrec
230
231     uncons tuck uncons roll> Q
232
233 .. code:: ipython3
234
235     J('[8 5 3 2] 9 [swap] [% not] [cons] app2 popdd')
236
237
238 .. parsed-literal::
239
240     [8 5 3 2] [9 swap] [9 % not]
241
242
243 --------------
244
245 ::
246
247             [a b c d] uncons
248             a [b c d] tuck
249     [b c d] a [b c d] [not] [popop 1] [Q] ifte
250
251     [b c d] a [] popop 1
252     [b c d] 1
253
254     [b c d] a [b c d] Q 
255
256
257        a [...] Q
258     ---------------
259        result 0
260
261        a [...] Q
262     ---------------
263            1
264
265
266     w/ Q == [first % not] [first / 0] [rest [not] [popop 1]] [ifte]
267
268
269
270     a [b c d] [first % not] [first / 0] [rest [not] [popop 1]] [ifte]
271     a [b c d]  first % not
272     a b % not
273     a%b not
274     bool(a%b)
275
276     a [b c d] [first % not] [first / 0] [rest [not] [popop 1]] [ifte]
277     a [b c d]                first / 0
278     a b / 0
279     a/b 0
280
281     a [b c d] [first % not] [first / 0] [rest [not] [popop 1]]   [ifte]
282     a [b c d]                            rest [not] [popop 1] [Q] ifte
283     a [c d]                                   [not] [popop 1] [Q] ifte
284     a [c d]                                   [not] [popop 1] [Q] ifte
285
286     a [c d] [not] [popop 1] [Q] ifte
287     a [c d]  not
288
289     a [] popop 1
290     1
291
292     a [c d] Q
293
294
295     uncons tuck [first % not] [first / 0] [rest [not] [popop 1]] [ifte]
296
297 I finally sat down with a piece of paper and blocked it out.
298 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
299
300 First, I made a function ``G`` that expects a number and a sequence of
301 candidates and return the result or zero:
302
303 ::
304
305        n [...] G
306     ---------------
307         result
308
309        n [...] G
310     ---------------
311            0
312
313 It's a recursive function that conditionally executes the recursive part
314 of its recursive branch
315
316 ::
317
318     [Pg] [E] [R1 [Pi] [T]] [ifte] genrec
319
320 The recursive branch is the else-part of the inner ``ifte``:
321
322 ::
323
324     G == [Pg] [E] [R1 [Pi] [T]]   [ifte] genrec
325       == [Pg] [E] [R1 [Pi] [T] [G] ifte] ifte
326
327 But this is in hindsight. Going forward I derived:
328
329 ::
330
331     G == [first % not]
332          [first /]
333          [rest [not] [popop 0]]
334          [ifte] genrec
335
336 The predicate detects if the ``n`` can be evenly divided by the
337 ``first`` item in the list. If so, the then-part returns the result.
338 Otherwise, we have:
339
340 ::
341
342     n [m ...] rest [not] [popop 0] [G] ifte
343     n [...]        [not] [popop 0] [G] ifte
344
345 This ``ifte`` guards against empty sequences and returns zero in that
346 case, otherwise it executes ``G``.
347
348 .. code:: ipython3
349
350     define('G [first % not] [first /] [rest [not] [popop 0]] [ifte] genrec')
351
352 Now we need a word that uses ``G`` on each (head, tail) pair of a
353 sequence until it finds a (non-zero) result. It's going to be designed
354 to work on a stack that has some candidate ``n``, a sequence of possible
355 divisors, and a result that is zero to signal to continue (a non-zero
356 value implies that it is the discovered result):
357
358 ::
359
360        n [...] p find-result
361     ---------------------------
362               result
363
364 It applies ``G`` using ``nullary`` because if it fails with one
365 candidate it needs the list to get the next one (the list is otherwise
366 consumed by ``G``.)
367
368 ::
369
370     find-result == [0 >] [roll> popop] [roll< popop uncons [G] nullary] tailrec
371
372     n [...] p [0 >] [roll> popop] [roll< popop uncons [G] nullary] tailrec
373
374 The base-case is trivial, return the (non-zero) result. The recursive
375 branch...
376
377 ::
378
379     n [...] p roll< popop uncons [G] nullary find-result
380     [...] p n       popop uncons [G] nullary find-result
381     [...]                 uncons [G] nullary find-result
382     m [..]                       [G] nullary find-result
383     m [..] p                                 find-result
384
385 The puzzle states that the input is well-formed, meaning that we can
386 expect a result before the row sequence empties and so do not need to
387 guard the ``uncons``.
388
389 .. code:: ipython3
390
391     define('find-result [0 >] [roll> popop] [roll< popop uncons [G] nullary] tailrec')
392
393 .. code:: ipython3
394
395     J('[11 9 8 7 3 2] 0 tuck find-result')
396
397
398 .. parsed-literal::
399
400     3.0
401
402
403 In order to get the thing started, we need to ``sort`` the list in
404 descending order, then prime the ``find-result`` function with a dummy
405 candidate value and zero ("continue") flag.
406
407 .. code:: ipython3
408
409     define('prep-row sort reverse 0 tuck')
410
411 Now we can define our program.
412
413 .. code:: ipython3
414
415     define('AoC20017.2.extra [prep-row find-result +] step_zero')
416
417 .. code:: ipython3
418
419     J('''
420     
421     [[5 9 2 8]
422      [9 4 7 3]
423      [3 8 6 5]] AoC20017.2.extra
424     
425     ''')
426
427
428 .. parsed-literal::
429
430     9.0
431