1 # Symbolic Evaluation with SymPy
7 from joy.joy import run
8 from joy.library import UnaryBuiltinWrapper
9 from joy.utils.pretty_print import TracePrinter
10 from joy.utils.stack import list_to_stack
12 from notebook_preamble import D, J, V, define
20 ## [SypPy Symbols](http://docs.sympy.org/latest/modules/core.html#module-sympy.core.symbol)
22 The SymPy package provides a powerful and elegant ["thunk"](https://en.wikipedia.org/wiki/Thunk) object that can take the place of a numeric value in calculations and "record" the operations performed on it.
24 We can create some of these objects and put them on the Joy stack:
28 stack = list_to_stack(sympy.symbols('c a b'))
31 If we evaluate the `quadratic` program
33 over [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2
35 The [SypPy Symbols](http://docs.sympy.org/latest/modules/core.html#module-sympy.core.symbol) will become the symbolic expression of the math operations. Unfortunately, the library `sqrt` function doesn't work with the SymPy objects:
39 viewer = TracePrinter()
41 run('over [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2', stack, D, viewer.viewer)
47 can't convert expression to float
48 b a c . over [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2
49 b a c a . [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2
50 b a c a [[[neg] dupdip sqr 4] dipd * * - sqrt pm] . dip 2 * [/] cons app2
51 b a c . [[neg] dupdip sqr 4] dipd * * - sqrt pm a 2 * [/] cons app2
52 b a c [[neg] dupdip sqr 4] . dipd * * - sqrt pm a 2 * [/] cons app2
53 b . [neg] dupdip sqr 4 a c * * - sqrt pm a 2 * [/] cons app2
54 b [neg] . dupdip sqr 4 a c * * - sqrt pm a 2 * [/] cons app2
55 b . neg b sqr 4 a c * * - sqrt pm a 2 * [/] cons app2
56 -b . b sqr 4 a c * * - sqrt pm a 2 * [/] cons app2
57 -b b . sqr 4 a c * * - sqrt pm a 2 * [/] cons app2
58 -b b . dup mul 4 a c * * - sqrt pm a 2 * [/] cons app2
59 -b b b . mul 4 a c * * - sqrt pm a 2 * [/] cons app2
60 -b b**2 . 4 a c * * - sqrt pm a 2 * [/] cons app2
61 -b b**2 4 . a c * * - sqrt pm a 2 * [/] cons app2
62 -b b**2 4 a . c * * - sqrt pm a 2 * [/] cons app2
63 -b b**2 4 a c . * * - sqrt pm a 2 * [/] cons app2
64 -b b**2 4 a*c . * - sqrt pm a 2 * [/] cons app2
65 -b b**2 4*a*c . - sqrt pm a 2 * [/] cons app2
66 -b -4*a*c + b**2 . sqrt pm a 2 * [/] cons app2
69 We can pick out that first symbolic expression obect from the Joy stack:
73 S, E = viewer.history[-1]
89 The Python `math.sqrt()` function causes the "can't convert expression to float" exception but `sympy.sqrt()` does not:
99 $$\sqrt{- 4 a c + b^{2}}$$
108 D['sqrt'] = UnaryBuiltinWrapper(sympy.sqrt)
111 Now it works just fine.
115 (root1, (root2, _)) = run('over [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2', stack, D)[0]
126 $$\frac{1}{2 a} \left(- b - \sqrt{- 4 a c + b^{2}}\right)$$
138 $$\frac{1}{2 a} \left(- b + \sqrt{- 4 a c + b^{2}}\right)$$
142 At some point I will probably make an optional library of Joy wrappers for SymPy functions, and either load it automatically if SymPy installation is available or have a CLI switch or something. There's a huge amount of incredibly useful stuff and I don't see why Joy shouldn't expose another interface for using it. (As an example, the symbolic expressions can be "lambdafied" into very fast versions, i.e. a function that takes `a`, `b`, and `c` and computes the value of the root using just low-level fast code, bypassing Joy and Python. Also, Numpy, &c.)
144 ## Partial Evaluation
146 [Futamura projections](https://en.wikipedia.org/wiki/Futamura_projection)
148 Starting with the example from [Partial Computation of Programs](http://hdl.handle.net/2433/103401)
149 by [Yoshihiko Futamura](http://fi.ftmr.info/) of a function to compute `u` to the `k`th power:
163 Partial evaluation with `k = 5`:
175 Translate `F(u, k)` to Joy
178 [pop] [Fw] while # the while statement
179 popopd # discard u k, "return" z
184 u k z [pop odd] [Ft] [] ifte # the if statement
185 [2 //] dip # k = k / 2 floordiv
186 [sqr] dipd # u = u * u
188 [[sqr] dip 2 //] dip # We can merge last two lines.
190 Helper function Ft (to compute z = z * u).
203 Fb == [[sqr] dip 2 //] dip
204 Fw == [pop odd] [Ft] [] ifte Fb
205 F == 1 [pop] [Fw] while popopd
214 define('Ft == [over] dip *')
219 define('Fb == [[sqr] dip 2 //] dip')
224 define('Fw == [pop odd] [Ft] [] ifte Fb')
229 define('F == 1 [pop] [Fw] while popopd')
242 In order to elide the tests let's define special versions of `while` and `ifte`:
246 from joy.joy import joy
247 from joy.library import FunctionWrapper
248 from joy.parser import Symbol
249 from joy.utils.stack import concat
252 S_while = Symbol('while')
256 def while_(S, expression, dictionary):
257 '''[if] [body] while'''
258 (body, (if_, stack)) = S
259 if joy(stack, if_, dictionary)[0][0]:
260 expression = concat(body, (if_, (body, (S_while, expression))))
261 return stack, expression, dictionary
265 def ifte(stack, expression, dictionary):
266 '''[if] [then] [else] ifte'''
267 (else_, (then, (if_, stack))) = stack
268 if_res = joy(stack, if_, dictionary)[0][0]
269 quote = then if if_res else else_
270 expression = concat(quote, expression)
271 return (stack, expression, dictionary)
278 And with a SymPy symbol for the `u` argument:
282 stack = list_to_stack([5, sympy.symbols('u')])
283 viewer = TracePrinter()
285 (result, _) = run('F', stack, D, viewer.viewer)[0]
292 u 5 . 1 [pop] [Fw] while popopd
293 u 5 1 . [pop] [Fw] while popopd
294 u 5 1 [pop] . [Fw] while popopd
295 u 5 1 [pop] [Fw] . while popopd
296 u 5 1 . Fw [pop] [Fw] while popopd
297 u 5 1 . [pop odd] [Ft] [] ifte Fb [pop] [Fw] while popopd
298 u 5 1 [pop odd] . [Ft] [] ifte Fb [pop] [Fw] while popopd
299 u 5 1 [pop odd] [Ft] . [] ifte Fb [pop] [Fw] while popopd
300 u 5 1 [pop odd] [Ft] [] . ifte Fb [pop] [Fw] while popopd
301 u 5 1 . Ft Fb [pop] [Fw] while popopd
302 u 5 1 . [over] dip * Fb [pop] [Fw] while popopd
303 u 5 1 [over] . dip * Fb [pop] [Fw] while popopd
304 u 5 . over 1 * Fb [pop] [Fw] while popopd
305 u 5 u . 1 * Fb [pop] [Fw] while popopd
306 u 5 u 1 . * Fb [pop] [Fw] while popopd
307 u 5 u . Fb [pop] [Fw] while popopd
308 u 5 u . [[sqr] dip 2 //] dip [pop] [Fw] while popopd
309 u 5 u [[sqr] dip 2 //] . dip [pop] [Fw] while popopd
310 u 5 . [sqr] dip 2 // u [pop] [Fw] while popopd
311 u 5 [sqr] . dip 2 // u [pop] [Fw] while popopd
312 u . sqr 5 2 // u [pop] [Fw] while popopd
313 u . dup mul 5 2 // u [pop] [Fw] while popopd
314 u u . mul 5 2 // u [pop] [Fw] while popopd
315 u**2 . 5 2 // u [pop] [Fw] while popopd
316 u**2 5 . 2 // u [pop] [Fw] while popopd
317 u**2 5 2 . // u [pop] [Fw] while popopd
318 u**2 2 . u [pop] [Fw] while popopd
319 u**2 2 u . [pop] [Fw] while popopd
320 u**2 2 u [pop] . [Fw] while popopd
321 u**2 2 u [pop] [Fw] . while popopd
322 u**2 2 u . Fw [pop] [Fw] while popopd
323 u**2 2 u . [pop odd] [Ft] [] ifte Fb [pop] [Fw] while popopd
324 u**2 2 u [pop odd] . [Ft] [] ifte Fb [pop] [Fw] while popopd
325 u**2 2 u [pop odd] [Ft] . [] ifte Fb [pop] [Fw] while popopd
326 u**2 2 u [pop odd] [Ft] [] . ifte Fb [pop] [Fw] while popopd
327 u**2 2 u . Fb [pop] [Fw] while popopd
328 u**2 2 u . [[sqr] dip 2 //] dip [pop] [Fw] while popopd
329 u**2 2 u [[sqr] dip 2 //] . dip [pop] [Fw] while popopd
330 u**2 2 . [sqr] dip 2 // u [pop] [Fw] while popopd
331 u**2 2 [sqr] . dip 2 // u [pop] [Fw] while popopd
332 u**2 . sqr 2 2 // u [pop] [Fw] while popopd
333 u**2 . dup mul 2 2 // u [pop] [Fw] while popopd
334 u**2 u**2 . mul 2 2 // u [pop] [Fw] while popopd
335 u**4 . 2 2 // u [pop] [Fw] while popopd
336 u**4 2 . 2 // u [pop] [Fw] while popopd
337 u**4 2 2 . // u [pop] [Fw] while popopd
338 u**4 1 . u [pop] [Fw] while popopd
339 u**4 1 u . [pop] [Fw] while popopd
340 u**4 1 u [pop] . [Fw] while popopd
341 u**4 1 u [pop] [Fw] . while popopd
342 u**4 1 u . Fw [pop] [Fw] while popopd
343 u**4 1 u . [pop odd] [Ft] [] ifte Fb [pop] [Fw] while popopd
344 u**4 1 u [pop odd] . [Ft] [] ifte Fb [pop] [Fw] while popopd
345 u**4 1 u [pop odd] [Ft] . [] ifte Fb [pop] [Fw] while popopd
346 u**4 1 u [pop odd] [Ft] [] . ifte Fb [pop] [Fw] while popopd
347 u**4 1 u . Ft Fb [pop] [Fw] while popopd
348 u**4 1 u . [over] dip * Fb [pop] [Fw] while popopd
349 u**4 1 u [over] . dip * Fb [pop] [Fw] while popopd
350 u**4 1 . over u * Fb [pop] [Fw] while popopd
351 u**4 1 u**4 . u * Fb [pop] [Fw] while popopd
352 u**4 1 u**4 u . * Fb [pop] [Fw] while popopd
353 u**4 1 u**5 . Fb [pop] [Fw] while popopd
354 u**4 1 u**5 . [[sqr] dip 2 //] dip [pop] [Fw] while popopd
355 u**4 1 u**5 [[sqr] dip 2 //] . dip [pop] [Fw] while popopd
356 u**4 1 . [sqr] dip 2 // u**5 [pop] [Fw] while popopd
357 u**4 1 [sqr] . dip 2 // u**5 [pop] [Fw] while popopd
358 u**4 . sqr 1 2 // u**5 [pop] [Fw] while popopd
359 u**4 . dup mul 1 2 // u**5 [pop] [Fw] while popopd
360 u**4 u**4 . mul 1 2 // u**5 [pop] [Fw] while popopd
361 u**8 . 1 2 // u**5 [pop] [Fw] while popopd
362 u**8 1 . 2 // u**5 [pop] [Fw] while popopd
363 u**8 1 2 . // u**5 [pop] [Fw] while popopd
364 u**8 0 . u**5 [pop] [Fw] while popopd
365 u**8 0 u**5 . [pop] [Fw] while popopd
366 u**8 0 u**5 [pop] . [Fw] while popopd
367 u**8 0 u**5 [pop] [Fw] . while popopd
384 Let's try partial evaluation by hand and use a "stronger" thunk.
386 Caret underscoring indicates terms that form thunks. When an arg is unavailable for a computation we just postpone it until the arg becomes available and in the meantime treat the pending computation as one unit.
389 u 5 . 1 [pop] [Fw] while popopd
390 u 5 1 . [pop] [Fw] while popopd
391 u 5 1 [pop] . [Fw] while popopd
392 u 5 1 [pop] [Fw] . while popopd
393 u 5 1 . Fw [pop] [Fw] while popopd
394 u 5 1 . [pop odd] [Ft] [] ifte Fb [pop] [Fw] while popopd
395 u 5 1 [pop odd] . [Ft] [] ifte Fb [pop] [Fw] while popopd
396 u 5 1 [pop odd] [Ft] . [] ifte Fb [pop] [Fw] while popopd
397 u 5 1 [pop odd] [Ft] [] . ifte Fb [pop] [Fw] while popopd
398 u 5 1 . Ft Fb [pop] [Fw] while popopd
399 u 5 1 . [over] dip * Fb [pop] [Fw] while popopd
400 u 5 1 [over] . dip * Fb [pop] [Fw] while popopd
401 u 5 . over 1 * Fb [pop] [Fw] while popopd
402 u 5 u . 1 * Fb [pop] [Fw] while popopd
403 u 5 u 1 . * Fb [pop] [Fw] while popopd
404 u 5 u . Fb [pop] [Fw] while popopd
405 u 5 u . [[sqr] dip 2 //] dip [pop] [Fw] while popopd
406 u 5 u [[sqr] dip 2 //] . dip [pop] [Fw] while popopd
407 u 5 . [sqr] dip 2 // u [pop] [Fw] while popopd
408 u 5 [sqr] . dip 2 // u [pop] [Fw] while popopd
409 u . sqr 5 2 // u [pop] [Fw] while popopd
410 u . dup mul 5 2 // u [pop] [Fw] while popopd
411 u dup * . 5 2 // u [pop] [Fw] while popopd
415 u dup * 2 u [pop] [Fw] . while popopd
416 u dup * 2 u . Fw [pop] [Fw] while popopd
417 u dup * 2 u . [pop odd] [Ft] [] ifte Fb [pop] [Fw] while popopd
418 u dup * 2 u [pop odd] . [Ft] [] ifte Fb [pop] [Fw] while popopd
419 u dup * 2 u [pop odd] [Ft] . [] ifte Fb [pop] [Fw] while popopd
420 u dup * 2 u [pop odd] [Ft] [] . ifte Fb [pop] [Fw] while popopd
421 u dup * 2 u . Fb [pop] [Fw] while popopd
422 u dup * 2 u . [[sqr] dip 2 //] dip [pop] [Fw] while popopd
423 u dup * 2 u [[sqr] dip 2 //] . dip [pop] [Fw] while popopd
424 u dup * 2 . [sqr] dip 2 // u [pop] [Fw] while popopd
425 u dup * 2 [sqr] . dip 2 // u [pop] [Fw] while popopd
426 u dup * . sqr 2 2 // u [pop] [Fw] while popopd
427 u dup * . dup mul 2 2 // u [pop] [Fw] while popopd
428 u dup * dup * . 2 2 // u [pop] [Fw] while popopd
434 w/ `K == u dup * dup *`
436 K 1 u [pop] [Fw] . while popopd
437 K 1 u . Fw [pop] [Fw] while popopd
438 K 1 u . [pop odd] [Ft] [] ifte Fb [pop] [Fw] while popopd
439 K 1 u [pop odd] . [Ft] [] ifte Fb [pop] [Fw] while popopd
440 K 1 u [pop odd] [Ft] . [] ifte Fb [pop] [Fw] while popopd
441 K 1 u [pop odd] [Ft] [] . ifte Fb [pop] [Fw] while popopd
442 K 1 u . Ft Fb [pop] [Fw] while popopd
443 K 1 u . [over] dip * Fb [pop] [Fw] while popopd
444 K 1 u [over] . dip * Fb [pop] [Fw] while popopd
445 K 1 . over u * Fb [pop] [Fw] while popopd
446 K 1 K . u * Fb [pop] [Fw] while popopd
447 K 1 K u . * Fb [pop] [Fw] while popopd
448 K 1 K u * . Fb [pop] [Fw] while popopd
453 K 1 L . Fb [pop] [Fw] while popopd
454 K 1 L . [[sqr] dip 2 //] dip [pop] [Fw] while popopd
455 K 1 L [[sqr] dip 2 //] . dip [pop] [Fw] while popopd
456 K 1 . [sqr] dip 2 // L [pop] [Fw] while popopd
457 K 1 [sqr] . dip 2 // L [pop] [Fw] while popopd
458 K . sqr 1 2 // L [pop] [Fw] while popopd
459 K . dup mul 1 2 // L [pop] [Fw] while popopd
460 K K . mul 1 2 // L [pop] [Fw] while popopd
461 K K * . 1 2 // L [pop] [Fw] while popopd
463 K K * . 1 2 // L [pop] [Fw] while popopd
464 K K * 1 . 2 // L [pop] [Fw] while popopd
465 K K * 1 2 . // L [pop] [Fw] while popopd
466 K K * 0 . L [pop] [Fw] while popopd
467 K K * 0 L . [pop] [Fw] while popopd
468 K K * 0 L [pop] . [Fw] while popopd
469 K K * 0 L [pop] [Fw] . while popopd
479 Our result "thunk" would be:
483 Mechanically, you could do:
486 u u [dup * dup *] dip *
487 u dup [dup * dup *] dip *
490 F5 == dup [dup * dup *] dip *
492 But we can swap the two arguments to the final `*` to get all mentions of `u` to the left:
498 Then de-duplicate "u":
504 To arrive at a startlingly elegant form for F5:
507 F5 == dup dup * dup * *
511 stack = list_to_stack([sympy.symbols('u')])
512 viewer = TracePrinter()
514 (result, _) = run('dup dup * dup * *', stack, D, viewer.viewer)[0]
521 u . dup dup * dup * *
537 I'm not sure how to implement these kinds of thunks. I think you have to have support in the interpreter, or you have to modify all of the functions like `dup` to check for thunks in their inputs.
539 Working on the compiler, from this:
543 We can already generate:
545 ---------------------------------
551 ---------------------------------
553 This is pretty old stuff... (E.g. from 1999, M. Anton Ertl [Compilation of Stack-Based Languages](http://www.complang.tuwien.ac.at/projects/rafts.html) he goes a lot further for Forth.)
560 ## "A Transformation Based Approach to Semantics-Directed Code Generation"
562 by Arthur Nunes-Harwitt
564 https://dl.acm.org/citation.cfm?doid=2635648.2635652
570 def m(x, y): return x * y
580 def m(x): return lambda y: x * y
590 def m(x): return "lambda y: %(x)s * y" % locals()
613 def p(n, b): # original
614 return 1 if n == 0 else b * p(n - 1, b)
618 return lambda b: 1 if n == 0 else b * p(n - 1, b)
622 return "lambda b: 1 if %(n)s == 0 else b * p(%(n)s - 1, b)" % locals()
628 lambda b: 1 if 3 == 0 else b * p(3 - 1, b)
633 p == [0 =] [popop 1] [-- over] [dip *] genrec
636 b n [0 =] [popop 1] [-- over [p] dip *]
638 b n -- over [p] dip *
647 ---------------------------------------------
648 [[n 0 =] [pop 1] [dup n --] [*] genrec]
652 def p(n): # lambda lowered
656 lambda b: b * p(n - 1, b)
660 def p(n): # lambda lowered quoted
664 "lambda b: b * p(%(n)s - 1, b)" % locals()
670 lambda b: b * p(3 - 1, b)
674 p == [0 =] [[pop 1]] [ [-- [dup] dip p *] cons ]ifte
678 3 [-- [dup] dip p *] cons
684 def p(n): # expression lifted
688 return lambda b: b * f(b)
703 return "lambda b: b * (%(f)s)(b)" % locals()
709 lambda b: b * (lambda b: b * (lambda b: b * (lambda b: 1)(b))(b))(b)
713 p == [0 =] [pop [pop 1]] [-- p [dupdip *] cons] ifte
717 3 -- p [dupdip *] cons
719 2 -- p [dupdip *] cons [dupdip *] cons
720 1 p [dupdip *] cons [dupdip *] cons
721 1 -- p [dupdip *] cons [dupdip *] cons [dupdip *] cons
722 0 p [dupdip *] cons [dupdip *] cons [dupdip *] cons
723 0 pop [pop 1] [dupdip *] cons [dupdip *] cons [dupdip *] cons
724 [pop 1] [dupdip *] cons [dupdip *] cons [dupdip *] cons
726 [[[[pop 1] dupdip *] dupdip *] dupdip *]
729 2 [[[[pop 1] dupdip *] dupdip *] dupdip *] i
730 2 [[[pop 1] dupdip *] dupdip *] dupdip *
731 2 [[pop 1] dupdip *] dupdip * 2 *
732 2 [pop 1] dupdip * 2 * 2 *
738 p == [0 =] [pop [pop 1]] [-- p [dupdip *] cons] ifte
739 p == [0 =] [pop [pop 1]] [-- [p] i [dupdip *] cons] ifte
740 p == [0 =] [pop [pop 1]] [--] [i [dupdip *] cons] genrec
744 define('p == [0 =] [pop [pop 1]] [--] [i [dupdip *] cons] genrec')
752 [[[[pop 1] dupdip *] dupdip *] dupdip *]
757 V('2 [[[[pop 1] dupdip *] dupdip *] dupdip *] i')
760 . 2 [[[[pop 1] dupdip *] dupdip *] dupdip *] i
761 2 . [[[[pop 1] dupdip *] dupdip *] dupdip *] i
762 2 [[[[pop 1] dupdip *] dupdip *] dupdip *] . i
763 2 . [[[pop 1] dupdip *] dupdip *] dupdip *
764 2 [[[pop 1] dupdip *] dupdip *] . dupdip *
765 2 . [[pop 1] dupdip *] dupdip * 2 *
766 2 [[pop 1] dupdip *] . dupdip * 2 *
767 2 . [pop 1] dupdip * 2 * 2 *
768 2 [pop 1] . dupdip * 2 * 2 *
769 2 . pop 1 2 * 2 * 2 *
782 stack = list_to_stack([sympy.symbols('u')])
783 (result, s) = run('p i', stack, D)[0]
789 p == [0 =] [pop pop 1] [-- over] [dip *] genrec
793 p == [0 =] [pop [pop 1]] [--] [i [dupdip *] cons] genrec
798 ## Try it with `F()`:
802 def odd(n): return n % 2
860 return uu * u if odd(k) else uu
874 z = lambda u: (lambda fu, u: fu * fu * u)(f(u), u)
876 z = lambda u: (lambda fu, u: fu * fu)(f(u), u)
890 z = "lambda u: (lambda fu, u: fu * fu * u)((%(f)s)(u), u)" % locals()
892 z = "lambda u: (lambda fu, u: fu * fu)((%(f)s)(u), u)" % locals()
918 z = "lambda u: (lambda fu, u: fu * fu * u)((%(f)s)(u), u)" % locals()
920 z = "lambda u: (lambda fu, u: fu * fu)((%(f)s)(u), u)" % locals()
947 z = "lambda u: u * u * u"
949 z = "lambda u: (lambda fu, u: fu * fu * u)((%s)(u), u)" % F(m)
954 z = "lambda u: u * u"
956 z = "lambda u: (lambda u: u * u)((%s)(u))" % F(m)
978 z = "lambda u: u * u * u"
980 z = "lambda u: (lambda fu, u: fu * fu * u)((%s)(u), u)" % F(m)
983 z = "lambda u: u * u"
985 z = "lambda u: (lambda u: u * u)((%s)(u))" % F(m)
997 print n, '%2i' % eval(source)(2), source
1000 So that gets pretty good, eh?
1003 But looking back at the definition in Joy, it doesn't seem easy to directly apply this technique to Joy code:
1006 Fb == [[sqr] dip 2 //] dip
1007 Fw == [pop odd] [Ft] [] ifte Fb
1008 F == 1 [pop] [Fw] while popopd
1011 But a direct translation of the Python code..?
1026 _F.1 == [1 =] [pop [dup dup * *]] [popd F [dupdip over * *] cons] ifte
1027 _F.2 == [1 =] [pop [dup *]] [popd F [i dup *] cons] ifte
1033 5 [ [[0 =] [pop 1]] [[1 =] []] [_F.0] ] cond
1035 5 dup 2 // [ [[0 =] [pop 1]] [[pop odd] _F.1] [_F.2] ] cond
1036 5 5 2 // [ [[0 =] [pop 1]] [[pop odd] _F.1] [_F.2] ] cond
1038 5 2 [ [[0 =] [pop 1]] [[pop odd] _F.1] [_F.2] ] cond
1040 5 2 [1 =] [popop [dup dup * *]] [popd F [dupdip over * *] cons] ifte
1041 5 2 popd F [dupdip over * *] cons
1042 2 F [dupdip over * *] cons
1044 2 F [dupdip over * *] cons
1047 2 [ [[0 =] [pop 1]] [[1 =] []] [_F.0] ] cond
1049 2 dup 2 // [ [[0 =] [pop 1]] [[pop odd] _F.1] [_F.2] ] cond
1050 2 2 2 // [ [[0 =] [pop 1]] [[pop odd] _F.1] [_F.2] ] cond
1051 2 1 [ [[0 =] [pop 1]] [[pop odd] _F.1] [_F.2] ] cond
1053 2 1 [1 =] [popop [dup *]] [popd F [i dup *] cons] ifte
1058 2 F [dupdip over * *] cons
1059 [dup *] [dupdip over * *] cons
1060 [[dup *] dupdip over * *]
1062 And here it is in action:
1064 2 [[dup *] dupdip over * *] i
1065 2 [dup *] dupdip over * *
1075 So, it works, but in this case the results of the partial evaluation are more elegant.
1082 ## Try it with `hylomorphism()`:
1086 def hylomorphism(c, F, P, G):
1087 '''Return a hylomorphism function H.'''
1094 result = F(b, H(aa))
1102 With abuse of syntax:
1106 def hylomorphism(c):
1107 return lambda F: lambda P: lambda G: lambda a: (
1112 result = F(b)(H(aa))
1123 def hylomorphism(c):
1127 return lambda F: lambda G: c
1128 return lambda F: lambda G: (
1138 ### expression lifting
1143 def hylomorphism(c):
1150 H = hylomorphism(c)(aa)(P)(G)
1151 return lambda F: F(b)(H(F))
1164 def hylomorphism(c):
1169 return "lambda F: %s" % (c,)
1171 H = hylomorphism(c)(aa)(P)(G)
1172 return "lambda F: F(%(b)s)((%(H)s)(F))" % locals()
1182 hylomorphism(0)(3)(lambda n: n == 0)(lambda n: (n-1, n-1))
1202 eval(hylomorphism(0)(5)(lambda n: n == 0)(lambda n: (n-1, n-1)))(F)
1207 eval(hylomorphism([])(5)(lambda n: n == 0)(lambda n: (n-1, n-1)))(lambda a: lambda b: [a] + b)
1217 hylomorphism(0)([1, 2, 3])(lambda n: not n)(lambda n: (n[0], n[1:]))
1222 hylomorphism([])([1, 2, 3])(lambda n: not n)(lambda n: (n[1:], n[1:]))
1227 eval(hylomorphism([])([1, 2, 3])(lambda n: not n)(lambda n: (n[1:], n[1:])))(lambda a: lambda b: [a] + b)
1247 def hylomorphism(c):
1248 return lambda a: lambda P: (
1250 result = lambda F: lambda G: c
1252 result = lambda F: lambda G: (
1263 def hylomorphism(c):
1265 lambda F: lambda P: lambda G: c
1267 lambda F: lambda P: lambda G: (
1277 def hylomorphism(c):
1278 return lambda a: lambda G: (
1279 lambda F: lambda P: c
1282 lambda F: lambda P: F(b)(H(aa))