Rebuild docs

This commit is contained in:
Simon Forman
2020-05-17 16:40:58 -07:00
parent ef6411205d
commit 56da4690d0
84 changed files with 7456 additions and 7972 deletions
@@ -1,69 +1,69 @@
∂RE
===
Brzozowski's Derivatives of Regular Expressions
Brzozowskis Derivatives of Regular Expressions
-----------------------------------------------
Legend:
::
∧ intersection
union
∘ concatenation (see below)
¬ complement
ϕ empty set (aka ∅)
λ singleton set containing just the empty string
I set of all letters in alphabet
∧ intersection
union
∘ concatenation (see below)
¬ complement
ϕ empty set (aka ∅)
λ singleton set containing just the empty string
I set of all letters in alphabet
Derivative of a set ``R`` of strings and a string ``a``:
::
∂a(R)
∂a(R)
∂a(a) → λ
∂a(λ) → ϕ
∂a(ϕ) → ϕ
∂a(¬a) → ϕ
∂a(R*) → ∂a(R)∘R*
∂a(¬R) → ¬∂a(R)
∂a(R∘S) → ∂a(R)∘S δ(R)∘∂a(S)
∂a(R ∧ S) → ∂a(R) ∧ ∂a(S)
∂a(R S) → ∂a(R) ∂a(S)
∂a(a) → λ
∂a(λ) → ϕ
∂a(ϕ) → ϕ
∂a(¬a) → ϕ
∂a(R*) → ∂a(R)∘R*
∂a(¬R) → ¬∂a(R)
∂a(R∘S) → ∂a(R)∘S δ(R)∘∂a(S)
∂a(R ∧ S) → ∂a(R) ∧ ∂a(S)
∂a(R S) → ∂a(R) ∂a(S)
∂ab(R) = ∂b(∂a(R))
∂ab(R) = ∂b(∂a(R))
Auxiliary predicate function ``δ`` (I call it ``nully``) returns either
``λ`` if ``λ ⊆ R`` or ``ϕ`` otherwise:
::
δ(a) → ϕ
δ(λ) → λ
δ(ϕ) → ϕ
δ(R*) → λ
δ(¬R) δ(R)≟ϕ → λ
δ(¬R) δ(R)≟λ → ϕ
δ(R∘S) → δ(R) ∧ δ(S)
δ(R ∧ S) → δ(R) ∧ δ(S)
δ(R S) → δ(R) δ(S)
δ(a) → ϕ
δ(λ) → λ
δ(ϕ) → ϕ
δ(R*) → λ
δ(¬R) δ(R)≟ϕ → λ
δ(¬R) δ(R)≟λ → ϕ
δ(R∘S) → δ(R) ∧ δ(S)
δ(R ∧ S) → δ(R) ∧ δ(S)
δ(R S) → δ(R) δ(S)
Some rules we will use later for "compaction":
Some rules we will use later for compaction:
::
R ∧ ϕ = ϕ ∧ R = ϕ
R ∧ ϕ = ϕ ∧ R = ϕ
R ∧ I = I ∧ R = R
R ∧ I = I ∧ R = R
R ϕ = ϕ R = R
R ϕ = ϕ R = R
R I = I R = I
R I = I R = I
R∘ϕ = ϕ∘R = ϕ
R∘ϕ = ϕ∘R = ϕ
R∘λ = λ∘R = R
R∘λ = λ∘R = R
Concatination of sets: for two sets A and B the set A∘B is defined as:
@@ -71,7 +71,7 @@ Concatination of sets: for two sets A and B the set A∘B is defined as:
E.g.:
{'a', 'b'}∘{'c', 'd'} → {'ac', 'ad', 'bc', 'bd'}
{a, b}∘{c, d} → {ac, ad, bc, bd}
Implementation
--------------
@@ -94,11 +94,11 @@ The empty set and the set of just the empty string.
Two-letter Alphabet
~~~~~~~~~~~~~~~~~~~
I'm only going to use two symbols (at first) becaase this is enough to
Im only going to use two symbols (at first) becaase this is enough to
illustrate the algorithm and because you can represent any other
alphabet with two symbols (if you had to.)
I chose the names ``O`` and ``l`` (uppercase "o" and lowercase "L") to
I chose the names ``O`` and ``l`` (uppercase “o” and lowercase “L”) to
look like ``0`` and ``1`` (zero and one) respectively.
.. code:: ipython2
@@ -108,18 +108,18 @@ look like ``0`` and ``1`` (zero and one) respectively.
Representing Regular Expressions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To represent REs in Python I'm going to use tagged tuples. A *regular
To represent REs in Python Im going to use tagged tuples. A *regular
expression* is one of:
::
O
l
(KSTAR, R)
(NOT, R)
(AND, R, S)
(CONS, R, S)
(OR, R, S)
O
l
(KSTAR, R)
(NOT, R)
(AND, R, S)
(CONS, R, S)
(OR, R, S)
Where ``R`` and ``S`` stand for *regular expressions*.
@@ -169,11 +169,11 @@ String Representation of RE Datastructures
``I``
~~~~~
Match anything. Often spelled "."
Match anything. Often spelled “.”
::
I = (0|1)*
I = (0|1)*
.. code:: ipython2
@@ -196,8 +196,8 @@ The example expression from Brzozowski:
::
(.111.) & (.01 + 11*)'
a & (b + c)'
(.111.) & (.01 + 11*)'
a & (b + c)'
Note that it contains one of everything.
@@ -221,7 +221,7 @@ Note that it contains one of everything.
``nully()``
~~~~~~~~~~~
Let's get that auxiliary predicate function ``δ`` out of the way.
Lets get that auxiliary predicate function ``δ`` out of the way.
.. code:: ipython2
@@ -256,10 +256,10 @@ Let's get that auxiliary predicate function ``δ`` out of the way.
r, s = nully(R[1]), nully(R[2])
return r & s if tag in {AND, CONS} else r | s
No "Compaction"
No Compaction
~~~~~~~~~~~~~~~
This is the straightforward version with no "compaction". It works fine,
This is the straightforward version with no compaction. It works fine,
but does waaaay too much work because the expressions grow each
derivation.
@@ -359,7 +359,7 @@ are *pure* so this is fine.
result = self.mem[key] = self.f(key)
return result
With "Compaction"
With Compaction
~~~~~~~~~~~~~~~~~
This version uses the rules above to perform compaction. It keeps the
@@ -409,8 +409,8 @@ expressions from growing too large.
return derv
Let's try it out...
-------------------
Lets try it out
-----------------
(FIXME: redo.)
@@ -460,27 +460,27 @@ Should match:
::
(.111.) & ((.01 | 11*)')
(.111.) & ((.01 | 11*)')
92 / 122
92 / 122
92 / 122
92 / 122
(.01 )'
(.01 | 1 )'
(.01 | ^ )'
(.01 | 1*)'
(.111.) & ((.01 | 1 )')
(.111. | 11.) & ((.01 | ^ )')
(.111. | 11.) & ((.01 | 1*)')
(.111. | 11. | 1.) & ((.01 )')
(.111. | 11. | 1.) & ((.01 | 1*)')
(.01 )'
(.01 | 1 )'
(.01 | ^ )'
(.01 | 1*)'
(.111.) & ((.01 | 1 )')
(.111. | 11.) & ((.01 | ^ )')
(.111. | 11.) & ((.01 | 1*)')
(.111. | 11. | 1.) & ((.01 )')
(.111. | 11. | 1.) & ((.01 | 1*)')
Larger Alphabets
----------------
We could parse larger alphabets by defining patterns for e.g. each byte
We could parse larger alphabets by defining patterns for e.g. each byte
of the ASCII code. Or we can generalize this code. If you study the code
above you'll see that we never use the "set-ness" of the symbols ``O``
above youll see that we never use the set-ness of the symbols ``O``
and ``l``. The only time Python set operators (``&`` and ``|``) appear
is in the ``nully()`` function, and there they operate on (recursively
computed) outputs of that function, never ``O`` and ``l``.
@@ -489,33 +489,33 @@ What if we try:
::
(OR, O, l)
(OR, O, l)
∂1((OR, O, l))
∂a(R S) → ∂a(R) ∂a(S)
∂1(O) ∂1(l)
∂a(¬a) → ϕ
ϕ ∂1(l)
∂a(a) → λ
ϕ λ
ϕ R = R
λ
∂1((OR, O, l))
∂a(R S) → ∂a(R) ∂a(S)
∂1(O) ∂1(l)
∂a(¬a) → ϕ
ϕ ∂1(l)
∂a(a) → λ
ϕ λ
ϕ R = R
λ
And compare it to:
::
{'0', '1')
{'0', '1')
∂1({'0', '1'))
∂a(R S) → ∂a(R) ∂a(S)
∂1({'0')) ∂1({'1'))
∂a(¬a) → ϕ
ϕ ∂1({'1'))
∂a(a) → λ
ϕ λ
ϕ R = R
λ
∂1({'0', '1'))
∂a(R S) → ∂a(R) ∂a(S)
∂1({'0')) ∂1({'1'))
∂a(¬a) → ϕ
ϕ ∂1({'1'))
∂a(a) → λ
ϕ λ
ϕ R = R
λ
This suggests that we should be able to alter the functions above to
detect sets and deal with them appropriately. Exercise for the Reader
@@ -529,9 +529,9 @@ machine transition table.
::
.111. & (.01 + 11*)'
.111. & (.01 + 11*)'
Says, "Three or more 1's and not ending in 01 nor composed of all 1's."
Says, Three or more 1s and not ending in 01 nor composed of all 1s.
.. figure:: attachment:omg.svg
:alt: omg.svg
@@ -540,32 +540,32 @@ Says, "Three or more 1's and not ending in 01 nor composed of all 1's."
Start at ``a`` and follow the transition arrows according to their
labels. Accepting states have a double outline. (Graphic generated with
`Dot from Graphviz <http://www.graphviz.org/>`__.) You'll see that only
`Dot from Graphviz <http://www.graphviz.org/>`__.) Youll see that only
paths that lead to one of the accepting states will match the regular
expression. All other paths will terminate at one of the non-accepting
states.
There's a happy path to ``g`` along 111:
Theres a happy path to ``g`` along 111:
::
a→c→e→g
a→c→e→g
After you reach ``g`` you're stuck there eating 1's until you see a 0,
which takes you to the ``i→j→i|i→j→h→i`` "trap". You can't reach any
After you reach ``g`` youre stuck there eating 1s until you see a 0,
which takes you to the ``i→j→i|i→j→h→i`` trap. You cant reach any
other states from those two loops.
If you see a 0 before you see 111 you will reach ``b``, which forms
another "trap" with ``d`` and ``f``. The only way out is another happy
another trap with ``d`` and ``f``. The only way out is another happy
path along 111 to ``h``:
::
b→d→f→h
b→d→f→h
Once you have reached ``h`` you can see as many 1's or as many 0' in a
row and still be either still at ``h`` (for 1's) or move to ``i`` (for
0's). If you find yourself at ``i`` you can see as many 0's, or
Once you have reached ``h`` you can see as many 1s or as many 0 in a
row and still be either still at ``h`` (for 1s) or move to ``i`` (for
0s). If you find yourself at ``i`` you can see as many 0s, or
repetitions of 10, as there are, but if you see just a 1 you move to
``j``.
@@ -575,14 +575,14 @@ RE to FSM
So how do we get the state machine from the regular expression?
It turns out that each RE is effectively a state, and each arrow points
to the derivative RE in respect to the arrow's symbol.
to the derivative RE in respect to the arrows symbol.
If we label the initial RE ``a``, we can say:
::
a --0--> ∂0(a)
a --1--> ∂1(a)
a --0--> ∂0(a)
a --1--> ∂1(a)
And so on, each new unique RE is a new state in the FSM table.
@@ -590,18 +590,18 @@ Here are the derived REs at each state:
::
a = (.111.) & ((.01 | 11*)')
b = (.111.) & ((.01 | 1)')
c = (.111. | 11.) & ((.01 | 1*)')
d = (.111. | 11.) & ((.01 | ^)')
e = (.111. | 11. | 1.) & ((.01 | 1*)')
f = (.111. | 11. | 1.) & ((.01)')
g = (.01 | 1*)'
h = (.01)'
i = (.01 | 1)'
j = (.01 | ^)'
a = (.111.) & ((.01 | 11*)')
b = (.111.) & ((.01 | 1)')
c = (.111. | 11.) & ((.01 | 1*)')
d = (.111. | 11.) & ((.01 | ^)')
e = (.111. | 11. | 1.) & ((.01 | 1*)')
f = (.111. | 11. | 1.) & ((.01)')
g = (.01 | 1*)'
h = (.01)'
i = (.01 | 1)'
j = (.01 | ^)'
You can see the one-way nature of the ``g`` state and the ``hij`` "trap"
You can see the one-way nature of the ``g`` state and the ``hij`` trap
in the way that the ``.111.`` on the left-hand side of the ``&``
disappears once it has been matched.
@@ -764,16 +764,16 @@ Drive a FSM
There are *lots* of FSM libraries already. Once you have the state
transition table they should all be straightforward to use. State
Machine code is very simple. Just for fun, here is an implementation in
Python that imitates what "compiled" FSM code might look like in an
"unrolled" form. Most FSM code uses a little driver loop and a table
Python that imitates what compiled FSM code might look like in an
unrolled form. Most FSM code uses a little driver loop and a table
datastructure, the code below instead acts like JMP instructions
("jump", or GOTO in higher-level-but-still-low-level languages) to
(jump, or GOTO in higher-level-but-still-low-level languages) to
hard-code the information in the table into a little patch of branches.
Trampoline Function
^^^^^^^^^^^^^^^^^^^
Python has no GOTO statement but we can fake it with a "trampoline"
Python has no GOTO statement but we can fake it with a trampoline
function.
.. code:: ipython2
@@ -790,8 +790,8 @@ function.
Stream Functions
^^^^^^^^^^^^^^^^
Little helpers to process the iterator of our data (a "stream" of "1"
and "0" characters, not bits.)
Little helpers to process the iterator of our data (a stream of “1”
and “0” characters, not bits.)
.. code:: ipython2
@@ -831,7 +831,7 @@ labels.)
Note that the implementations of ``h`` and ``g`` are identical ergo
``h = g`` and we could eliminate one in the code but ``h`` is an
accepting state and ``g`` isn't.
accepting state and ``g`` isnt.
.. code:: ipython2
@@ -885,7 +885,7 @@ Reversing the Derivatives to Generate Matching Strings
------------------------------------------------------
(UNFINISHED) Brzozowski also shewed how to go from the state machine to
strings and expressions...
strings and expressions
Each of these states is just a name for a Brzozowskian RE, and so, other
than the initial state ``a``, they can can be described in terms of the
@@ -893,54 +893,54 @@ derivative-with-respect-to-N of some other state/RE:
::
c = d1(a)
b = d0(a)
b = d0(c)
...
i = d0(j)
j = d1(i)
c = d1(a)
b = d0(a)
b = d0(c)
...
i = d0(j)
j = d1(i)
Consider:
::
c = d1(a)
b = d0(c)
c = d1(a)
b = d0(c)
Substituting:
::
b = d0(d1(a))
b = d0(d1(a))
Unwrapping:
::
b = d10(a)
b = d10(a)
'''
’’’
::
j = d1(d0(j))
j = d1(d0(j))
Unwrapping:
::
j = d1(d0(j)) = d01(j)
j = d1(d0(j)) = d01(j)
We have a loop or "fixed point".
We have a loop or fixed point.
::
j = d01(j) = d0101(j) = d010101(j) = ...
j = d01(j) = d0101(j) = d010101(j) = ...
hmm...
hmm
::
j = (01)*
j = (01)*
@@ -11,51 +11,51 @@ Consider the ``x`` combinator:
::
x == dup i
x == dup i
We can apply it to a quoted program consisting of some value ``a`` and
some function ``B``:
::
[a B] x
[a B] a 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
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]
[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]
b [a B] rest cons
b [B] cons
[b B]
Altogether, this is the definition of ``B``:
::
B == swap [C] dip rest cons
B == swap [C] dip rest cons
We can make a generator for the Natural numbers (0, 1, 2, ...) by using
We can make a generator for the Natural numbers (0, 1, 2, ) by using
``0`` for ``a`` and ``[dup ++]`` for ``[C]``:
::
[0 swap [dup ++] dip rest cons]
[0 swap [dup ++] dip rest cons]
Let's try it:
Lets try it:
.. code:: ipython2
@@ -128,32 +128,32 @@ our quoted program:
::
a [C] G
-------------------------
[a swap [C] direco]
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]
[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
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:
Lets try it out:
.. code:: ipython2
@@ -208,20 +208,20 @@ Generating Multiples of Three and Five
--------------------------------------
Look at the treatment of the Project Euler Problem One in the
"Developing a Program" notebook and you'll see that we might be
Developing a Program notebook and youll see that we might be
interested in generating an endless cycle of:
::
3 2 1 3 1 2 3
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
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.
@@ -250,7 +250,7 @@ int right two bits.
3 3702 .
If we plug ``14811`` and ``[PE1.1]`` into our generator form...
If we plug ``14811`` and ``[PE1.1]`` into our generator form
.. code:: ipython2
@@ -262,8 +262,7 @@ If we plug ``14811`` and ``[PE1.1]`` into our generator form...
[14811 swap [PE1.1] direco]
...we get a generator that works for seven cycles before it reaches
zero:
we get a generator that works for seven cycles before it reaches zero:
.. code:: ipython2
@@ -306,15 +305,15 @@ if so.
(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
thats a little beyond the scope of this article. This solution does
extra work, but not much, and were 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
five less than 1000. Its worked out that we need to use all seven
numbers sixty-six times and then four more.
.. code:: ipython2
@@ -375,76 +374,76 @@ Consider:
::
[b a F] x
[b a F] b a F
[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
[b a F] b a +
[b a F] b+a
From here we want to arrive at:
::
b [b+a b F]
b [b+a b F]
Let's start with ``swons``:
Lets start with ``swons``:
::
[b a F] b+a swons
[b+a b a F]
[b a F] b+a swons
[b+a b a F]
Considering this quote as a stack:
::
F a b b+a
F a b b+a
We want to get it to:
::
F b b+a b
F b b+a b
So:
::
F a b b+a popdd over
F b b+a b
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]
[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]
[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]
[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]
F == + [popdd over] cons infra uncons
fib_gen == [1 1 F]
.. code:: ipython2
@@ -467,8 +466,8 @@ Putting it all together:
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.
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
@@ -479,13 +478,13 @@ function that adds a term in the sequence to a sum if it is even, and
define('PE2.1 == dup 2 % [+] [pop] branch')
And a predicate function that detects when the terms in the series
"exceed four million".
exceed four million.
.. code:: ipython2
define('>4M == 4000000 >')
Now it's straightforward to define ``PE2`` as a recursive function that
Now its 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.
@@ -503,18 +502,18 @@ and sums the even ones.
4613732
Here's the collected program definitions:
Heres the collected program definitions:
::
fib == + swons [popdd over] infra uncons
fib_gen == [1 1 fib]
fib == + swons [popdd over] infra uncons
fib_gen == [1 1 fib]
even == dup 2 %
>4M == 4000000 >
even == dup 2 %
>4M == 4000000 >
PE2.1 == even [+] [pop] branch
PE2 == 0 fib_gen x [pop >4M] [popop] [[PE2.1] dip x] primrec
PE2.1 == even [+] [pop] branch
PE2 == 0 fib_gen x [pop >4M] [popop] [[PE2.1] dip x] primrec
Even-valued Fibonacci Terms
~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -523,16 +522,16 @@ Using ``o`` for odd and ``e`` for even:
::
o + o = e
e + e = e
o + e = o
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 . . .
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.
+11 -4
View File
@@ -22,8 +22,8 @@ that you start by running the package:
::
$ python -m joy
Joypy - Copyright © 2017 Simon Forman
$ python3 -m joy
Thun - Copyright © 2017 Simon Forman
This program comes with ABSOLUTELY NO WARRANTY; for details type "warranty".
This is free software, and you are welcome to redistribute it
under certain conditions; type "sharing" for details.
@@ -31,7 +31,7 @@ that you start by running the package:
docs for a word.
<-top
<-top
joy? _
@@ -40,7 +40,14 @@ You can enter Joy notation at the prompt and a :doc:`trace of evaluation <../pre
be printed followed by the stack and prompt again::
joy? 23 sqr 18 +
. 23 sqr 18 +
547 <-top
joy?
There is a `trace` combinator::
joy? 23 [sqr 18 +] trace
23 . sqr 18 +
23 . dup mul 18 +
23 23 . mul 18 +
+57 -57
View File
@@ -1,10 +1,10 @@
`Newton's method <https://en.wikipedia.org/wiki/Newton%27s_method>`__
`Newtons method <https://en.wikipedia.org/wiki/Newton%27s_method>`__
=====================================================================
Let's use the Newton-Raphson method for finding the root of an equation
Lets use the Newton-Raphson method for finding the root of an equation
to write a function that can compute the square root of a number.
Cf. `"Why Functional Programming Matters" by John
Cf. `Why Functional Programming Matters by John
Hughes <https://www.cs.kent.ac.uk/people/staff/dat/miranda/whyfp90.pdf>`__
.. code:: ipython2
@@ -20,9 +20,9 @@ computes the next approximation:
::
a F
---------
a'
a F
---------
a'
A Function to Compute the Next Approximation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -34,17 +34,17 @@ square root:
::
a n over / + 2 /
a n a / + 2 /
a n/a + 2 /
a+n/a 2 /
(a+n/a)/2
a n over / + 2 /
a n a / + 2 /
a n/a + 2 /
a+n/a 2 /
(a+n/a)/2
The function we want has the argument ``n`` in it:
::
F == n over / + 2 /
F == n over / + 2 /
Make it into a Generator
~~~~~~~~~~~~~~~~~~~~~~~~
@@ -53,27 +53,27 @@ Our generator would be created by:
::
a [dup F] make_generator
a [dup F] make_generator
With n as part of the function F, but n is the input to the sqrt
function were writing. If we let 1 be the initial approximation:
::
1 n 1 / + 2 /
1 n/1 + 2 /
1 n + 2 /
n+1 2 /
(n+1)/2
1 n 1 / + 2 /
1 n/1 + 2 /
1 n + 2 /
n+1 2 /
(n+1)/2
The generator can be written as:
::
23 1 swap [over / + 2 /] cons [dup] swoncat make_generator
1 23 [over / + 2 /] cons [dup] swoncat make_generator
1 [23 over / + 2 /] [dup] swoncat make_generator
1 [dup 23 over / + 2 /] make_generator
23 1 swap [over / + 2 /] cons [dup] swoncat make_generator
1 23 [over / + 2 /] cons [dup] swoncat make_generator
1 [23 over / + 2 /] [dup] swoncat make_generator
1 [dup 23 over / + 2 /] make_generator
.. code:: ipython2
@@ -89,8 +89,8 @@ The generator can be written as:
[1 [dup 23 over / + 2 /] codireco]
Let's drive the generator a few time (with the ``x`` combinator) and
square the approximation to see how well it works...
Lets drive the generator a few time (with the ``x`` combinator) and
square the approximation to see how well it works
.. code:: ipython2
@@ -105,42 +105,42 @@ square the approximation to see how well it works...
Finding Consecutive Approximations within a Tolerance
-----------------------------------------------------
From `"Why Functional Programming Matters" by John
From `Why Functional Programming Matters by John
Hughes <https://www.cs.kent.ac.uk/people/staff/dat/miranda/whyfp90.pdf>`__:
The remainder of a square root finder is a function *within*, which
takes a tolerance and a list of approximations and looks down the
list for two successive approximations that differ by no more than
the given tolerance.
The remainder of a square root finder is a function *within*, which
takes a tolerance and a list of approximations and looks down the
list for two successive approximations that differ by no more than
the given tolerance.
(And note that by “list” he means a lazily-evaluated list.)
Using the *output* ``[a G]`` of the above generator for square root
approximations, and further assuming that the first term a has been
generated already and epsilon ε is handy on the stack...
generated already and epsilon ε is handy on the stack
::
a [b G] ε within
---------------------- a b - abs ε <=
b
a [b G] ε within
---------------------- a b - abs ε <=
b
a [b G] ε within
---------------------- a b - abs ε >
b [c G] ε within
a [b G] ε within
---------------------- a b - abs ε >
b [c G] ε within
Predicate
~~~~~~~~~
::
a [b G] ε [first - abs] dip <=
a [b G] first - abs ε <=
a b - abs ε <=
a-b abs ε <=
abs(a-b) ε <=
(abs(a-b)<=ε)
a [b G] ε [first - abs] dip <=
a [b G] first - abs ε <=
a b - abs ε <=
a-b abs ε <=
abs(a-b) ε <=
(abs(a-b)<=ε)
.. code:: ipython2
@@ -151,10 +151,10 @@ Base-Case
::
a [b G] ε roll< popop first
[b G] ε a popop first
[b G] first
b
a [b G] ε roll< popop first
[b G] ε a popop first
[b G] first
b
.. code:: ipython2
@@ -165,7 +165,7 @@ Recur
::
a [b G] ε R0 [within] R1
a [b G] ε R0 [within] R1
1. Discard a.
2. Use ``x`` combinator to generate next term from ``G``.
@@ -175,14 +175,14 @@ Pretty straightforward:
::
a [b G] ε R0 [within] R1
a [b G] ε [popd x] dip [within] i
a [b G] popd x ε [within] i
[b G] x ε [within] i
b [c G] ε [within] i
b [c G] ε within
a [b G] ε R0 [within] R1
a [b G] ε [popd x] dip [within] i
a [b G] popd x ε [within] i
[b G] x ε [within] i
b [c G] ε [within] i
b [c G] ε within
b [c G] ε within
b [c G] ε within
.. code:: ipython2
@@ -196,15 +196,15 @@ The recursive function we have defined so far needs a slight preamble:
::
[a G] x ε ...
a [b G] ε ...
[a G] x ε ...
a [b G] ε ...
.. code:: ipython2
define('within == x 0.000000001 [_within_P] [_within_B] [_within_R] primrec')
define('sqrt == gsra within')
Try it out...
Try it out
.. code:: ipython2
File diff suppressed because it is too large Load Diff
+16 -16
View File
@@ -10,9 +10,9 @@ Cf.
::
-b ± sqrt(b^2 - 4 * a * c)
--------------------------------
2 * a
-b ± sqrt(b^2 - 4 * a * c)
--------------------------------
2 * a
:math:`\frac{-b \pm \sqrt{b^2 - 4ac}}{2a}`
@@ -28,21 +28,21 @@ a definition without them.
::
b neg
b neg
``sqrt(b^2 - 4 * a * c)``
~~~~~~~~~~~~~~~~~~~~~~~~~
::
b sqr 4 a c * * - sqrt
b sqr 4 a c * * - sqrt
``/2a``
~~~~~~~
::
a 2 * /
a 2 * /
``±``
~~~~~
@@ -52,14 +52,14 @@ replaces them with their sum and difference.
::
pm == [+] [-] cleave popdd
pm == [+] [-] cleave popdd
Putting Them Together
~~~~~~~~~~~~~~~~~~~~~
::
b neg b sqr 4 a c * * - sqrt pm a 2 * [/] cons app2
b neg b sqr 4 a c * * - sqrt pm a 2 * [/] cons app2
We use ``app2`` to compute both roots by using a quoted program
``[2a /]`` built with ``cons``.
@@ -72,20 +72,20 @@ the variables:
::
b neg b sqr 4 a c * * - sqrt pm a 2 * [/] cons app2
b [neg] dupdip sqr 4 a c * * - sqrt pm a 2 * [/] cons app2
b a c [[neg] dupdip sqr 4] dipd * * - sqrt pm a 2 * [/] cons app2
b a c a [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2
b a c over [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2
b neg b sqr 4 a c * * - sqrt pm a 2 * [/] cons app2
b [neg] dupdip sqr 4 a c * * - sqrt pm a 2 * [/] cons app2
b a c [[neg] dupdip sqr 4] dipd * * - sqrt pm a 2 * [/] cons app2
b a c a [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2
b a c over [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2
The three arguments are to the left, so we can "chop off" everything to
the right and say it's the definition of the ``quadratic`` function:
The three arguments are to the left, so we can chop off everything to
the right and say its the definition of the ``quadratic`` function:
.. code:: ipython2
define('quadratic == over [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2')
Let's try it out:
Lets try it out:
.. code:: ipython2
@@ -10,44 +10,43 @@ several generic specializations.
::
[if] [then] [rec1] [rec2] genrec
---------------------------------------------------------------------
[if] [then] [rec1 [[if] [then] [rec1] [rec2] genrec] rec2] ifte
[if] [then] [rec1] [rec2] genrec
---------------------------------------------------------------------
[if] [then] [rec1 [[if] [then] [rec1] [rec2] genrec] rec2] ifte
From "Recursion Theory and Joy" (j05cmp.html) by Manfred von Thun:
From Recursion Theory and Joy (j05cmp.html) by Manfred von Thun:
"The genrec combinator takes four program parameters in addition to
whatever data parameters it needs. Fourth from the top is an
if-part, followed by a then-part. If the if-part yields true, then
the then-part is executed and the combinator terminates. The other
two parameters are the rec1-part and the rec2-part. If the if-part
yields false, the rec1-part is executed. Following that the four
program parameters and the combinator are again pushed onto the
stack bundled up in a quoted form. Then the rec2-part is executed,
where it will find the bundled form. Typically it will then execute
the bundled form, either with i or with app2, or some other
combinator."
The genrec combinator takes four program parameters in addition to
whatever data parameters it needs. Fourth from the top is an if-part,
followed by a then-part. If the if-part yields true, then the
then-part is executed and the combinator terminates. The other two
parameters are the rec1-part and the rec2-part. If the if-part yields
false, the rec1-part is executed. Following that the four program
parameters and the combinator are again pushed onto the stack bundled
up in a quoted form. Then the rec2-part is executed, where it will
find the bundled form. Typically it will then execute the bundled
form, either with i or with app2, or some other combinator.”
Designing Recursive Functions
-----------------------------
The way to design one of these is to fix your base case and test and
then treat ``R1`` and ``R2`` as an else-part "sandwiching" a quotation
then treat ``R1`` and ``R2`` as an else-part sandwiching a quotation
of the whole function.
For example, given a (general recursive) function ``F``:
::
F == [I] [T] [R1] [R2] genrec
== [I] [T] [R1 [F] R2] ifte
F == [I] [T] [R1] [R2] genrec
== [I] [T] [R1 [F] R2] ifte
If the ``[I]`` predicate is false you must derive ``R1`` and ``R2``
from:
::
... R1 [F] R2
... R1 [F] R2
Set the stack arguments in front and figure out what ``R1`` and ``R2``
have to do to apply the quoted ``[F]`` in the proper way.
@@ -59,9 +58,9 @@ Primitive recursive functions are those where ``R2 == i``.
::
P == [I] [T] [R] primrec
== [I] [T] [R [P] i] ifte
== [I] [T] [R P] ifte
P == [I] [T] [R] primrec
== [I] [T] [R [P] i] ifte
== [I] [T] [R P] ifte
`Hylomorphism <https://en.wikipedia.org/wiki/Hylomorphism_%28computer_science%29>`__
------------------------------------------------------------------------------------
@@ -75,8 +74,8 @@ is a recursive function ``H :: A -> C`` that converts a value of type
- A combiner ``F :: (B, C) -> C``
- A predicate ``P :: A -> Bool`` to detect the base case
- A base case value ``c :: C``
- Recursive calls (zero or more); it has a "call stack in the form of a
cons list".
- Recursive calls (zero or more); it has a call stack in the form of a
cons list.
It may be helpful to see this function implemented in imperative Python
code.
@@ -96,12 +95,12 @@ code.
return H
Cf. `"Bananas, Lenses, & Barbed
Wire" <http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.41.125>`__
Cf. `Bananas, Lenses, & Barbed
Wire <http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.41.125>`__
Note that during evaluation of ``H()`` the intermediate ``b`` values are
stored in the Python call stack. This is what is meant by "call stack in
the form of a cons list".
stored in the Python call stack. This is what is meant by call stack in
the form of a cons list.
Hylomorphism in Joy
-------------------
@@ -111,7 +110,7 @@ hylomorphism combinator ``H`` from constituent parts.
::
H == [P] c [G] [F] hylomorphism
H == [P] c [G] [F] hylomorphism
The function ``H`` is recursive, so we start with ``ifte`` and set the
else-part to some function ``J`` that will contain a quoted copy of
@@ -120,37 +119,37 @@ with the base case value ``c``.)
::
H == [P] [pop c] [J] ifte
H == [P] [pop c] [J] ifte
The else-part ``J`` gets just the argument ``a`` on the stack.
::
a J
a G The first thing to do is use the generator G
aa b which produces b and a new aa
aa b [H] dip we recur with H on the new aa
aa H b F and run F on the result.
a J
a G The first thing to do is use the generator G
aa b which produces b and a new aa
aa b [H] dip we recur with H on the new aa
aa H b F and run F on the result.
This gives us a definition for ``J``.
::
J == G [H] dip F
J == G [H] dip F
Plug it in and convert to genrec.
::
H == [P] [pop c] [G [H] dip F] ifte
H == [P] [pop c] [G] [dip F] genrec
H == [P] [pop c] [G [H] dip F] ifte
H == [P] [pop c] [G] [dip F] genrec
This is the form of a hylomorphism in Joy, which nicely illustrates that
it is a simple specialization of the general recursion combinator.
::
H == [P] c [G] [F] hylomorphism == [P] [pop c] [G] [dip F] genrec
H == [P] c [G] [F] hylomorphism == [P] [pop c] [G] [dip F] genrec
Derivation of ``hylomorphism`` combinator
-----------------------------------------
@@ -160,9 +159,9 @@ arguments out of the pieces given to the ``hylomorphism`` combinator.
::
[P] c [G] [F] hylomorphism
------------------------------------------
[P] [pop c] [G] [dip F] genrec
[P] c [G] [F] hylomorphism
------------------------------------------
[P] [pop c] [G] [dip F] genrec
Working in reverse:
@@ -174,17 +173,17 @@ So:
::
H == [P] [pop c] [G] [dip F] genrec
[P] [c] [pop] swoncat [G] [F] [dip] swoncat genrec
[P] c unit [pop] swoncat [G] [F] [dip] swoncat genrec
[P] c [G] [F] [unit [pop] swoncat] dipd [dip] swoncat genrec
H == [P] [pop c] [G] [dip F] genrec
[P] [c] [pop] swoncat [G] [F] [dip] swoncat genrec
[P] c unit [pop] swoncat [G] [F] [dip] swoncat genrec
[P] c [G] [F] [unit [pop] swoncat] dipd [dip] swoncat genrec
At this point all of the arguments (givens) to the hylomorphism are to
the left so we have a definition for ``hylomorphism``:
::
hylomorphism == [unit [pop] swoncat] dipd [dip] swoncat genrec
hylomorphism == [unit [pop] swoncat] dipd [dip] swoncat genrec
.. code:: ipython2
@@ -193,7 +192,7 @@ the left so we have a definition for ``hylomorphism``:
Example: Finding `Triangular Numbers <https://en.wikipedia.org/wiki/Triangular_number>`__
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Let's write a function that, given a positive integer, returns the sum
Lets write a function that, given a positive integer, returns the sum
of all positive integers less than that one. (In this case the types
``A``, ``B`` and ``C`` are all ``int``.)
@@ -208,7 +207,7 @@ To sum a range of integers from 0 to *n* - 1:
define('triangular_number == [1 <=] 0 [-- dup] [+] hylomorphism')
Let's try it:
Lets try it:
.. code:: ipython2
@@ -236,30 +235,30 @@ Four Specializations
There are at least four kinds of recursive combinator, depending on two
choices. The first choice is whether the combiner function ``F`` should
be evaluated during the recursion or pushed into the pending expression
to be "collapsed" at the end. The second choice is whether the combiner
to be collapsed at the end. The second choice is whether the combiner
needs to operate on the current value of the datastructure or the
generator's output, in other words, whether ``F`` or ``G`` should run
generators output, in other words, whether ``F`` or ``G`` should run
first in the recursive branch.
::
H1 == [P] [pop c] [G ] [dip F] genrec
H2 == c swap [P] [pop] [G [F] dip ] [i] genrec
H3 == [P] [pop c] [ [G] dupdip ] [dip F] genrec
H4 == c swap [P] [pop] [ [F] dupdip G] [i] genrec
H1 == [P] [pop c] [G ] [dip F] genrec
H2 == c swap [P] [pop] [G [F] dip ] [i] genrec
H3 == [P] [pop c] [ [G] dupdip ] [dip F] genrec
H4 == c swap [P] [pop] [ [F] dupdip G] [i] genrec
The working of the generator function ``G`` differs slightly for each.
Consider the recursive branches:
::
... a G [H1] dip F w/ a G == a b
... a G [H1] dip F w/ a G == a b
... c a G [F] dip H2 a G == b a
... c a G [F] dip H2 a G == b a
... a [G] dupdip [H3] dip F a G == a
... a [G] dupdip [H3] dip F a G == a
... c a [F] dupdip G H4 a G == a
... c a [F] dupdip G H4 a G == a
The following four sections illustrate how these work, omitting the
predicate evaluation.
@@ -269,31 +268,31 @@ predicate evaluation.
::
H1 == [P] [pop c] [G] [dip F] genrec
H1 == [P] [pop c] [G] [dip F] genrec
Iterate n times.
::
... a G [H1] dip F
... a b [H1] dip F
... a H1 b F
... a G [H1] dip F b F
... a″ b [H1] dip F b F
... a″ H1 b F b F
... a″ G [H1] dip F b F b F
... a‴ b″ [H1] dip F b F b F
... a‴ H1 b″ F b F b F
... a‴ pop c b″ F b F b F
... c b″ F b F b F
... d b F b F
... d b F
... d″
... a G [H1] dip F
... a b [H1] dip F
... a H1 b F
... a G [H1] dip F b F
... a″ b [H1] dip F b F
... a″ H1 b F b F
... a″ G [H1] dip F b F b F
... a‴ b″ [H1] dip F b F b F
... a‴ H1 b″ F b F b F
... a‴ pop c b″ F b F b F
... c b″ F b F b F
... d b F b F
... d b F
... d″
This form builds up a pending expression (continuation) that contains
the intermediate results along with the pending combiner functions. When
the base case is reached the last term is replaced by the identity value
``c`` and the continuation "collapses" into the final result using the
``c`` and the continuation collapses into the final result using the
combiner ``F``.
``H2``
@@ -307,53 +306,53 @@ reverse order.
::
H2 == c swap [P] [pop] [G [F] dip] primrec
H2 == c swap [P] [pop] [G [F] dip] primrec
... c a G [F] dip H2
... c b a [F] dip H2
... c b F a H2
... d a H2
... d a G [F] dip H2
... d b a″ [F] dip H2
... d b F a″ H2
... d a″ H2
... d a″ G [F] dip H2
... d b″ a‴ [F] dip H2
... d b″ F a‴ H2
... d″ a‴ H2
... d″ a‴ pop
... d″
... c a G [F] dip H2
... c b a [F] dip H2
... c b F a H2
... d a H2
... d a G [F] dip H2
... d b a″ [F] dip H2
... d b F a″ H2
... d a″ H2
... d a″ G [F] dip H2
... d b″ a‴ [F] dip H2
... d b″ F a‴ H2
... d″ a‴ H2
... d″ a‴ pop
... d″
``H3``
~~~~~~
If you examine the traces above you'll see that the combiner ``F`` only
gets to operate on the results of ``G``, it never "sees" the first value
If you examine the traces above youll see that the combiner ``F`` only
gets to operate on the results of ``G``, it never sees the first value
``a``. If the combiner and the generator both need to work on the
current value then ``dup`` must be used, and the generator must produce
one item instead of two (the b is instead the duplicate of a.)
::
H3 == [P] [pop c] [[G] dupdip] [dip F] genrec
H3 == [P] [pop c] [[G] dupdip] [dip F] genrec
... a [G] dupdip [H3] dip F
... a G a [H3] dip F
... a a [H3] dip F
... a H3 a F
... a [G] dupdip [H3] dip F a F
... a G a [H3] dip F a F
... a″ a [H3] dip F a F
... a″ H3 a F a F
... a″ [G] dupdip [H3] dip F a F a F
... a″ G a″ [H3] dip F a F a F
... a‴ a″ [H3] dip F a F a F
... a‴ H3 a″ F a F a F
... a‴ pop c a″ F a F a F
... c a″ F a F a F
... d a F a F
... d a F
... d″
... a [G] dupdip [H3] dip F
... a G a [H3] dip F
... a a [H3] dip F
... a H3 a F
... a [G] dupdip [H3] dip F a F
... a G a [H3] dip F a F
... a″ a [H3] dip F a F
... a″ H3 a F a F
... a″ [G] dupdip [H3] dip F a F a F
... a″ G a″ [H3] dip F a F a F
... a‴ a″ [H3] dip F a F a F
... a‴ H3 a″ F a F a F
... a‴ pop c a″ F a F a F
... c a″ F a F a F
... d a F a F
... d a F
... d″
``H4``
~~~~~~
@@ -364,22 +363,22 @@ the form:
::
H4 == c swap [P] [pop] [[F] dupdip G] primrec
H4 == c swap [P] [pop] [[F] dupdip G] primrec
... c a [F] dupdip G H4
... c a F a G H4
... d a G H4
... d a H4
... d a [F] dupdip G H4
... d a F a G H4
... d a G H4
... d a″ H4
... d a″ [F] dupdip G H4
... d a″ F a″ G H4
... d″ a″ G H4
... d″ a‴ H4
... d″ a‴ pop
... d″
... c a [F] dupdip G H4
... c a F a G H4
... d a G H4
... d a H4
... d a [F] dupdip G H4
... d a F a G H4
... d a G H4
... d a″ H4
... d a″ [F] dupdip G H4
... d a″ F a″ G H4
... d″ a″ G H4
... d″ a‴ H4
... d″ a‴ pop
... d″
Anamorphism
-----------
@@ -390,13 +389,10 @@ values.
::
A == [P] [] [G] [swons] hylomorphism
A == [P] [] [G] [swons] hylomorphism
``range`` et. al.
~~~~~~~~~~~~~~~~~
An example of an anamorphism is the ``range`` function which generates
the list of integers from 0 to *n* - 1 given *n*.
``range`` et. al. An example of an anamorphism is the ``range`` function which generates the list of integers from 0 to *n* - 1 given *n*.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Each of the above variations can be used to make four slightly different
``range`` functions.
@@ -406,8 +402,8 @@ Each of the above variations can be used to make four slightly different
::
H1 == [P] [pop c] [G] [dip F] genrec
== [0 <=] [pop []] [-- dup] [dip swons] genrec
H1 == [P] [pop c] [G] [dip F] genrec
== [0 <=] [pop []] [-- dup] [dip swons] genrec
.. code:: ipython2
@@ -428,8 +424,8 @@ Each of the above variations can be used to make four slightly different
::
H2 == c swap [P] [pop] [G [F] dip] primrec
== [] swap [0 <=] [pop] [-- dup [swons] dip] primrec
H2 == c swap [P] [pop] [G [F] dip] primrec
== [] swap [0 <=] [pop] [-- dup [swons] dip] primrec
.. code:: ipython2
@@ -450,8 +446,8 @@ Each of the above variations can be used to make four slightly different
::
H3 == [P] [pop c] [[G] dupdip] [dip F] genrec
== [0 <=] [pop []] [[--] dupdip] [dip swons] genrec
H3 == [P] [pop c] [[G] dupdip] [dip F] genrec
== [0 <=] [pop []] [[--] dupdip] [dip swons] genrec
.. code:: ipython2
@@ -472,8 +468,8 @@ Each of the above variations can be used to make four slightly different
::
H4 == c swap [P] [pop] [[F] dupdip G ] primrec
== [] swap [0 <=] [pop] [[swons] dupdip --] primrec
H4 == c swap [P] [pop] [[F] dupdip G ] primrec
== [] swap [0 <=] [pop] [[swons] dupdip --] primrec
.. code:: ipython2
@@ -503,7 +499,7 @@ and makes some new value.
::
C == [not] c [uncons swap] [F] hylomorphism
C == [not] c [uncons swap] [F] hylomorphism
.. code:: ipython2
@@ -513,7 +509,7 @@ An example of a catamorphism is the sum function.
::
sum == [not] 0 [swuncons] [+] hylomorphism
sum == [not] 0 [swuncons] [+] hylomorphism
.. code:: ipython2
@@ -585,16 +581,16 @@ For the Factorial function:
::
H4 == c swap [P] [pop] [[F] dupdip G] primrec
H4 == c swap [P] [pop] [[F] dupdip G] primrec
With:
::
c == 1
F == *
G == --
P == 1 <=
c == 1
F == *
G == --
P == 1 <=
.. code:: ipython2
@@ -613,31 +609,31 @@ With:
Example: ``tails``
------------------
An example of a paramorphism for lists given in the `"Bananas..."
An example of a paramorphism for lists given in the `Bananas…”
paper <http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.41.125>`__
is ``tails`` which returns the list of "tails" of a list.
is ``tails`` which returns the list of tails of a list.
::
[1 2 3] tails
--------------------
[[] [3] [2 3]]
[1 2 3] tails
--------------------
[[] [3] [2 3]]
We can build as we go, and we want ``F`` to run after ``G``, so we use
pattern ``H2``:
::
H2 == c swap [P] [pop] [G [F] dip] primrec
H2 == c swap [P] [pop] [G [F] dip] primrec
We would use:
::
c == []
F == swons
G == rest dup
P == not
c == []
F == swons
G == rest dup
P == not
.. code:: ipython2
@@ -656,39 +652,39 @@ We would use:
Conclusion: Patterns of Recursion
---------------------------------
Our story so far...
Our story so far
Hylo-, Ana-, Cata-
~~~~~~~~~~~~~~~~~~
::
H == [P ] [pop c ] [G ] [dip F ] genrec
A == [P ] [pop []] [G ] [dip swap cons] genrec
C == [not] [pop c ] [uncons swap] [dip F ] genrec
H == [P ] [pop c ] [G ] [dip F ] genrec
A == [P ] [pop []] [G ] [dip swap cons] genrec
C == [not] [pop c ] [uncons swap] [dip F ] genrec
Para-, ?-, ?-
~~~~~~~~~~~~~
::
P == c swap [P ] [pop] [[F ] dupdip G ] primrec
? == [] swap [P ] [pop] [[swap cons] dupdip G ] primrec
? == c swap [not] [pop] [[F ] dupdip uncons swap] primrec
P == c swap [P ] [pop] [[F ] dupdip G ] primrec
? == [] swap [P ] [pop] [[swap cons] dupdip G ] primrec
? == c swap [not] [pop] [[F ] dupdip uncons swap] primrec
Appendix: Fun with Symbols
--------------------------
::
|[ (c, F), (G, P) ]| == (|c, F|) • [(G, P)]
|[ (c, F), (G, P) ]| == (|c, F|) • [(G, P)]
`"Bananas, Lenses, & Barbed
Wire" <http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.41.125>`__
`Bananas, Lenses, & Barbed
Wire <http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.41.125>`__
::
(|...|) [(...)] [<...>]
(|...|) [(...)] [<...>]
I think they are having slightly too much fun with the symbols. However,
"Too much is always better than not enough."
Too much is always better than not enough.
+4 -4
View File
@@ -4,8 +4,8 @@ Replacing Functions in the Dictionary
For now, there is no way to define new functions from within the Joy
language. All functions (and the interpreter) all accept and return a
dictionary parameter (in addition to the stack and expression) so that
we can implement e.g. a function that adds new functions to the
dictionary. However, there's no function that does that. Adding a new
we can implement e.g. a function that adds new functions to the
dictionary. However, theres no function that does that. Adding a new
function to the dictionary is a meta-interpreter action, you have to do
it in Python, not Joy.
@@ -74,8 +74,8 @@ Both ``sum`` and ``size`` each convert a sequence to a single value.
::
sum == 0 swap [+] step
size == 0 swap [pop ++] step
sum == 0 swap [+] step
size == 0 swap [pop ++] step
An efficient ``sum`` function is already in the library. But for
``size`` we can use a “compiled” version hand-written in Python to speed
+102 -102
View File
@@ -17,10 +17,10 @@ symbols together, juxtaposition:
::
foo bar
foo bar
Operations have inputs and outputs. The outputs of ``foo`` must be
compatible in "arity", type, and shape with the inputs of ``bar``.
compatible in arity, type, and shape with the inputs of ``bar``.
Branch
------
@@ -29,72 +29,72 @@ Do one thing or another.
::
boolean [F] [T] branch
boolean [F] [T] branch
t [F] [T] branch
----------------------
T
t [F] [T] branch
----------------------
T
f [F] [T] branch
----------------------
F
f [F] [T] branch
----------------------
F
branch == unit cons swap pick i
branch == unit cons swap pick i
boolean [F] [T] branch
boolean [F] [T] unit cons swap pick i
boolean [F] [[T]] cons swap pick i
boolean [[F] [T]] swap pick i
[[F] [T]] boolean pick i
[F-or-T] i
boolean [F] [T] branch
boolean [F] [T] unit cons swap pick i
boolean [F] [[T]] cons swap pick i
boolean [[F] [T]] swap pick i
[[F] [T]] boolean pick i
[F-or-T] i
Given some branch function ``G``:
::
G == [F] [T] branch
G == [F] [T] branch
Used in a sequence like so:
::
foo G bar
foo G bar
The inputs and outputs of ``F`` and ``T`` must be compatible with the
outputs for ``foo`` and the inputs of ``bar``, respectively.
::
foo F bar
foo F bar
foo T bar
foo T bar
``ifte``
~~~~~~~~
Often it will be easier on the programmer to write branching code with
the predicate specified in a quote. The ``ifte`` combinator provides
this (``T`` for "then" and ``E`` for "else"):
this (``T`` for then and ``E`` for else):
::
[P] [T] [E] ifte
[P] [T] [E] ifte
Defined in terms of ``branch``:
::
ifte == [nullary not] dip branch
ifte == [nullary not] dip branch
In this case, ``P`` must be compatible with the stack and return a
Boolean value, and ``T`` and ``E`` both must be compatible with the
preceeding and following functions, as described above for ``F`` and
``T``. (Note that in the current implementation we are depending on
Python for the underlying semantics, so the Boolean value doesn't *have*
to be Boolean because Python's rules for "truthiness" will be used to
Python for the underlying semantics, so the Boolean value doesnt *have*
to be Boolean because Pythons rules for truthiness will be used to
evaluate it. I reflect this in the structure of the stack effect comment
of ``branch``, it will only accept Boolean values, and in the definition
of ``ifte`` above by including ``not`` in the quote, which also has the
@@ -107,17 +107,17 @@ Do one thing zero or more times.
::
boolean [Q] loop
boolean [Q] loop
t [Q] loop
----------------
Q [Q] loop
t [Q] loop
----------------
Q [Q] loop
... f [Q] loop
--------------------
...
... f [Q] loop
--------------------
...
The ``loop`` combinator generates a copy of itself in the true branch.
This is the hallmark of recursive defintions. In Thun there is no
@@ -128,21 +128,21 @@ constructs that do not need to be directly self-referential, unlike
::
loop == [] swap [dup dip loop] cons branch
loop == [] swap [dup dip loop] cons branch
boolean [Q] loop
boolean [Q] [] swap [dup dip loop] cons branch
boolean [] [Q] [dup dip loop] cons branch
boolean [] [[Q] dup dip loop] branch
boolean [Q] loop
boolean [Q] [] swap [dup dip loop] cons branch
boolean [] [Q] [dup dip loop] cons branch
boolean [] [[Q] dup dip loop] branch
In action the false branch does nothing while the true branch does:
::
t [] [[Q] dup dip loop] branch
[Q] dup dip loop
[Q] [Q] dip loop
Q [Q] loop
t [] [[Q] dup dip loop] branch
[Q] dup dip loop
[Q] [Q] dip loop
Q [Q] loop
Because ``loop`` expects and consumes a Boolean value, the ``Q``
function must be compatible with the previous stack *and itself* with a
@@ -150,15 +150,15 @@ boolean flag for the next iteration:
::
Q == G b
Q == G b
Q [Q] loop
G b [Q] loop
G Q [Q] loop
G G b [Q] loop
G G Q [Q] loop
G G G b [Q] loop
G G G
Q [Q] loop
G b [Q] loop
G Q [Q] loop
G G b [Q] loop
G G Q [Q] loop
G G G b [Q] loop
G G G
``while``
~~~~~~~~~
@@ -170,21 +170,21 @@ flag for the next iteration:
::
[P] [B] while
--------------------------------------
[P] nullary [B [P] nullary] loop
[P] [B] while
--------------------------------------
[P] nullary [B [P] nullary] loop
while == swap [nullary] cons dup dipd concat loop
while == swap [nullary] cons dup dipd concat loop
[P] [B] while
[P] [B] swap [nullary] cons dup dipd concat loop
[B] [P] [nullary] cons dup dipd concat loop
[B] [[P] nullary] dup dipd concat loop
[B] [[P] nullary] [[P] nullary] dipd concat loop
[P] nullary [B] [[P] nullary] concat loop
[P] nullary [B [P] nullary] loop
[P] [B] while
[P] [B] swap [nullary] cons dup dipd concat loop
[B] [P] [nullary] cons dup dipd concat loop
[B] [[P] nullary] dup dipd concat loop
[B] [[P] nullary] [[P] nullary] dipd concat loop
[P] nullary [B] [[P] nullary] concat loop
[P] nullary [B [P] nullary] loop
Parallel
--------
@@ -192,11 +192,11 @@ Parallel
The *parallel* operation indicates that two (or more) functions *do not
interfere* with each other and so can run in parallel. The main
difficulty in this sort of thing is orchestrating the recombining
("join" or "wait") of the results of the functions after they finish.
(join or wait) of the results of the functions after they finish.
The current implementaions and the following definitions *are not
actually parallel* (yet), but there is no reason they couldn't be
reimplemented in terms of e.g. Python threads. I am not concerned with
actually parallel* (yet), but there is no reason they couldnt be
reimplemented in terms of e.g. Python threads. I am not concerned with
performance of the system just yet, only the elegance of the code it
allows us to write.
@@ -207,27 +207,27 @@ Joy has a few parallel combinators, the main one being ``cleave``:
::
... x [A] [B] cleave
---------------------------------------------------------
... [x ...] [A] infra first [x ...] [B] infra first
---------------------------------------------------------
... a b
... x [A] [B] cleave
---------------------------------------------------------
... [x ...] [A] infra first [x ...] [B] infra first
---------------------------------------------------------
... a b
The ``cleave`` combinator expects a value and two quotes and it executes
each quote in "separate universes" such that neither can affect the
each quote in separate universes such that neither can affect the
other, then it takes the first item from the stack in each universe and
replaces the value and quotes with their respective results.
(I think this corresponds to the "fork" operator, the little
(I think this corresponds to the fork operator, the little
upward-pointed triangle, that takes two functions ``A :: x -> a`` and
``B :: x -> b`` and returns a function ``F :: x -> (a, b)``, in Conal
Elliott's "Compiling to Categories" paper, et. al.)
Elliotts Compiling to Categories paper, et. al.)
Just a thought, if you ``cleave`` two jobs and one requires more time to
finish than the other you'd like to be able to assign resources
finish than the other youd like to be able to assign resources
accordingly so that they both finish at the same time.
"Apply" Functions
Apply Functions
~~~~~~~~~~~~~~~~~
There are also ``app2`` and ``app3`` which run a single quote on more
@@ -235,35 +235,35 @@ than one value:
::
... y x [Q] app2
---------------------------------------------------------
... [y ...] [Q] infra first [x ...] [Q] infra first
... y x [Q] app2
---------------------------------------------------------
... [y ...] [Q] infra first [x ...] [Q] infra first
... z y x [Q] app3
---------------------------------
... [z ...] [Q] infra first
[y ...] [Q] infra first
[x ...] [Q] infra first
... z y x [Q] app3
---------------------------------
... [z ...] [Q] infra first
[y ...] [Q] infra first
[x ...] [Q] infra first
Because the quoted program can be ``i`` we can define ``cleave`` in
terms of ``app2``:
::
cleave == [i] app2 [popd] dip
cleave == [i] app2 [popd] dip
(I'm not sure why ``cleave`` was specified to take that value, I may
(Im not sure why ``cleave`` was specified to take that value, I may
make a combinator that does the same thing but without expecting a
value.)
::
clv == [i] app2
clv == [i] app2
[A] [B] clv
------------------
a b
[A] [B] clv
------------------
a b
``map``
~~~~~~~
@@ -273,10 +273,10 @@ The common ``map`` function in Joy should also be though of as a
::
[a b c ...] [Q] map
[a b c ...] [Q] map
There is no reason why the implementation of ``map`` couldn't distribute
the ``Q`` function over e.g. a pool of worker CPUs.
There is no reason why the implementation of ``map`` couldnt distribute
the ``Q`` function over e.g. a pool of worker CPUs.
``pam``
~~~~~~~
@@ -285,16 +285,16 @@ One of my favorite combinators, the ``pam`` combinator is just:
::
pam == [i] map
pam == [i] map
This can be used to run any number of programs separately on the current
stack and combine their (first) outputs in a result list.
::
[[A] [B] [C] ...] [i] map
-------------------------------
[ a b c ...]
[[A] [B] [C] ...] [i] map
-------------------------------
[ a b c ...]
Handling Other Kinds of Join
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -302,7 +302,7 @@ Handling Other Kinds of Join
The ``cleave`` operators and others all have pretty brutal join
semantics: everything works and we always wait for every
sub-computation. We can imagine a few different potentially useful
patterns of "joining" results from parallel combinators.
patterns of joining results from parallel combinators.
first-to-finish
^^^^^^^^^^^^^^^
@@ -313,24 +313,24 @@ stack could be replaced by its output stack.
The other sub-programs would be cancelled.
"Fulminators"
Fulminators
^^^^^^^^^^^^^
Also known as "Futures" or "Promises" (by *everybody* else. "Fulinators"
Also known as Futures or Promises (by *everybody* else. Fulinators
is what I was going to call them when I was thinking about implementing
them in Thun.)
The runtime could be amended to permit "thunks" representing the results
The runtime could be amended to permit thunks representing the results
of in-progress computations to be left on the stack and picked up by
subsequent functions. These would themselves be able to leave behind
more "thunks", the values of which depend on the eventual resolution of
more thunks, the values of which depend on the eventual resolution of
the values of the previous thunks.
In this way you can create "chains" (and more complex shapes) out of
In this way you can create chains (and more complex shapes) out of
normal-looking code that consist of a kind of call-graph interspersed
with "asyncronous" ... events?
with asyncronous” … events?
In any case, until I can find a rigorous theory that shows that this
sort of thing works perfectly in Joy code I'm not going to worry about
sort of thing works perfectly in Joy code Im not going to worry about
it. (And I think the Categories can deal with it anyhow? Incremental
evaluation, yeah?)
+109 -109
View File
@@ -1,8 +1,8 @@
Treating Trees II: ``treestep``
===============================
Let's consider a tree structure, similar to one described `"Why
functional programming matters" by John
Lets consider a tree structure, similar to one described `Why
functional programming matters by John
Hughes <https://www.cs.kent.ac.uk/people/staff/dat/miranda/whyfp90.pdf>`__,
that consists of a node value followed by zero or more child trees. (The
asterisk is meant to indicate the `Kleene
@@ -10,7 +10,7 @@ star <https://en.wikipedia.org/wiki/Kleene_star>`__.)
::
tree = [] | [node tree*]
tree = [] | [node tree*]
In the spirit of ``step`` we are going to define a combinator
``treestep`` which expects a tree and three additional items: a
@@ -18,15 +18,15 @@ base-case function ``[B]``, and two quoted programs ``[N]`` and ``[C]``.
::
tree [B] [N] [C] treestep
tree [B] [N] [C] treestep
If the current tree node is empty then just execute ``B``:
::
[] [B] [N] [C] treestep
---------------------------
[] B
[] [B] [N] [C] treestep
---------------------------
[] B
Otherwise, evaluate ``N`` on the node value, ``map`` the whole function
(abbreviated here as ``K``) over the child trees recursively, and then
@@ -34,11 +34,11 @@ combine the result with ``C``.
::
[node tree*] [B] [N] [C] treestep
--------------------------------------- w/ K == [B] [N] [C] treestep
node N [tree*] [K] map C
[node tree*] [B] [N] [C] treestep
--------------------------------------- w/ K == [B] [N] [C] treestep
node N [tree*] [K] map C
(Later on we'll experiment with making ``map`` part of ``C`` so you can
(Later on well experiment with making ``map`` part of ``C`` so you can
use other combinators.)
Derive the recursive function.
@@ -49,59 +49,59 @@ will produce.
::
K == [not] [B] [R0] [R1] genrec
== [not] [B] [R0 [K] R1] ifte
K == [not] [B] [R0] [R1] genrec
== [not] [B] [R0 [K] R1] ifte
So we just have to derive ``J``:
::
J == R0 [K] R1
J == R0 [K] R1
The behavior of ``J`` is to accept a (non-empty) tree node and arrive at
the desired outcome.
::
[node tree*] J
------------------------------
node N [tree*] [K] map C
[node tree*] J
------------------------------
node N [tree*] [K] map C
So ``J`` will have some form like:
::
J == ... [N] ... [K] ... [C] ...
J == ... [N] ... [K] ... [C] ...
Let's dive in. First, unquote the node and ``dip`` ``N``.
Lets dive in. First, unquote the node and ``dip`` ``N``.
::
[node tree*] uncons [N] dip
node [tree*] [N] dip
node N [tree*]
[node tree*] uncons [N] dip
node [tree*] [N] dip
node N [tree*]
Next, ``map`` ``K`` over the child trees and combine with ``C``.
::
node N [tree*] [K] map C
node N [tree*] [K] map C
node N [K.tree*] C
node N [tree*] [K] map C
node N [tree*] [K] map C
node N [K.tree*] C
So:
::
J == uncons [N] dip [K] map C
J == uncons [N] dip [K] map C
Plug it in and convert to ``genrec``:
::
K == [not] [B] [J ] ifte
== [not] [B] [uncons [N] dip [K] map C] ifte
== [not] [B] [uncons [N] dip] [map C] genrec
K == [not] [B] [J ] ifte
== [not] [B] [uncons [N] dip [K] map C] ifte
== [not] [B] [uncons [N] dip] [map C] genrec
Extract the givens to parameterize the program.
-----------------------------------------------
@@ -110,26 +110,26 @@ Working backwards:
::
[not] [B] [uncons [N] dip] [map C] genrec
[B] [not] swap [uncons [N] dip] [map C] genrec
[B] [uncons [N] dip] [[not] swap] dip [map C] genrec
^^^^^^^^^^^^^^^^
[B] [[N] dip] [uncons] swoncat [[not] swap] dip [map C] genrec
[B] [N] [dip] cons [uncons] swoncat [[not] swap] dip [map C] genrec
^^^^^^^^^^^^^^^^^^^^^^^^^^^
[not] [B] [uncons [N] dip] [map C] genrec
[B] [not] swap [uncons [N] dip] [map C] genrec
[B] [uncons [N] dip] [[not] swap] dip [map C] genrec
^^^^^^^^^^^^^^^^
[B] [[N] dip] [uncons] swoncat [[not] swap] dip [map C] genrec
[B] [N] [dip] cons [uncons] swoncat [[not] swap] dip [map C] genrec
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Extract a couple of auxiliary definitions:
::
TS.0 == [[not] swap] dip
TS.1 == [dip] cons [uncons] swoncat
TS.0 == [[not] swap] dip
TS.1 == [dip] cons [uncons] swoncat
::
[B] [N] TS.1 TS.0 [map C] genrec
[B] [N] [map C] [TS.1 TS.0] dip genrec
[B] [N] [C] [map] swoncat [TS.1 TS.0] dip genrec
[B] [N] TS.1 TS.0 [map C] genrec
[B] [N] [map C] [TS.1 TS.0] dip genrec
[B] [N] [C] [map] swoncat [TS.1 TS.0] dip genrec
The givens are all to the left so we have our definition.
@@ -140,10 +140,10 @@ Working backwards:
::
[not] [B] [uncons [N] dip] [map C] genrec
[not] [B] [N] [dip] cons [uncons] swoncat [map C] genrec
[B] [N] [not] roll> [dip] cons [uncons] swoncat [map C] genrec
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[not] [B] [uncons [N] dip] [map C] genrec
[not] [B] [N] [dip] cons [uncons] swoncat [map C] genrec
[B] [N] [not] roll> [dip] cons [uncons] swoncat [map C] genrec
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Define ``treestep``
-------------------
@@ -171,7 +171,7 @@ all nodes in a tree with this function:
::
sumtree == [pop 0] [] [sum +] treestep
sumtree == [pop 0] [] [sum +] treestep
.. code:: ipython2
@@ -181,9 +181,9 @@ Running this function on an empty tree value gives zero:
::
[] [pop 0] [] [sum +] treestep
------------------------------------
0
[] [pop 0] [] [sum +] treestep
------------------------------------
0
.. code:: ipython2
@@ -199,11 +199,11 @@ Running it on a non-empty node:
::
[n tree*] [pop 0] [] [sum +] treestep
n [tree*] [[pop 0] [] [sum +] treestep] map sum +
n [ ... ] sum +
n m +
n+m
[n tree*] [pop 0] [] [sum +] treestep
n [tree*] [[pop 0] [] [sum +] treestep] map sum +
n [ ... ] sum +
n m +
n+m
.. code:: ipython2
@@ -310,7 +310,7 @@ Redefining the Ordered Binary Tree in terms of ``treestep``.
::
Tree = [] | [[key value] left right]
Tree = [] | [[key value] left right]
What kind of functions can we write for this with our ``treestep``?
@@ -318,26 +318,26 @@ The pattern for processing a non-empty node is:
::
node N [tree*] [K] map C
node N [tree*] [K] map C
Plugging in our BTree structure:
::
[key value] N [left right] [K] map C
[key value] N [left right] [K] map C
Traversal
~~~~~~~~~
::
[key value] first [left right] [K] map i
key [value] [left right] [K] map i
key [left right] [K] map i
key [lkey rkey ] i
key lkey rkey
[key value] first [left right] [K] map i
key [value] [left right] [K] map i
key [left right] [K] map i
key [lkey rkey ] i
key lkey rkey
This doesn't quite work:
This doesnt quite work:
.. code:: ipython2
@@ -349,25 +349,25 @@ This doesn't quite work:
3 'B' 'B'
Doesn't work because ``map`` extracts the ``first`` item of whatever its
Doesnt work because ``map`` extracts the ``first`` item of whatever its
mapped function produces. We have to return a list, rather than
depositing our results directly on the stack.
::
[key value] N [left right] [K] map C
[key value] N [left right] [K] map C
[key value] first [left right] [K] map flatten cons
key [left right] [K] map flatten cons
key [[lk] [rk] ] flatten cons
key [ lk rk ] cons
[key lk rk ]
[key value] first [left right] [K] map flatten cons
key [left right] [K] map flatten cons
key [[lk] [rk] ] flatten cons
key [ lk rk ] cons
[key lk rk ]
So:
::
[] [first] [flatten cons] treestep
[] [first] [flatten cons] treestep
.. code:: ipython2
@@ -388,18 +388,18 @@ From here:
::
key [[lk] [rk]] C
key [[lk] [rk]] i
key [lk] [rk] roll<
[lk] [rk] key swons concat
[lk] [key rk] concat
[lk key rk]
key [[lk] [rk]] C
key [[lk] [rk]] i
key [lk] [rk] roll<
[lk] [rk] key swons concat
[lk] [key rk] concat
[lk key rk]
So:
::
[] [i roll< swons concat] [first] treestep
[] [i roll< swons concat] [first] treestep
.. code:: ipython2
@@ -414,20 +414,20 @@ So:
With ``treegrind``?
-------------------
The ``treegrind`` function doesn't include the ``map`` combinator, so
The ``treegrind`` function doesnt include the ``map`` combinator, so
the ``[C]`` function must arrange to use some combinator on the quoted
recursive copy ``[K]``. With this function, the pattern for processing a
non-empty node is:
::
node N [tree*] [K] C
node N [tree*] [K] C
Plugging in our BTree structure:
::
[key value] N [left right] [K] C
[key value] N [left right] [K] C
.. code:: ipython2
@@ -454,7 +454,7 @@ Iteration through the nodes
[3 0] 'N' [2 0] 'N' [9 0] 'N' [5 0] 'N' [4 0] 'N' [8 0] 'N' [6 0] 'N' [7 0] 'N'
Sum the nodes' keys.
Sum the nodes keys.
.. code:: ipython2
@@ -485,28 +485,28 @@ I think we do:
::
[B] [N] [C] treegrind
[B] [N] [C] treegrind
We'll start by saying that the base-case (the key is not in the tree) is
Well start by saying that the base-case (the key is not in the tree) is
user defined, and the per-node function is just the query key literal:
::
[B] [query_key] [C] treegrind
[B] [query_key] [C] treegrind
This means we just have to define ``C`` from:
::
[key value] query_key [left right] [K] C
[key value] query_key [left right] [K] C
Let's try ``cmp``:
Lets try ``cmp``:
::
C == P [T>] [E] [T<] cmp
C == P [T>] [E] [T<] cmp
[key value] query_key [left right] [K] P [T>] [E] [T<] cmp
[key value] query_key [left right] [K] P [T>] [E] [T<] cmp
The predicate ``P``
~~~~~~~~~~~~~~~~~~~
@@ -516,16 +516,16 @@ equal):
::
[key value] query_key [left right] [K] P
[key value] query_key [left right] [K] roll<
[key value] [left right] [K] query_key [roll< uncons swap] dip
[key value] query_key [left right] [K] P
[key value] query_key [left right] [K] roll<
[key value] [left right] [K] query_key [roll< uncons swap] dip
[key value] [left right] [K] roll< uncons swap query_key
[left right] [K] [key value] uncons swap query_key
[left right] [K] key [value] swap query_key
[left right] [K] [value] key query_key
[key value] [left right] [K] roll< uncons swap query_key
[left right] [K] [key value] uncons swap query_key
[left right] [K] key [value] swap query_key
[left right] [K] [value] key query_key
P == roll< [roll< uncons swap] dip
P == roll< [roll< uncons swap] dip
(Possibly with a swap at the end? Or just swap ``T<`` and ``T>``.)
@@ -533,15 +533,15 @@ So now:
::
[left right] [K] [value] key query_key [T>] [E] [T<] cmp
[left right] [K] [value] key query_key [T>] [E] [T<] cmp
Becomes one of these three:
::
[left right] [K] [value] T>
[left right] [K] [value] E
[left right] [K] [value] T<
[left right] [K] [value] T>
[left right] [K] [value] E
[left right] [K] [value] T<
``E``
~~~~~
@@ -550,27 +550,27 @@ Easy.
::
E == roll> popop first
E == roll> popop first
``T<`` and ``T>``
~~~~~~~~~~~~~~~~~
::
T< == pop [first] dip i
T> == pop [second] dip i
T< == pop [first] dip i
T> == pop [second] dip i
Putting it together
-------------------
::
T> == pop [first] dip i
T< == pop [second] dip i
E == roll> popop first
P == roll< [roll< uncons swap] dip
T> == pop [first] dip i
T< == pop [second] dip i
E == roll> popop first
P == roll< [roll< uncons swap] dip
Tree-get == [P [T>] [E] [T<] cmp] treegrind
Tree-get == [P [T>] [E] [T<] cmp] treegrind
To me, that seems simpler than the ``genrec`` version.
+1 -1
View File
@@ -98,7 +98,7 @@ An Example
(... [3 4 ] 2 1 0 -- ... [1 2 ])
Unification Works "in Reverse"
Unification Works in Reverse
------------------------------
.. code:: ipython2
File diff suppressed because it is too large Load Diff
+26 -27
View File
@@ -1,14 +1,14 @@
Traversing Datastructures with Zippers
======================================
This notebook is about using the "zipper" with joy datastructures. See
This notebook is about using the zipper with joy datastructures. See
the `Zipper wikipedia
entry <https://en.wikipedia.org/wiki/Zipper_%28data_structure%29>`__ or
the original paper: `"FUNCTIONAL PEARL The Zipper" by Gérard
the original paper: `FUNCTIONAL PEARL The Zipper by Gérard
Huet <https://www.st.cs.uni-saarland.de/edu/seminare/2005/advanced-fp/docs/huet-zipper.pdf>`__
Given a datastructure on the stack we can navigate through it, modify
it, and rebuild it using the "zipper" technique.
it, and rebuild it using the zipper technique.
.. code:: ipython2
@@ -17,10 +17,9 @@ it, and rebuild it using the "zipper" technique.
Trees
-----
In Joypy there aren't any complex datastructures, just ints, floats,
In Joypy there arent any complex datastructures, just ints, floats,
strings, Symbols (strings that are names of functions) and sequences
(aka lists, aka quoted literals, aka aggregates, etc...), but we can
build
(aka lists, aka quoted literals, aka aggregates, etc), but we can build
`trees <https://en.wikipedia.org/wiki/Tree_%28data_structure%29>`__ out
of sequences.
@@ -45,12 +44,12 @@ In Joy we can do this with the following words:
::
z-down == [] swap uncons swap
z-up == swons swap shunt
z-right == [swons] cons dip uncons swap
z-left == swons [uncons swap] dip swap
z-down == [] swap uncons swap
z-up == swons swap shunt
z-right == [swons] cons dip uncons swap
z-left == swons [uncons swap] dip swap
Let's use them to change 25 into 625. The first time a word is used I
Lets use them to change 25 into 625. The first time a word is used I
show the trace so you can see how it works. If we were going to use
these a lot it would make sense to write Python versions for efficiency,
but see below.
@@ -208,8 +207,8 @@ but see below.
``dip`` and ``infra``
---------------------
In Joy we have the ``dip`` and ``infra`` combinators which can "target"
or "address" any particular item in a Joy tree structure.
In Joy we have the ``dip`` and ``infra`` combinators which can target
or address any particular item in a Joy tree structure.
.. code:: ipython2
@@ -247,8 +246,8 @@ or "address" any particular item in a Joy tree structure.
[1 [2 [3 4 625 6] 7] 8] .
If you read the trace carefully you'll see that about half of it is the
``dip`` and ``infra`` combinators de-quoting programs and "digging" into
If you read the trace carefully youll see that about half of it is the
``dip`` and ``infra`` combinators de-quoting programs and digging into
the subject datastructure. Instead of maintaining temporary results on
the stack they are pushed into the pending expression (continuation).
When ``sqr`` has run the rest of the pending expression rebuilds the
@@ -264,12 +263,12 @@ been embedded in a nested series of quoted programs, e.g.:
::
[...] [Q] [dip dip infra dip infra dip infra] Z
-------------------------------------------------------------
[...] [[[[[[[Q] dip] dip] infra] dip] infra] dip] infra
[...] [Q] [dip dip infra dip infra dip infra] Z
-------------------------------------------------------------
[...] [[[[[[[Q] dip] dip] infra] dip] infra] dip] infra
The ``Z`` function isn't hard to make.
The ``Z`` function isnt hard to make.
.. code:: ipython2
@@ -333,21 +332,21 @@ a string made from only two characters.
::
[...] [Q] 'ddididi' Zstr
-------------------------------------------------------------
[...] [[[[[[[Q] dip] dip] infra] dip] infra] dip] infra
[...] [Q] 'ddididi' Zstr
-------------------------------------------------------------
[...] [[[[[[[Q] dip] dip] infra] dip] infra] dip] infra
The string can be considered a name or address for an item in the
subject datastructure.
Determining the right "path" for an item in a tree.
Determining the right path for an item in a tree.
---------------------------------------------------
It's easy to read off (in reverse) the right sequence of "d" and "i"
Its easy to read off (in reverse) the right sequence of “d” and “i”
from the subject datastructure:
::
[ n [ n [ n n x ...
i d i d i d d Bingo!
[ n [ n [ n n x ...
i d i d i d d Bingo!