640 lines
13 KiB
ReStructuredText
640 lines
13 KiB
ReStructuredText
|
|
Using ``x`` to Generate Values
|
|
==============================
|
|
|
|
Cf. jp-reprod.html
|
|
|
|
.. code:: ipython2
|
|
|
|
from notebook_preamble import J, V, define
|
|
|
|
Consider the ``x`` combinator:
|
|
|
|
::
|
|
|
|
x == dup i
|
|
|
|
We can apply it to a quoted program consisting of some value ``a`` and a
|
|
function ``B``:
|
|
|
|
::
|
|
|
|
[a B] x
|
|
[a B] a B
|
|
|
|
Let ``B`` function ``swap`` the ``a`` with the quote and run some
|
|
function ``C`` on it to generate a new value ``b``:
|
|
|
|
::
|
|
|
|
B == swap [C] dip
|
|
|
|
[a B] a B
|
|
[a B] a swap [C] dip
|
|
a [a B] [C] dip
|
|
a C [a B]
|
|
b [a B]
|
|
|
|
Now discard the quoted ``a`` with ``rest`` then ``cons`` ``b``:
|
|
|
|
::
|
|
|
|
b [a B] rest cons
|
|
b [B] cons
|
|
[b B]
|
|
|
|
Putting it together, this is the definition of ``B``:
|
|
|
|
::
|
|
|
|
B == swap [C] dip rest cons
|
|
|
|
We can create a quoted program that generates the Natural numbers (0, 1,
|
|
2, ...) by using ``0`` for ``a`` and ``[dup ++]`` for ``[C]``:
|
|
|
|
::
|
|
|
|
[0 swap [dup ++] dip rest cons]
|
|
|
|
Let's try it:
|
|
|
|
.. code:: ipython2
|
|
|
|
V('[0 swap [dup ++] dip rest cons] x')
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
. [0 swap [dup ++] dip rest cons] x
|
|
[0 swap [dup ++] dip rest cons] . x
|
|
[0 swap [dup ++] dip rest cons] . 0 swap [dup ++] dip rest cons
|
|
[0 swap [dup ++] dip rest cons] 0 . swap [dup ++] dip rest cons
|
|
0 [0 swap [dup ++] dip rest cons] . [dup ++] dip rest cons
|
|
0 [0 swap [dup ++] dip rest cons] [dup ++] . dip rest cons
|
|
0 . dup ++ [0 swap [dup ++] dip rest cons] rest cons
|
|
0 0 . ++ [0 swap [dup ++] dip rest cons] rest cons
|
|
0 1 . [0 swap [dup ++] dip rest cons] rest cons
|
|
0 1 [0 swap [dup ++] dip rest cons] . rest cons
|
|
0 1 [swap [dup ++] dip rest cons] . cons
|
|
0 [1 swap [dup ++] dip rest cons] .
|
|
|
|
|
|
After one application of ``x`` the quoted program contains ``1`` and
|
|
``0`` is below it on the stack.
|
|
|
|
.. code:: ipython2
|
|
|
|
J('[0 swap [dup ++] dip rest cons] x x x x x pop')
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
0 1 2 3 4
|
|
|
|
|
|
``direco``
|
|
~~~~~~~~~~
|
|
|
|
.. code:: ipython2
|
|
|
|
define('direco == dip rest cons')
|
|
|
|
.. code:: ipython2
|
|
|
|
V('[0 swap [dup ++] direco] x')
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
. [0 swap [dup ++] direco] x
|
|
[0 swap [dup ++] direco] . x
|
|
[0 swap [dup ++] direco] . 0 swap [dup ++] direco
|
|
[0 swap [dup ++] direco] 0 . swap [dup ++] direco
|
|
0 [0 swap [dup ++] direco] . [dup ++] direco
|
|
0 [0 swap [dup ++] direco] [dup ++] . direco
|
|
0 [0 swap [dup ++] direco] [dup ++] . dip rest cons
|
|
0 . dup ++ [0 swap [dup ++] direco] rest cons
|
|
0 0 . ++ [0 swap [dup ++] direco] rest cons
|
|
0 1 . [0 swap [dup ++] direco] rest cons
|
|
0 1 [0 swap [dup ++] direco] . rest cons
|
|
0 1 [swap [dup ++] direco] . cons
|
|
0 [1 swap [dup ++] direco] .
|
|
|
|
|
|
Making Generators
|
|
=================
|
|
|
|
We want to define a function that accepts ``a`` and ``[C]`` and builds
|
|
our quoted program:
|
|
|
|
::
|
|
|
|
a [C] G
|
|
-------------------------
|
|
[a swap [C] direco]
|
|
|
|
Working in reverse:
|
|
|
|
::
|
|
|
|
[a swap [C] direco] cons
|
|
a [swap [C] direco] concat
|
|
a [swap] [[C] direco] swap
|
|
a [[C] direco] [swap]
|
|
a [C] [direco] cons [swap]
|
|
|
|
Reading from the bottom up:
|
|
|
|
::
|
|
|
|
G == [direco] cons [swap] swap concat cons
|
|
G == [direco] cons [swap] swoncat cons
|
|
|
|
.. code:: ipython2
|
|
|
|
define('G == [direco] cons [swap] swoncat cons')
|
|
|
|
Let's try it out:
|
|
|
|
.. code:: ipython2
|
|
|
|
J('0 [dup ++] G')
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
[0 swap [dup ++] direco]
|
|
|
|
|
|
.. code:: ipython2
|
|
|
|
J('0 [dup ++] G x x x pop')
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
0 1 2
|
|
|
|
|
|
Powers of 2
|
|
~~~~~~~~~~~
|
|
|
|
.. code:: ipython2
|
|
|
|
J('1 [dup 1 <<] G x x x x x x x x x pop')
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
1 2 4 8 16 32 64 128 256
|
|
|
|
|
|
``[x] times``
|
|
~~~~~~~~~~~~~
|
|
|
|
If we have one of these quoted programs we can drive it using ``times``
|
|
with the ``x`` combinator.
|
|
|
|
.. code:: ipython2
|
|
|
|
J('23 [dup ++] G 5 [x] times')
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
23 24 25 26 27 [28 swap [dup ++] direco]
|
|
|
|
|
|
Generating Multiples of Three and Five
|
|
======================================
|
|
|
|
Look at the treatment of the Project Euler Problem One in `Developing a
|
|
Program.ipynb <./Developing%20a%20Program.ipynb>`__ and you'll see that
|
|
we might be interested in generating an endless cycle of:
|
|
|
|
::
|
|
|
|
3 2 1 3 1 2 3
|
|
|
|
To do this we want to encode the numbers as pairs of bits in a single
|
|
int:
|
|
|
|
::
|
|
|
|
3 2 1 3 1 2 3
|
|
0b 11 10 01 11 01 10 11 == 14811
|
|
|
|
And pick them off by masking with 3 (binary 11) and then shifting the
|
|
int right two bits.
|
|
|
|
.. code:: ipython2
|
|
|
|
define('PE1.1 == dup [3 &] dip 2 >>')
|
|
|
|
.. code:: ipython2
|
|
|
|
V('14811 PE1.1')
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
. 14811 PE1.1
|
|
14811 . PE1.1
|
|
14811 . dup [3 &] dip 2 >>
|
|
14811 14811 . [3 &] dip 2 >>
|
|
14811 14811 [3 &] . dip 2 >>
|
|
14811 . 3 & 14811 2 >>
|
|
14811 3 . & 14811 2 >>
|
|
3 . 14811 2 >>
|
|
3 14811 . 2 >>
|
|
3 14811 2 . >>
|
|
3 3702 .
|
|
|
|
|
|
If we plug ``14811`` and ``[PE1.1]`` into our generator form...
|
|
|
|
.. code:: ipython2
|
|
|
|
J('14811 [PE1.1] G')
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
[14811 swap [PE1.1] direco]
|
|
|
|
|
|
...we get a generator that works for seven cycles before it reaches
|
|
zero:
|
|
|
|
.. code:: ipython2
|
|
|
|
J('[14811 swap [PE1.1] direco] 7 [x] times')
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
3 2 1 3 1 2 3 [0 swap [PE1.1] direco]
|
|
|
|
|
|
Reset at Zero
|
|
~~~~~~~~~~~~~
|
|
|
|
We need a function that checks if the int has reached zero and resets it
|
|
if so.
|
|
|
|
.. code:: ipython2
|
|
|
|
define('PE1.1.check == dup [pop 14811] [] branch')
|
|
|
|
.. code:: ipython2
|
|
|
|
J('14811 [PE1.1.check PE1.1] G')
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
[14811 swap [PE1.1.check PE1.1] direco]
|
|
|
|
|
|
.. code:: ipython2
|
|
|
|
J('[14811 swap [PE1.1.check PE1.1] direco] 21 [x] times')
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 [0 swap [PE1.1.check PE1.1] direco]
|
|
|
|
|
|
(It would be more efficient to reset the int every seven cycles but
|
|
that's a little beyond the scope of this article. This solution does
|
|
extra work, but not much, and we're not using it "in production" as they
|
|
say.)
|
|
|
|
Run 466 times
|
|
~~~~~~~~~~~~~
|
|
|
|
In the PE1 problem we are asked to sum all the multiples of three and
|
|
five less than 1000. It's worked out that we need to use all seven
|
|
numbers sixty-six times and then four more.
|
|
|
|
.. code:: ipython2
|
|
|
|
J('7 66 * 4 +')
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
466
|
|
|
|
|
|
If we drive our generator 466 times and sum the stack we get 999.
|
|
|
|
.. code:: ipython2
|
|
|
|
J('[14811 swap [PE1.1.check PE1.1] direco] 466 [x] times')
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 [57 swap [PE1.1.check PE1.1] direco]
|
|
|
|
|
|
.. code:: ipython2
|
|
|
|
J('[14811 swap [PE1.1.check PE1.1] direco] 466 [x] times pop enstacken sum')
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
999
|
|
|
|
|
|
Project Euler Problem One
|
|
=========================
|
|
|
|
.. code:: ipython2
|
|
|
|
define('PE1.2 == + dup [+] dip')
|
|
|
|
Now we can add ``PE1.2`` to the quoted program given to ``G``.
|
|
|
|
.. code:: ipython2
|
|
|
|
J('0 0 0 [PE1.1.check PE1.1] G 466 [x [PE1.2] dip] times popop')
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
233168
|
|
|
|
|
|
A generator for the Fibonacci Sequence.
|
|
=======================================
|
|
|
|
Consider:
|
|
|
|
::
|
|
|
|
[b a F] x
|
|
[b a F] b a F
|
|
|
|
The obvious first thing to do is just add ``b`` and ``a``:
|
|
|
|
::
|
|
|
|
[b a F] b a +
|
|
[b a F] b+a
|
|
|
|
From here we want to arrive at:
|
|
|
|
::
|
|
|
|
b [b+a b F]
|
|
|
|
Let's start with ``swons``:
|
|
|
|
::
|
|
|
|
[b a F] b+a swons
|
|
[b+a b a F]
|
|
|
|
Considering this quote as a stack:
|
|
|
|
::
|
|
|
|
F a b b+a
|
|
|
|
We want to get it to:
|
|
|
|
::
|
|
|
|
F b b+a b
|
|
|
|
So:
|
|
|
|
::
|
|
|
|
F a b b+a popdd over
|
|
F b b+a b
|
|
|
|
And therefore:
|
|
|
|
::
|
|
|
|
[b+a b a F] [popdd over] infra
|
|
[b b+a b F]
|
|
|
|
But we can just use ``cons`` to carry ``b+a`` into the quote:
|
|
|
|
::
|
|
|
|
[b a F] b+a [popdd over] cons infra
|
|
[b a F] [b+a popdd over] infra
|
|
[b b+a b F]
|
|
|
|
Lastly:
|
|
|
|
::
|
|
|
|
[b b+a b F] uncons
|
|
b [b+a b F]
|
|
|
|
Putting it all together:
|
|
|
|
::
|
|
|
|
F == + [popdd over] cons infra uncons
|
|
fib_gen == [1 1 F]
|
|
|
|
.. code:: ipython2
|
|
|
|
define('fib == + [popdd over] cons infra uncons')
|
|
|
|
.. code:: ipython2
|
|
|
|
define('fib_gen == [1 1 fib]')
|
|
|
|
.. code:: ipython2
|
|
|
|
J('fib_gen 10 [x] times')
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
1 2 3 5 8 13 21 34 55 89 [144 89 fib]
|
|
|
|
|
|
Project Euler Problem Two
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
::
|
|
|
|
By considering the terms in the Fibonacci sequence whose values do not exceed four million,
|
|
find the sum of the even-valued terms.
|
|
|
|
Now that we have a generator for the Fibonacci sequence, we need a
|
|
function that adds a term in the sequence to a sum if it is even, and
|
|
``pop``\ s it otherwise.
|
|
|
|
.. code:: ipython2
|
|
|
|
define('PE2.1 == dup 2 % [+] [pop] branch')
|
|
|
|
And a predicate function that detects when the terms in the series
|
|
"exceed four million".
|
|
|
|
.. code:: ipython2
|
|
|
|
define('>4M == 4000000 >')
|
|
|
|
Now it's straightforward to define ``PE2`` as a recursive function that
|
|
generates terms in the Fibonacci sequence until they exceed four million
|
|
and sums the even ones.
|
|
|
|
.. code:: ipython2
|
|
|
|
define('PE2 == 0 fib_gen x [pop >4M] [popop] [[PE2.1] dip x] primrec')
|
|
|
|
.. code:: ipython2
|
|
|
|
J('PE2')
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
4613732
|
|
|
|
|
|
Here's the collected program definitions:
|
|
|
|
::
|
|
|
|
fib == + swons [popdd over] infra uncons
|
|
fib_gen == [1 1 fib]
|
|
|
|
even == dup 2 %
|
|
>4M == 4000000 >
|
|
|
|
PE2.1 == even [+] [pop] branch
|
|
PE2 == 0 fib_gen x [pop >4M] [popop] [[PE2.1] dip x] primrec
|
|
|
|
Even-valued Fibonacci Terms
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Using ``o`` for odd and ``e`` for even:
|
|
|
|
::
|
|
|
|
o + o = e
|
|
e + e = e
|
|
o + e = o
|
|
|
|
So the Fibonacci sequence considered in terms of just parity would be:
|
|
|
|
::
|
|
|
|
o o e o o e o o e o o e o o e o o e
|
|
1 1 2 3 5 8 . . .
|
|
|
|
Every third term is even.
|
|
|
|
.. code:: ipython2
|
|
|
|
J('[1 0 fib] x x x') # To start the sequence with 1 1 2 3 instead of 1 2 3.
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
1 1 2 [3 2 fib]
|
|
|
|
|
|
Drive the generator three times and ``popop`` the two odd terms.
|
|
|
|
.. code:: ipython2
|
|
|
|
J('[1 0 fib] x x x [popop] dipd')
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
2 [3 2 fib]
|
|
|
|
|
|
.. code:: ipython2
|
|
|
|
define('PE2.2 == x x x [popop] dipd')
|
|
|
|
.. code:: ipython2
|
|
|
|
J('[1 0 fib] 10 [PE2.2] times')
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
2 8 34 144 610 2584 10946 46368 196418 832040 [1346269 832040 fib]
|
|
|
|
|
|
Replace ``x`` with our new driver function ``PE2.2`` and start our
|
|
``fib`` generator at ``1 0``.
|
|
|
|
.. code:: ipython2
|
|
|
|
J('0 [1 0 fib] PE2.2 [pop >4M] [popop] [[PE2.1] dip PE2.2] primrec')
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
4613732
|
|
|
|
|
|
How to compile these?
|
|
=====================
|
|
|
|
You would probably start with a special version of ``G``, and perhaps
|
|
modifications to the default ``x``?
|
|
|
|
An Interesting Variation
|
|
========================
|
|
|
|
.. code:: ipython2
|
|
|
|
define('codireco == cons dip rest cons')
|
|
|
|
.. code:: ipython2
|
|
|
|
V('[0 [dup ++] codireco] x')
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
. [0 [dup ++] codireco] x
|
|
[0 [dup ++] codireco] . x
|
|
[0 [dup ++] codireco] . 0 [dup ++] codireco
|
|
[0 [dup ++] codireco] 0 . [dup ++] codireco
|
|
[0 [dup ++] codireco] 0 [dup ++] . codireco
|
|
[0 [dup ++] codireco] 0 [dup ++] . cons dip rest cons
|
|
[0 [dup ++] codireco] [0 dup ++] . dip rest cons
|
|
. 0 dup ++ [0 [dup ++] codireco] rest cons
|
|
0 . dup ++ [0 [dup ++] codireco] rest cons
|
|
0 0 . ++ [0 [dup ++] codireco] rest cons
|
|
0 1 . [0 [dup ++] codireco] rest cons
|
|
0 1 [0 [dup ++] codireco] . rest cons
|
|
0 1 [[dup ++] codireco] . cons
|
|
0 [1 [dup ++] codireco] .
|
|
|
|
|
|
.. code:: ipython2
|
|
|
|
define('G == [codireco] cons cons')
|
|
|
|
.. code:: ipython2
|
|
|
|
J('230 [dup ++] G 5 [x] times pop')
|
|
|
|
|
|
.. parsed-literal::
|
|
|
|
230 231 232 233 234
|
|
|