diff --git a/docs/0._This_Implementation_of_Joy_in_Python.rst b/docs/0._This_Implementation_of_Joy_in_Python.rst index 47635e5..80a196d 100644 --- a/docs/0._This_Implementation_of_Joy_in_Python.rst +++ b/docs/0._This_Implementation_of_Joy_in_Python.rst @@ -22,18 +22,18 @@ that you start by running the package: :: - $ python -m joy - Joypy - 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. - Type "words" to see a list of all words, and "[] help" to print the - docs for a word. + $ python -m joy + Joypy - 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. + Type "words" to see a list of all words, and "[] help" to print the + docs for a word. - <-top + <-top - joy? _ + joy? _ The ``<-top`` marker points to the top of the (initially empty) stack. You can enter Joy notation at the prompt and a `trace of @@ -42,18 +42,18 @@ and prompt again: :: - joy? 23 sqr 18 + - . 23 sqr 18 + - 23 . sqr 18 + - 23 . dup mul 18 + - 23 23 . mul 18 + - 529 . 18 + - 529 18 . + - 547 . + joy? 23 sqr 18 + + . 23 sqr 18 + + 23 . sqr 18 + + 23 . dup mul 18 + + 23 23 . mul 18 + + 529 . 18 + + 529 18 . + + 547 . - 547 <-top + 547 <-top - joy? + joy? Stacks (aka list, quote, sequence, etc.) ======================================== @@ -103,8 +103,8 @@ Purely Functional Datastructures. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Because Joy lists are made out of Python tuples they are immutable, so -all Joy datastructures are *`purely -functional `__*. +all Joy datastructures are `purely +functional `__. The ``joy()`` function. ======================= @@ -119,8 +119,8 @@ looks up in the dictionary. Each function is passed the stack, expression, and dictionary and returns them. Whatever the function returns becomes the new stack, -expression, and dictionary. (The dictionary is passed to enable e.g. -writing words that let you enter new words into the dictionary at +expression, and dictionary. (The dictionary is passed to enable +e.g. writing words that let you enter new words into the dictionary at runtime, which nothing does yet and may be a bad idea, and the ``help`` command.) @@ -133,7 +133,7 @@ command.) View function ~~~~~~~~~~~~~ -The ``joy()`` function accepts a "viewer" function which it calls on +The ``joy()`` function accepts a “viewer” function which it calls on each iteration passing the current stack and expression just before evaluation. This can be used for tracing, breakpoints, retrying after exceptions, or interrupting an evaluation and saving to disk or sending @@ -147,7 +147,7 @@ A ``viewer`` records each step of the evaluation of a Joy program. The ``TracePrinter`` has a facility for printing out a trace of the evaluation, one line per step. Each step is aligned to the current interpreter position, signified by a period separating the stack on the -left from the pending expression ("continuation") on the right. +left from the pending expression (“continuation”) on the right. `Continuation-Passing Style `__ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -191,7 +191,7 @@ Parser The parser is extremely simple, the undocumented ``re.Scanner`` class does most of the tokenizing work and then you just build the tuple -structure out of the tokens. There's no Abstract Syntax Tree or anything +structure out of the tokens. There’s no Abstract Syntax Tree or anything like that. .. code:: ipython2 @@ -226,7 +226,7 @@ like that. -That's pretty much all there is to it. +That’s pretty much all there is to it. .. code:: ipython2 @@ -298,7 +298,7 @@ That's pretty much all there is to it. Library ======= -The Joy library of functions (aka commands, or "words" after Forth +The Joy library of functions (aka commands, or “words” after Forth usage) encapsulates all the actual functionality (no pun intended) of the Joy system. There are simple functions such as addition ``add`` (or ``+``, the library module supports aliases), and combinators which @@ -398,42 +398,42 @@ continuation) and returns control to the interpreter. -Currently, there's no function to add new definitions to the dictionary -from "within" Joy code itself. Adding new definitions remains a +Currently, there’s no function to add new definitions to the dictionary +from “within” Joy code itself. Adding new definitions remains a meta-interpreter action. You have to do it yourself, in Python, and wash your hands afterward. It would be simple enough to define one, but it would open the door to *name binding* and break the idea that all state is captured in the -stack and expression. There's an implicit *standard dictionary* that +stack and expression. There’s an implicit *standard dictionary* that defines the actual semantics of the syntactic stack and expression datastructures (which only contain symbols, not the actual functions. Pickle some and see for yourself.) -"There should be only one." +“There should be only one.” ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Which brings me to talking about one of my hopes and dreams for this -notation: "There should be only one." What I mean is that there should +notation: “There should be only one.” What I mean is that there should be one universal standard dictionary of commands, and all bespoke work done in a UI for purposes takes place by direct interaction and macros. There would be a *Grand Refactoring* biannually (two years, not six -months, that's semi-annually) where any new definitions factored out of +months, that’s semi-annually) where any new definitions factored out of the usage and macros of the previous time, along with new algorithms and -such, were entered into the dictionary and posted to e.g. IPFS. +such, were entered into the dictionary and posted to e.g. IPFS. Code should not burgeon wildly, as it does today. The variety of code should map more-or-less to the well-factored variety of human -computably-solvable problems. There shouldn't be dozens of chat apps, JS -frameworks, programming languages. It's a waste of time, a `fractal -"thundering herd" +computably-solvable problems. There shouldn’t be dozens of chat apps, JS +frameworks, programming languages. It’s a waste of time, a `fractal +“thundering herd” attack `__ on human mentality. Literary Code Library ^^^^^^^^^^^^^^^^^^^^^ -If you read over the other notebooks you'll see that developing code in +If you read over the other notebooks you’ll see that developing code in Joy is a lot like doing simple mathematics, and the descriptions of the code resemble math papers. The code also works the first time, no bugs. If you have any experience programming at all, you are probably diff --git a/docs/1._Basic_Use_of_Joy_in_a_Notebook.rst b/docs/1._Basic_Use_of_Joy_in_a_Notebook.rst index 6795c65..6f7a4cd 100644 --- a/docs/1._Basic_Use_of_Joy_in_a_Notebook.rst +++ b/docs/1._Basic_Use_of_Joy_in_a_Notebook.rst @@ -58,7 +58,7 @@ A ``viewer`` records each step of the evaluation of a Joy program. The ``TracePrinter`` has a facility for printing out a trace of the evaluation, one line per step. Each step is aligned to the current interpreter position, signified by a period separating the stack on the -left from the pending expression ("continuation") on the right. I find +left from the pending expression (“continuation”) on the right. I find these traces beautiful, like a kind of art. .. code:: ipython2 @@ -105,7 +105,7 @@ these traces beautiful, like a kind of art. 15 . -Here's a longer trace. +Here’s a longer trace. .. code:: ipython2 diff --git a/docs/2._Library_Examples.rst b/docs/2._Library_Examples.rst index 81030ca..01e893a 100644 --- a/docs/2._Library_Examples.rst +++ b/docs/2._Library_Examples.rst @@ -10,10 +10,10 @@ Stack Chatter This is what I like to call the functions that just rearrange things on the stack. (One thing I want to mention is that during a hypothetical -compilation phase these "stack chatter" words effectively disappear, +compilation phase these “stack chatter” words effectively disappear, because we can map the logical stack locations to registers that remain static for the duration of the computation. This remains to be done but -it's "off the shelf" technology.) +it’s “off the shelf” technology.) ``clear`` ~~~~~~~~~ @@ -139,7 +139,7 @@ they are not, is on the top of both the list and the stack. ``roll<`` ``rolldown`` ``roll>`` ``rollup`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The "down" and "up" refer to the movement of two of the top three items +The “down” and “up” refer to the movement of two of the top three items (displacing the third.) .. code:: ipython2 @@ -474,7 +474,7 @@ List words ``swaack`` ~~~~~~~~~~ -"Swap stack" swap the list on the top of the stack for the stack, and +“Swap stack” swap the list on the top of the stack for the stack, and put the old stack on top of the new one. Think of it as a context switch. Niether of the lists/stacks change their order. @@ -869,7 +869,7 @@ pow ~~~~~~~~~~~~~~~~~~ If we represent fractions as a quoted pair of integers [q d] this word -reduces them to their ... least common factors or whatever. +reduces them to their … least common factors or whatever. .. code:: ipython2 @@ -931,7 +931,7 @@ Get the Boolean value of the item on the top of the stack. :: - ? == dup truthy + ? == dup truthy .. code:: ipython2 @@ -1178,15 +1178,15 @@ function ``[G]``, the ``anamorphism`` combinator creates a sequence. :: - n [P] [G] anamorphism - --------------------------- - [...] + n [P] [G] anamorphism + --------------------------- + [...] Example, ``range``: :: - range == [0 <=] [1 - dup] anamorphism + range == [0 <=] [1 - dup] anamorphism .. code:: ipython2 @@ -1226,25 +1226,25 @@ Example, ``range``: :: - ... x [P] [Q] cleave + ... x [P] [Q] cleave -From the original Joy docs: "The cleave combinator expects two +From the original Joy docs: “The cleave combinator expects two quotations, and below that an item ``x`` It first executes ``[P]``, with ``x`` on top, and saves the top result element. Then it executes ``[Q]``, again with ``x``, and saves the top result. Finally it restores the stack to what it was below ``x`` and pushes the two results P(X) and -Q(X)." +Q(X).” Note that ``P`` and ``Q`` can use items from the stack freely, since the stack (below ``x``) is restored. ``cleave`` is a kind of *parallel* -primitive, and it would make sense to create a version that uses, e.g. -Python threads or something, to actually run ``P`` and ``Q`` +primitive, and it would make sense to create a version that uses, +e.g. Python threads or something, to actually run ``P`` and ``Q`` concurrently. The current implementation of ``cleave`` is a definition in terms of ``app2``: :: - cleave == [i] app2 [popd] dip + cleave == [i] app2 [popd] dip .. code:: ipython2 @@ -1297,7 +1297,7 @@ Expects a quoted program ``[Q]`` on the stack and some item under it, :: - n [Q] dupdip == n Q n + n [Q] dupdip == n Q n .. code:: ipython2 @@ -1417,7 +1417,7 @@ Expects a quoted program ``[Q]`` on the stack and some item under it, :: - [predicate] [then] [else] ifte + [predicate] [then] [else] ifte .. code:: ipython2 @@ -1724,7 +1724,7 @@ Run a quoted program enforcing :: - [predicate] [body] while + [predicate] [body] while .. code:: ipython2 @@ -1784,8 +1784,8 @@ Run a quoted program enforcing ``void`` ======== -Implements `**Laws of Form** -*arithmetic* `__ +Implements `Laws of Form +arithmetic `__ over quote-only datastructures (that is, datastructures that consist soley of containers, without strings or numbers or anything else.) diff --git a/docs/3._Developing_a_Program.rst b/docs/3._Developing_a_Program.rst index 56995f8..7723f59 100644 --- a/docs/3._Developing_a_Program.rst +++ b/docs/3._Developing_a_Program.rst @@ -1,17 +1,17 @@ -`Project Euler, first problem: "Multiples of 3 and 5" `__ +`Project Euler, first problem: “Multiples of 3 and 5” `__ ============================================================================================= :: - If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. + If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. - Find the sum of all the multiples of 3 or 5 below 1000. + Find the sum of all the multiples of 3 or 5 below 1000. .. code:: ipython2 from notebook_preamble import J, V, define -Let's create a predicate that returns ``True`` if a number is a multiple +Let’s create a predicate that returns ``True`` if a number is a multiple of 3 or 5 and ``False`` otherwise. .. code:: ipython2 @@ -44,7 +44,7 @@ Given the predicate function ``P`` a suitable program is: :: - PE1 == 1000 range [P] filter sum + PE1 == 1000 range [P] filter sum This function generates a list of the integers from 0 to 999, filters that list by ``P``, and then sums the result. @@ -68,22 +68,22 @@ Consider the first few terms in the series: :: - 3 5 6 9 10 12 15 18 20 21 ... + 3 5 6 9 10 12 15 18 20 21 ... Subtract each number from the one after it (subtracting 0 from 3): :: - 3 5 6 9 10 12 15 18 20 21 24 25 27 30 ... - 0 3 5 6 9 10 12 15 18 20 21 24 25 27 ... - ------------------------------------------- - 3 2 1 3 1 2 3 3 2 1 3 1 2 3 ... + 3 5 6 9 10 12 15 18 20 21 24 25 27 30 ... + 0 3 5 6 9 10 12 15 18 20 21 24 25 27 ... + ------------------------------------------- + 3 2 1 3 1 2 3 3 2 1 3 1 2 3 ... You get this lovely repeating palindromic sequence: :: - 3 2 1 3 1 2 3 + 3 2 1 3 1 2 3 To make a counter that increments by factors of 3 and 5 you just add these differences to the counter one-by-one in a loop. @@ -95,7 +95,7 @@ the counter to the running sum. This function will do that: :: - PE1.1 == + [+] dupdip + PE1.1 == + [+] dupdip .. code:: ipython2 @@ -276,8 +276,8 @@ get to 990 and then the first four numbers 3 2 1 3 to get to 999. 233168 -This form uses no extra storage and produces no unused summands. It's -good but there's one more trick we can apply. The list of seven terms +This form uses no extra storage and produces no unused summands. It’s +good but there’s one more trick we can apply. The list of seven terms takes up at least seven bytes. But notice that all of the terms are less than four, and so each can fit in just two bits. We could store all seven terms in just fourteen bits and use masking and shifts to pick out @@ -286,8 +286,8 @@ integer terms from the list. :: - 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 .. code:: ipython2 @@ -516,14 +516,14 @@ And so we have at last: 233168 -Let's refactor. +Let’s refactor. :: - 14811 7 [PE1.2] times pop - 14811 4 [PE1.2] times pop - 14811 n [PE1.2] times pop - n 14811 swap [PE1.2] times pop + 14811 7 [PE1.2] times pop + 14811 4 [PE1.2] times pop + 14811 n [PE1.2] times pop + n 14811 swap [PE1.2] times pop .. code:: ipython2 @@ -545,21 +545,21 @@ Now we can simplify the definition above: 233168 -Here's our joy program all in one place. It doesn't make so much sense, +Here’s our joy program all in one place. It doesn’t make so much sense, but if you have read through the above description of how it was derived -I hope it's clear. +I hope it’s clear. :: - PE1.1 == + [+] dupdip - PE1.2 == [3 & PE1.1] dupdip 2 >> - PE1.3 == 14811 swap [PE1.2] times pop - PE1 == 0 0 66 [7 PE1.3] times 4 PE1.3 pop + PE1.1 == + [+] dupdip + PE1.2 == [3 & PE1.1] dupdip 2 >> + PE1.3 == 14811 swap [PE1.2] times pop + PE1 == 0 0 66 [7 PE1.3] times 4 PE1.3 pop Generator Version ================= -It's a little clunky iterating sixty-six times though the seven numbers +It’s a little clunky iterating sixty-six times though the seven numbers then four more. In the *Generator Programs* notebook we derive a generator that can be repeatedly driven by the ``x`` combinator to produce a stream of the seven numbers repeating over and over again. @@ -591,8 +591,8 @@ terms to reach up to but not over one thousand. 466 -Here they are... -~~~~~~~~~~~~~~~~ +Here they are… +~~~~~~~~~~~~~~ .. code:: ipython2 @@ -604,8 +604,8 @@ Here they are... 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 -...and they do sum to 999. -~~~~~~~~~~~~~~~~~~~~~~~~~~ +…and they do sum to 999. +~~~~~~~~~~~~~~~~~~~~~~~~ .. code:: ipython2 @@ -618,7 +618,7 @@ Here they are... Now we can use ``PE1.1`` to accumulate the terms as we go, and then -``pop`` the generator and the counter from the stack when we're done, +``pop`` the generator and the counter from the stack when we’re done, leaving just the sum. .. code:: ipython2 @@ -652,21 +652,21 @@ Instead of summing them, :: - 10 9 8 7 6 - + 1 2 3 4 5 - ---- -- -- -- -- - 11 11 11 11 11 - - 11 * 5 = 55 + 10 9 8 7 6 + + 1 2 3 4 5 + ---- -- -- -- -- + 11 11 11 11 11 + + 11 * 5 = 55 From the above example we can deduce that the sum of the first N positive integers is: :: - (N + 1) * N / 2 + (N + 1) * N / 2 -(The formula also works for odd values of N, I'll leave that to you if +(The formula also works for odd values of N, I’ll leave that to you if you want to work it out or you can take my word for it.) .. code:: ipython2 @@ -695,20 +695,20 @@ Generalizing to Blocks of Terms We can apply the same reasoning to the PE1 problem. -Between 0 and 990 inclusive there are sixty-six "blocks" of seven terms +Between 0 and 990 inclusive there are sixty-six “blocks” of seven terms each, starting with: :: - [3 5 6 9 10 12 15] + [3 5 6 9 10 12 15] And ending with: :: - [978 980 981 984 985 987 990] + [978 980 981 984 985 987 990] -If we reverse one of these two blocks and sum pairs... +If we reverse one of these two blocks and sum pairs… .. code:: ipython2 @@ -749,9 +749,9 @@ additional unpaired terms between 990 and 1000: :: - 993 995 996 999 + 993 995 996 999 -So we can give the "sum of all the multiples of 3 or 5 below 1000" like +So we can give the “sum of all the multiples of 3 or 5 below 1000” like so: .. code:: ipython2 @@ -764,7 +764,7 @@ so: 233168 -It's worth noting, I think, that this same reasoning holds for any two +It’s worth noting, I think, that this same reasoning holds for any two numbers :math:`n` and :math:`m` the multiples of which we hope to sum. The multiples would have a cycle of differences of length :math:`k` and so we could compute the sum of :math:`Nk` multiples as above. @@ -774,14 +774,14 @@ interval spanning the least common multiple of :math:`n` and :math:`m`: :: - | | | | | | | | - | | | | | + | | | | | | | | + | | | | | Here we have 4 and 7, and you can read off the sequence of differences directly from the diagram: 4 3 1 4 2 2 4 1 3 4. Geometrically, the actual values of :math:`n` and :math:`m` and their -*lcm* don't matter, the pattern they make will always be symmetrical +*lcm* don’t matter, the pattern they make will always be symmetrical around its midpoint. The same reasoning holds for multiples of more than two numbers. @@ -793,6 +793,6 @@ is just: :: - PE1 == 233168 + PE1 == 233168 Fin. diff --git a/docs/Advent_of_Code_2017_December_1st.rst b/docs/Advent_of_Code_2017_December_1st.rst index e53ed97..f9cbb60 100644 --- a/docs/Advent_of_Code_2017_December_1st.rst +++ b/docs/Advent_of_Code_2017_December_1st.rst @@ -21,7 +21,7 @@ For example: from notebook_preamble import J, V, define -I'll assume the input is a Joy sequence of integers (as opposed to a +I’ll assume the input is a Joy sequence of integers (as opposed to a string or something else.) We might proceed by creating a word that makes a copy of the sequence @@ -31,26 +31,26 @@ a total if the pair matches. :: - AoC2017.1 == pair_up total_matches + AoC2017.1 == pair_up total_matches -Let's derive ``pair_up``: +Let’s derive ``pair_up``: :: - [a b c] pair_up - ------------------------- - [[a b] [b c] [c a]] + [a b c] pair_up + ------------------------- + [[a b] [b c] [c a]] Straightforward (although the order of each pair is reversed, due to the -way ``zip`` works, but it doesn't matter for this program): +way ``zip`` works, but it doesn’t matter for this program): :: - [a b c] dup - [a b c] [a b c] uncons swap - [a b c] [b c] a unit concat - [a b c] [b c a] zip - [[b a] [c b] [a c]] + [a b c] dup + [a b c] [a b c] uncons swap + [a b c] [b c] a unit concat + [a b c] [b c a] zip + [[b a] [c b] [a c]] .. code:: ipython2 @@ -80,40 +80,40 @@ Now we need to derive ``total_matches``. It will be a ``step`` function: :: - total_matches == 0 swap [F] step + total_matches == 0 swap [F] step Where ``F`` will have the pair to work with, and it will basically be a ``branch`` or ``ifte``. :: - total [n m] F + total [n m] F It will probably be easier to write if we dequote the pair: :: - total [n m] i F′ - ---------------------- - total n m F′ + total [n m] i F′ + ---------------------- + total n m F′ Now ``F′`` becomes just: :: - total n m [=] [pop +] [popop] ifte + total n m [=] [pop +] [popop] ifte So: :: - F == i [=] [pop +] [popop] ifte + F == i [=] [pop +] [popop] ifte And thus: :: - total_matches == 0 swap [i [=] [pop +] [popop] ifte] step + total_matches == 0 swap [i [=] [pop +] [popop] ifte] step .. code:: ipython2 @@ -197,17 +197,17 @@ Now we can define our main program and evaluate it on the examples. :: - pair_up == dup uncons swap unit concat zip - total_matches == 0 swap [i [=] [pop +] [popop] ifte] step + pair_up == dup uncons swap unit concat zip + total_matches == 0 swap [i [=] [pop +] [popop] ifte] step - AoC2017.1 == pair_up total_matches + AoC2017.1 == pair_up total_matches -Now the paired digit is "halfway" round. +Now the paired digit is “halfway” round. :: - [a b c d] dup size 2 / [drop] [take reverse] cleave concat zip + [a b c d] dup size 2 / [drop] [take reverse] cleave concat zip .. code:: ipython2 @@ -219,7 +219,7 @@ Now the paired digit is "halfway" round. [[3 1] [4 2] [1 3] [2 4]] -I realized that each pair is repeated... +I realized that each pair is repeated… .. code:: ipython2 @@ -270,19 +270,19 @@ Refactor FTW With Joy a great deal of the heuristics from Forth programming carry over nicely. For example, refactoring into small, well-scoped commands -with mnemonic names... +with mnemonic names… :: - rotate_seq == uncons swap unit concat - pair_up == dup rotate_seq zip - add_if_match == [=] [pop +] [popop] ifte - total_matches == [i add_if_match] step_zero + rotate_seq == uncons swap unit concat + pair_up == dup rotate_seq zip + add_if_match == [=] [pop +] [popop] ifte + total_matches == [i add_if_match] step_zero - AoC2017.1 == pair_up total_matches + AoC2017.1 == pair_up total_matches - half_of_size == dup size 2 / - split_at == [drop] [take reverse] cleave - pair_up.extra == half_of_size split_at zip swap pop + half_of_size == dup size 2 / + split_at == [drop] [take reverse] cleave + pair_up.extra == half_of_size split_at zip swap pop - AoC2017.1.extra == pair_up.extra total_matches 2 * + AoC2017.1.extra == pair_up.extra total_matches 2 * diff --git a/docs/Advent_of_Code_2017_December_2nd.rst b/docs/Advent_of_Code_2017_December_2nd.rst index 6dc0a28..8834119 100644 --- a/docs/Advent_of_Code_2017_December_2nd.rst +++ b/docs/Advent_of_Code_2017_December_2nd.rst @@ -11,35 +11,35 @@ For example, given the following spreadsheet: :: - 5 1 9 5 - 7 5 3 - 2 4 6 8 + 5 1 9 5 + 7 5 3 + 2 4 6 8 -- The first row's largest and smallest values are 9 and 1, and their +- The first row’s largest and smallest values are 9 and 1, and their difference is 8. -- The second row's largest and smallest values are 7 and 3, and their +- The second row’s largest and smallest values are 7 and 3, and their difference is 4. -- The third row's difference is 6. +- The third row’s difference is 6. -In this example, the spreadsheet's checksum would be 8 + 4 + 6 = 18. +In this example, the spreadsheet’s checksum would be 8 + 4 + 6 = 18. .. code:: ipython2 from notebook_preamble import J, V, define -I'll assume the input is a Joy sequence of sequences of integers. +I’ll assume the input is a Joy sequence of sequences of integers. :: - [[5 1 9 5] - [7 5 3] - [2 4 6 8]] + [[5 1 9 5] + [7 5 3] + [2 4 6 8]] So, obviously, the initial form will be a ``step`` function: :: - AoC2017.2 == 0 swap [F +] step + AoC2017.2 == 0 swap [F +] step This function ``F`` must get the ``max`` and ``min`` of a row of numbers and subtract. We can define a helper function ``maxmin`` which does @@ -63,7 +63,7 @@ Then ``F`` just does that then subtracts the min from the max: :: - F == maxmin - + F == maxmin - So: @@ -87,18 +87,18 @@ So: 18 -...find the only two numbers in each row where one evenly divides the +…find the only two numbers in each row where one evenly divides the other - that is, where the result of the division operation is a whole number. They would like you to find those numbers on each line, divide -them, and add up each line's result. +them, and add up each line’s result. For example, given the following spreadsheet: :: - 5 9 2 8 - 9 4 7 3 - 3 8 6 5 + 5 9 2 8 + 9 4 7 3 + 3 8 6 5 - In the first row, the only two numbers that evenly divide are 8 and 2; the result of this division is 4. @@ -107,7 +107,7 @@ For example, given the following spreadsheet: In this example, the sum of the results would be 4 + 3 + 2 = 9. -What is the sum of each row's result in your puzzle input? +What is the sum of each row’s result in your puzzle input? .. code:: ipython2 @@ -131,8 +131,8 @@ What is the sum of each row's result in your puzzle input? :: - [9 8 5 2] uncons [swap [divmod] cons F] dupdip G - [8 5 2] [9 divmod] F [8 5 2] G + [9 8 5 2] uncons [swap [divmod] cons F] dupdip G + [8 5 2] [9 divmod] F [8 5 2] G .. code:: ipython2 @@ -162,7 +162,7 @@ What is the sum of each row's result in your puzzle input? Tricky ------ -Let's think. +Let’s think. Given a *sorted* sequence (from highest to lowest) we want to \* for head, tail in sequence \* for term in tail: \* check if the head % term @@ -173,62 +173,62 @@ So we want a ``loop`` I think :: - [a b c d] True [Q] loop - [a b c d] Q [Q] loop + [a b c d] True [Q] loop + [a b c d] Q [Q] loop ``Q`` should either leave the result and False, or the ``rest`` and True. :: - [a b c d] Q - ----------------- - result 0 + [a b c d] Q + ----------------- + result 0 - [a b c d] Q - ----------------- - [b c d] 1 + [a b c d] Q + ----------------- + [b c d] 1 This suggests that ``Q`` should start with: :: - [a b c d] uncons dup roll< - [b c d] [b c d] a + [a b c d] uncons dup roll< + [b c d] [b c d] a -Now we just have to ``pop`` it if we don't need it. +Now we just have to ``pop`` it if we don’t need it. :: - [b c d] [b c d] a [P] [T] [cons] app2 popdd [E] primrec - [b c d] [b c d] [a P] [a T] [E] primrec + [b c d] [b c d] a [P] [T] [cons] app2 popdd [E] primrec + [b c d] [b c d] [a P] [a T] [E] primrec -------------- :: - w/ Q == [% not] [T] [F] primrec + w/ Q == [% not] [T] [F] primrec - [a b c d] uncons - a [b c d] tuck - [b c d] a [b c d] uncons - [b c d] a b [c d] roll> - [b c d] [c d] a b Q - [b c d] [c d] a b [% not] [T] [F] primrec + [a b c d] uncons + a [b c d] tuck + [b c d] a [b c d] uncons + [b c d] a b [c d] roll> + [b c d] [c d] a b Q + [b c d] [c d] a b [% not] [T] [F] primrec - [b c d] [c d] a b T - [b c d] [c d] a b / roll> popop 0 + [b c d] [c d] a b T + [b c d] [c d] a b / roll> popop 0 - [b c d] [c d] a b F Q - [b c d] [c d] a b pop swap uncons ... Q - [b c d] [c d] a swap uncons ... Q - [b c d] a [c d] uncons ... Q - [b c d] a c [d] roll> Q - [b c d] [d] a c Q + [b c d] [c d] a b F Q + [b c d] [c d] a b pop swap uncons ... Q + [b c d] [c d] a swap uncons ... Q + [b c d] a [c d] uncons ... Q + [b c d] a c [d] roll> Q + [b c d] [d] a c Q - Q == [% not] [/ roll> popop 0] [pop swap uncons roll>] primrec + Q == [% not] [/ roll> popop 0] [pop swap uncons roll>] primrec - uncons tuck uncons roll> Q + uncons tuck uncons roll> Q .. code:: ipython2 @@ -244,55 +244,55 @@ Now we just have to ``pop`` it if we don't need it. :: - [a b c d] uncons - a [b c d] tuck - [b c d] a [b c d] [not] [popop 1] [Q] ifte + [a b c d] uncons + a [b c d] tuck + [b c d] a [b c d] [not] [popop 1] [Q] ifte - [b c d] a [] popop 1 - [b c d] 1 + [b c d] a [] popop 1 + [b c d] 1 - [b c d] a [b c d] Q + [b c d] a [b c d] Q - a [...] Q - --------------- - result 0 + a [...] Q + --------------- + result 0 - a [...] Q - --------------- - 1 + a [...] Q + --------------- + 1 - w/ Q == [first % not] [first / 0] [rest [not] [popop 1]] [ifte] + w/ Q == [first % not] [first / 0] [rest [not] [popop 1]] [ifte] - a [b c d] [first % not] [first / 0] [rest [not] [popop 1]] [ifte] - a [b c d] first % not - a b % not - a%b not - bool(a%b) + a [b c d] [first % not] [first / 0] [rest [not] [popop 1]] [ifte] + a [b c d] first % not + a b % not + a%b not + bool(a%b) - a [b c d] [first % not] [first / 0] [rest [not] [popop 1]] [ifte] - a [b c d] first / 0 - a b / 0 - a/b 0 + a [b c d] [first % not] [first / 0] [rest [not] [popop 1]] [ifte] + a [b c d] first / 0 + a b / 0 + a/b 0 - a [b c d] [first % not] [first / 0] [rest [not] [popop 1]] [ifte] - a [b c d] rest [not] [popop 1] [Q] ifte - a [c d] [not] [popop 1] [Q] ifte - a [c d] [not] [popop 1] [Q] ifte + a [b c d] [first % not] [first / 0] [rest [not] [popop 1]] [ifte] + a [b c d] rest [not] [popop 1] [Q] ifte + a [c d] [not] [popop 1] [Q] ifte + a [c d] [not] [popop 1] [Q] ifte - a [c d] [not] [popop 1] [Q] ifte - a [c d] not + a [c d] [not] [popop 1] [Q] ifte + a [c d] not - a [] popop 1 - 1 + a [] popop 1 + 1 - a [c d] Q + a [c d] Q - uncons tuck [first % not] [first / 0] [rest [not] [popop 1]] [ifte] + uncons tuck [first % not] [first / 0] [rest [not] [popop 1]] [ifte] I finally sat down with a piece of paper and blocked it out. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -302,36 +302,36 @@ candidates and return the result or zero: :: - n [...] G - --------------- - result + n [...] G + --------------- + result - n [...] G - --------------- - 0 + n [...] G + --------------- + 0 -It's a recursive function that conditionally executes the recursive part +It’s a recursive function that conditionally executes the recursive part of its recursive branch :: - [Pg] [E] [R1 [Pi] [T]] [ifte] genrec + [Pg] [E] [R1 [Pi] [T]] [ifte] genrec The recursive branch is the else-part of the inner ``ifte``: :: - G == [Pg] [E] [R1 [Pi] [T]] [ifte] genrec - == [Pg] [E] [R1 [Pi] [T] [G] ifte] ifte + G == [Pg] [E] [R1 [Pi] [T]] [ifte] genrec + == [Pg] [E] [R1 [Pi] [T] [G] ifte] ifte But this is in hindsight. Going forward I derived: :: - G == [first % not] - [first /] - [rest [not] [popop 0]] - [ifte] genrec + G == [first % not] + [first /] + [rest [not] [popop 0]] + [ifte] genrec The predicate detects if the ``n`` can be evenly divided by the ``first`` item in the list. If so, the then-part returns the result. @@ -339,8 +339,8 @@ Otherwise, we have: :: - n [m ...] rest [not] [popop 0] [G] ifte - n [...] [not] [popop 0] [G] ifte + n [m ...] rest [not] [popop 0] [G] ifte + n [...] [not] [popop 0] [G] ifte This ``ifte`` guards against empty sequences and returns zero in that case, otherwise it executes ``G``. @@ -350,16 +350,16 @@ case, otherwise it executes ``G``. define('G == [first % not] [first /] [rest [not] [popop 0]] [ifte] genrec') Now we need a word that uses ``G`` on each (head, tail) pair of a -sequence until it finds a (non-zero) result. It's going to be designed +sequence until it finds a (non-zero) result. It’s going to be designed to work on a stack that has some candidate ``n``, a sequence of possible divisors, and a result that is zero to signal to continue (a non-zero value implies that it is the discovered result): :: - n [...] p find-result - --------------------------- - result + n [...] p find-result + --------------------------- + result It applies ``G`` using ``nullary`` because if it fails with one candidate it needs the list to get the next one (the list is otherwise @@ -367,20 +367,20 @@ consumed by ``G``.) :: - find-result == [0 >] [roll> popop] [roll< popop uncons [G] nullary] primrec + find-result == [0 >] [roll> popop] [roll< popop uncons [G] nullary] primrec - n [...] p [0 >] [roll> popop] [roll< popop uncons [G] nullary] primrec + n [...] p [0 >] [roll> popop] [roll< popop uncons [G] nullary] primrec The base-case is trivial, return the (non-zero) result. The recursive -branch... +branch… :: - n [...] p roll< popop uncons [G] nullary find-result - [...] p n popop uncons [G] nullary find-result - [...] uncons [G] nullary find-result - m [..] [G] nullary find-result - m [..] p find-result + n [...] p roll< popop uncons [G] nullary find-result + [...] p n popop uncons [G] nullary find-result + [...] uncons [G] nullary find-result + m [..] [G] nullary find-result + m [..] p find-result The puzzle states that the input is well-formed, meaning that we can expect a result before the row sequence empties and so do not need to @@ -402,7 +402,7 @@ guard the ``uncons``. In order to get the thing started, we need to ``sort`` the list in descending order, then prime the ``find-result`` function with a dummy -candidate value and zero ("continue") flag. +candidate value and zero (“continue”) flag. .. code:: ipython2 diff --git a/docs/Advent_of_Code_2017_December_3rd.rst b/docs/Advent_of_Code_2017_December_3rd.rst index 4bfeeaa..2a69d85 100644 --- a/docs/Advent_of_Code_2017_December_3rd.rst +++ b/docs/Advent_of_Code_2017_December_3rd.rst @@ -13,11 +13,11 @@ example, the first few squares are allocated like this: :: - 17 16 15 14 13 - 18 5 4 3 12 - 19 6 1 2 11 - 20 7 8 9 10 - 21 22 23---> ... + 17 16 15 14 13 + 18 5 4 3 12 + 19 6 1 2 11 + 20 7 8 9 10 + 21 22 23---> ... While this is very space-efficient (no squares are skipped), requested data must be carried back to square 1 (the location of the only access @@ -27,7 +27,7 @@ Distance between the location of the data and square 1. For example: -- Data from square 1 is carried 0 steps, since it's at the access port. +- Data from square 1 is carried 0 steps, since it’s at the access port. - Data from square 12 is carried 3 steps, such as: down, left, left. - Data from square 23 is carried only 2 steps: up twice. - Data from square 1024 must be carried 31 steps. @@ -39,8 +39,8 @@ Analysis ~~~~~~~~ I freely admit that I worked out the program I wanted to write using -graph paper and some Python doodles. There's no point in trying to write -a Joy program until I'm sure I understand the problem well enough. +graph paper and some Python doodles. There’s no point in trying to write +a Joy program until I’m sure I understand the problem well enough. The first thing I did was to write a column of numbers from 1 to n (32 as it happens) and next to them the desired output number, to look for @@ -48,61 +48,61 @@ patterns directly: :: - 1 0 - 2 1 - 3 2 - 4 1 - 5 2 - 6 1 - 7 2 - 8 1 - 9 2 - 10 3 - 11 2 - 12 3 - 13 4 - 14 3 - 15 2 - 16 3 - 17 4 - 18 3 - 19 2 - 20 3 - 21 4 - 22 3 - 23 2 - 24 3 - 25 4 - 26 5 - 27 4 - 28 3 - 29 4 - 30 5 - 31 6 - 32 5 + 1 0 + 2 1 + 3 2 + 4 1 + 5 2 + 6 1 + 7 2 + 8 1 + 9 2 + 10 3 + 11 2 + 12 3 + 13 4 + 14 3 + 15 2 + 16 3 + 17 4 + 18 3 + 19 2 + 20 3 + 21 4 + 22 3 + 23 2 + 24 3 + 25 4 + 26 5 + 27 4 + 28 3 + 29 4 + 30 5 + 31 6 + 32 5 -There are four groups repeating for a given "rank", then the pattern +There are four groups repeating for a given “rank”, then the pattern enlarges and four groups repeat again, etc. :: - 1 2 - 3 2 3 4 - 5 4 3 4 5 6 - 7 6 5 4 5 6 7 8 - 9 8 7 6 5 6 7 8 9 10 + 1 2 + 3 2 3 4 + 5 4 3 4 5 6 + 7 6 5 4 5 6 7 8 + 9 8 7 6 5 6 7 8 9 10 Four of this pyramid interlock to tile the plane extending from the -initial "1" square. +initial “1” square. :: - 2 3 | 4 5 | 6 7 | 8 9 - 10 11 12 13|14 15 16 17|18 19 20 21|22 23 24 25 + 2 3 | 4 5 | 6 7 | 8 9 + 10 11 12 13|14 15 16 17|18 19 20 21|22 23 24 25 And so on. -We can figure out the pattern for a row of the pyramid at a given "rank" +We can figure out the pattern for a row of the pyramid at a given “rank” :math:`k`: :math:`2k - 1, 2k - 2, ..., k, k + 1, k + 2, ..., 2k` @@ -115,15 +115,15 @@ This shows that the series consists at each place of :math:`k` plus some number that begins at :math:`k - 1`, decreases to zero, then increases to :math:`k`. Each row has :math:`2k` members. -Let's figure out how, given an index into a row, we can calculate the +Let’s figure out how, given an index into a row, we can calculate the value there. The index will be from 0 to :math:`k - 1`. -Let's look at an example, with :math:`k = 4`: +Let’s look at an example, with :math:`k = 4`: :: - 0 1 2 3 4 5 6 7 - 7 6 5 4 5 6 7 8 + 0 1 2 3 4 5 6 7 + 7 6 5 4 5 6 7 8 .. code:: ipython2 @@ -156,7 +156,7 @@ value: 3 2 1 0 1 2 3 4 -Great, now add :math:`k`... +Great, now add :math:`k`\ … .. code:: ipython2 @@ -190,7 +190,7 @@ index: 9 8 7 6 5 6 7 8 9 10 -(I'm leaving out details of how I figured this all out and just giving +(I’m leaving out details of how I figured this all out and just giving the relevent bits. It took a little while to zero in of the aspects of the pattern that were important for the task.) @@ -209,8 +209,8 @@ initial square we have: :math:`corner_k = 1 + \sum_{n=1}^k 8n` -I'm not mathematically sophisticated enough to turn this directly into a -formula (but Sympy is, see below.) I'm going to write a simple Python +I’m not mathematically sophisticated enough to turn this directly into a +formula (but Sympy is, see below.) I’m going to write a simple Python function to iterate and search: .. code:: ipython2 @@ -420,7 +420,7 @@ Sympy to the Rescue Find the rank for large numbers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Using e.g. Sympy we can find the rank directly by solving for the roots +Using e.g. Sympy we can find the rank directly by solving for the roots of an equation. For large numbers this will (eventually) be faster than iterating as ``rank_and_offset()`` does. @@ -459,7 +459,7 @@ We want: -We can write a function to solve for :math:`k` given some :math:`n`... +We can write a function to solve for :math:`k` given some :math:`n`\ … .. code:: ipython2 @@ -472,7 +472,7 @@ about the larger one we use ``max()`` to select it. It will generally not be a nice integer (unless :math:`n` is the number of an end-corner of a rank) so we take the ``floor()`` and add 1 to get the integer rank of :math:`n`. (Taking the ``ceiling()`` gives off-by-one errors on the -rank boundaries. I don't know why. I'm basically like a monkey doing +rank boundaries. I don’t know why. I’m basically like a monkey doing math here.) =-D It gives correct answers: @@ -534,7 +534,7 @@ And it runs much faster (at least for large numbers): After finding the rank you would still have to find the actual value of -the rank's first corner and subtract it (plus 2) from the number and +the rank’s first corner and subtract it (plus 2) from the number and compute the offset as above and then the final output, but this overhead is partially shared by the other method, and overshadowed by the time it (the other iterative method) would take for really big inputs. @@ -542,8 +542,8 @@ is partially shared by the other method, and overshadowed by the time it The fun thing to do here would be to graph the actual runtime of both methods against each other to find the trade-off point. -It took me a second to realize I could do this... -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +It took me a second to realize I could do this… +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Sympy is a *symbolic* math library, and it supports symbolic manipulation of equations. I can put in :math:`y` (instead of a value) @@ -558,7 +558,7 @@ and ask it to solve for :math:`k`. g, f = solve(E - y, k) The equation is quadratic so there are two roots, we are interested in -the greater one... +the greater one… .. code:: ipython2 @@ -622,7 +622,7 @@ to get a Python function that calculates the rank directly. 50 4 -It's pretty fast. +It’s pretty fast. .. code:: ipython2 @@ -685,7 +685,7 @@ compute the offset into a pyramid row. (Note the sneaky way the sign changes from :math:`k(k + 1)` to :math:`k(k - 1)`. This is because we want to subract the -:math:`(k - 1)`\ th rank's total places (its own and those of lesser +:math:`(k - 1)`\ th rank’s total places (its own and those of lesser rank) from our :math:`n` of rank :math:`k`. Substituting :math:`k - 1` for :math:`k` in :math:`k(k + 1)` gives :math:`(k - 1)(k - 1 + 1)`, which of course simplifies to :math:`k(k - 1)`.) @@ -797,17 +797,17 @@ this code in Joy. ;-) :: - n rank_of - --------------- - k + n rank_of + --------------- + k The translation is straightforward. :: - int(floor(sqrt(n - 1) / 2 - 0.5) + 1) + int(floor(sqrt(n - 1) / 2 - 0.5) + 1) - rank_of == -- sqrt 2 / 0.5 - floor ++ + rank_of == -- sqrt 2 / 0.5 - floor ++ .. code:: ipython2 @@ -818,36 +818,36 @@ The translation is straightforward. :: - n k offset_of - ------------------- - i + n k offset_of + ------------------- + i - (n - 2 + 4 * k * (k - 1)) % (2 * k) + (n - 2 + 4 * k * (k - 1)) % (2 * k) -A little tricky... +A little tricky… :: - n k dup 2 * - n k k 2 * - n k k*2 [Q] dip % - n k Q k*2 % + n k dup 2 * + n k k 2 * + n k k*2 [Q] dip % + n k Q k*2 % - n k dup -- - n k k -- - n k k-1 4 * * 2 + - - n k*k-1*4 2 + - - n k*k-1*4+2 - - n-k*k-1*4+2 + n k dup -- + n k k -- + n k k-1 4 * * 2 + - + n k*k-1*4 2 + - + n k*k-1*4+2 - + n-k*k-1*4+2 - n-k*k-1*4+2 k*2 % - n-k*k-1*4+2%k*2 + n-k*k-1*4+2 k*2 % + n-k*k-1*4+2%k*2 Ergo: :: - offset_of == dup 2 * [dup -- 4 * * 2 + -] dip % + offset_of == dup 2 * [dup -- 4 * * 2 + -] dip % .. code:: ipython2 @@ -858,18 +858,18 @@ Ergo: :: - k i row_value - ------------------- - n + k i row_value + ------------------- + n - abs(i - (k - 1)) + k + abs(i - (k - 1)) + k - k i over -- - abs + - k i k -- - abs + - k i k-1 - abs + - k i-k-1 abs + - k |i-k-1| + - k+|i-k-1| + k i over -- - abs + + k i k -- - abs + + k i k-1 - abs + + k i-k-1 abs + + k |i-k-1| + + k+|i-k-1| .. code:: ipython2 @@ -880,16 +880,16 @@ Ergo: :: - n aoc2017.3 - ----------------- - m + n aoc2017.3 + ----------------- + m - n dup rank_of - n k [offset_of] dupdip - n k offset_of k - i k swap row_value - k i row_value - m + n dup rank_of + n k [offset_of] dupdip + n k offset_of k + i k swap row_value + k i row_value + m .. code:: ipython2 @@ -965,8 +965,8 @@ Ergo: :: - rank_of == -- sqrt 2 / 0.5 - floor ++ - offset_of == dup 2 * [dup -- 4 * * 2 + -] dip % - row_value == over -- - abs + + rank_of == -- sqrt 2 / 0.5 - floor ++ + offset_of == dup 2 * [dup -- 4 * * 2 + -] dip % + row_value == over -- - abs + - aoc2017.3 == dup rank_of [offset_of] dupdip swap row_value + aoc2017.3 == dup rank_of [offset_of] dupdip swap row_value diff --git a/docs/Advent_of_Code_2017_December_4th.rst b/docs/Advent_of_Code_2017_December_4th.rst index de4d40b..e73f4ec 100644 --- a/docs/Advent_of_Code_2017_December_4th.rst +++ b/docs/Advent_of_Code_2017_December_4th.rst @@ -12,30 +12,30 @@ For example: - aa bb cc dd aa is not valid - the word aa appears more than once. - aa bb cc dd aaa is valid - aa and aaa count as different words. -The system's full passphrase list is available as your puzzle input. How +The system’s full passphrase list is available as your puzzle input. How many passphrases are valid? .. code:: ipython2 from notebook_preamble import J, V, define -I'll assume the input is a Joy sequence of sequences of integers. +I’ll assume the input is a Joy sequence of sequences of integers. :: - [[5 1 9 5] - [7 5 4 3] - [2 4 6 8]] + [[5 1 9 5] + [7 5 4 3] + [2 4 6 8]] So, obviously, the initial form will be a ``step`` function: :: - AoC2017.4 == 0 swap [F +] step + AoC2017.4 == 0 swap [F +] step :: - F == [size] [unique size] cleave = + F == [size] [unique size] cleave = The ``step_zero`` combinator includes the ``0 swap`` that would normally open one of these definitions: @@ -53,7 +53,7 @@ open one of these definitions: :: - AoC2017.4 == [F +] step_zero + AoC2017.4 == [F +] step_zero .. code:: ipython2 diff --git a/docs/Advent_of_Code_2017_December_5th.rst b/docs/Advent_of_Code_2017_December_5th.rst index 19c4758..0ce6cff 100644 --- a/docs/Advent_of_Code_2017_December_5th.rst +++ b/docs/Advent_of_Code_2017_December_5th.rst @@ -4,7 +4,7 @@ Advent of Code 2017 December 5th ------------ -...a list of the offsets for each jump. Jumps are relative: -1 moves to +…a list of the offsets for each jump. Jumps are relative: -1 moves to the previous instruction, and 2 skips the next one. Start at the first instruction in the list. The goal is to follow the jumps until one leads outside the list. @@ -18,13 +18,13 @@ For example, consider the following list of jump offsets: :: - 0 - 3 - 0 - 1 - -3 + 0 + 3 + 0 + 1 + -3 -Positive jumps ("forward") move downward; negative jumps move upward. +Positive jumps (“forward”) move downward; negative jumps move upward. For legibility in this example, these offset values will be written all on one line, with the current instruction marked in parentheses. The following steps would be taken before an exit is found: @@ -35,14 +35,24 @@ following steps would be taken before an exit is found: - - (1) 3 0 1 -3 - jump with offset 0 (that is, don't jump at all). + (1) 3 0 1 -3 - jump with offset 0 (that is, don’t jump at all). Fortunately, the instruction is then incremented to 1. -- 2 (3) 0 1 -3 - step forward because of the instruction we just - modified. The first instruction is incremented again, now to 2. -- 2 4 0 1 (-3) - jump all the way to the end; leave a 4 behind. -- 2 (4) 0 1 -2 - go back to where we just were; increment -3 to -2. -- 2 5 0 1 -2 - jump 4 steps forward, escaping the maze. +- :: + + 2 (3) 0 1 -3 - step forward because of the instruction we just modified. The first instruction is incremented again, now to 2. + +- :: + + 2 4 0 1 (-3) - jump all the way to the end; leave a 4 behind. + +- :: + + 2 (4) 0 1 -2 - go back to where we just were; increment -3 to -2. + +- :: + + 2 5 0 1 -2 - jump 4 steps forward, escaping the maze. In this example, the exit is reached in 5 steps. @@ -51,7 +61,7 @@ How many steps does it take to reach the exit? Breakdown --------- -For now, I'm going to assume a starting state with the size of the +For now, I’m going to assume a starting state with the size of the sequence pre-computed. We need it to define the exit condition and it is a trivial preamble to generate it. We then need and ``index`` and a ``step-count``, which are both initially zero. Then we have the sequence @@ -59,66 +69,66 @@ itself, and some recursive function ``F`` that does the work. :: - size index step-count [...] F - ----------------------------------- - step-count + size index step-count [...] F + ----------------------------------- + step-count - F == [P] [T] [R1] [R2] genrec + F == [P] [T] [R1] [R2] genrec Later on I was thinking about it and the Forth heuristic came to mind, to wit: four things on the stack are kind of much. Immediately I -realized that the size properly belongs in the predicate of ``F``! D'oh! +realized that the size properly belongs in the predicate of ``F``! D’oh! :: - index step-count [...] F - ------------------------------ - step-count + index step-count [...] F + ------------------------------ + step-count -So, let's start by nailing down the predicate: +So, let’s start by nailing down the predicate: :: - F == [P] [T] [R1] [R2] genrec - == [P] [T] [R1 [F] R2] ifte + F == [P] [T] [R1] [R2] genrec + == [P] [T] [R1 [F] R2] ifte - 0 0 [0 3 0 1 -3] popop 5 >= + 0 0 [0 3 0 1 -3] popop 5 >= - P == popop 5 >= + P == popop 5 >= Now we need the else-part: :: - index step-count [0 3 0 1 -3] roll< popop + index step-count [0 3 0 1 -3] roll< popop - E == roll< popop + E == roll< popop Last but not least, the recursive branch :: - 0 0 [0 3 0 1 -3] R1 [F] R2 + 0 0 [0 3 0 1 -3] R1 [F] R2 The ``R1`` function has a big job: :: - R1 == get the value at index - increment the value at the index - add the value gotten to the index - increment the step count + R1 == get the value at index + increment the value at the index + add the value gotten to the index + increment the step count The only tricky thing there is incrementing an integer in the sequence. Joy sequences are not particularly good for random access. We could encode the list of jump offsets in a big integer and use math to do the -processing for a good speed-up, but it still wouldn't beat the -performance of e.g. a mutable array. This is just one of those places -where "plain vanilla" Joypy doesn't shine (in default performance. The +processing for a good speed-up, but it still wouldn’t beat the +performance of e.g. a mutable array. This is just one of those places +where “plain vanilla” Joypy doesn’t shine (in default performance. The legendary *Sufficiently-Smart Compiler* would of course rewrite this -function to use an array "under the hood".) +function to use an array “under the hood”.) -In the meantime, I'm going to write a primitive function that just does +In the meantime, I’m going to write a primitive function that just does what we need. .. code:: ipython2 @@ -166,52 +176,52 @@ get the value at index :: - 3 0 [0 1 2 3 4] [roll< at] nullary - 3 0 [0 1 2 n 4] n + 3 0 [0 1 2 3 4] [roll< at] nullary + 3 0 [0 1 2 n 4] n increment the value at the index ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: - 3 0 [0 1 2 n 4] n [Q] dip - 3 0 [0 1 2 n 4] Q n - 3 0 [0 1 2 n 4] [popd incr_at] unary n - 3 0 [0 1 2 n+1 4] n + 3 0 [0 1 2 n 4] n [Q] dip + 3 0 [0 1 2 n 4] Q n + 3 0 [0 1 2 n 4] [popd incr_at] unary n + 3 0 [0 1 2 n+1 4] n add the value gotten to the index ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: - 3 0 [0 1 2 n+1 4] n [+] cons dipd - 3 0 [0 1 2 n+1 4] [n +] dipd - 3 n + 0 [0 1 2 n+1 4] - 3+n 0 [0 1 2 n+1 4] + 3 0 [0 1 2 n+1 4] n [+] cons dipd + 3 0 [0 1 2 n+1 4] [n +] dipd + 3 n + 0 [0 1 2 n+1 4] + 3+n 0 [0 1 2 n+1 4] increment the step count ~~~~~~~~~~~~~~~~~~~~~~~~ :: - 3+n 0 [0 1 2 n+1 4] [++] dip - 3+n 1 [0 1 2 n+1 4] + 3+n 0 [0 1 2 n+1 4] [++] dip + 3+n 1 [0 1 2 n+1 4] -All together now... -~~~~~~~~~~~~~~~~~~~ +All together now… +~~~~~~~~~~~~~~~~~ :: - get_value == [roll< at] nullary - incr_value == [[popd incr_at] unary] dip - add_value == [+] cons dipd - incr_step_count == [++] dip + get_value == [roll< at] nullary + incr_value == [[popd incr_at] unary] dip + add_value == [+] cons dipd + incr_step_count == [++] dip - R1 == get_value incr_value add_value incr_step_count + R1 == get_value incr_value add_value incr_step_count - F == [P] [T] [R1] primrec + F == [P] [T] [R1] primrec - F == [popop !size! >=] [roll< pop] [get_value incr_value add_value incr_step_count] primrec + F == [popop !size! >=] [roll< pop] [get_value incr_value add_value incr_step_count] primrec .. code:: ipython2 @@ -250,9 +260,9 @@ We want to go from this to this: :: - [...] AoC2017.5.preamble - ------------------------------ - 0 0 [...] [popop n >=] + [...] AoC2017.5.preamble + ------------------------------ + 0 0 [...] [popop n >=] Where ``n`` is the size of the sequence. @@ -260,23 +270,23 @@ The first part is obviously ``0 0 roll<``, then ``dup size``: :: - [...] 0 0 roll< dup size - 0 0 [...] n + [...] 0 0 roll< dup size + 0 0 [...] n Then: :: - 0 0 [...] n [>=] cons [popop] swoncat + 0 0 [...] n [>=] cons [popop] swoncat So: :: - init-index-and-step-count == 0 0 roll< - prepare-predicate == dup size [>=] cons [popop] swoncat + init-index-and-step-count == 0 0 roll< + prepare-predicate == dup size [>=] cons [popop] swoncat - AoC2017.5.preamble == init-index-and-step-count prepare-predicate + AoC2017.5.preamble == init-index-and-step-count prepare-predicate .. code:: ipython2 @@ -303,21 +313,21 @@ So: :: - AoC2017.5 == AoC2017.5.preamble [roll< popop] [AoC2017.5.0] primrec + AoC2017.5 == AoC2017.5.preamble [roll< popop] [AoC2017.5.0] primrec - AoC2017.5.0 == get_value incr_value add_value incr_step_count - AoC2017.5.preamble == init-index-and-step-count prepare-predicate + AoC2017.5.0 == get_value incr_value add_value incr_step_count + AoC2017.5.preamble == init-index-and-step-count prepare-predicate - get_value == [roll< at] nullary - incr_value == [[popd incr_at] unary] dip - add_value == [+] cons dipd - incr_step_count == [++] dip + get_value == [roll< at] nullary + incr_value == [[popd incr_at] unary] dip + add_value == [+] cons dipd + incr_step_count == [++] dip - init-index-and-step-count == 0 0 roll< - prepare-predicate == dup size [>=] cons [popop] swoncat + init-index-and-step-count == 0 0 roll< + prepare-predicate == dup size [>=] cons [popop] swoncat This is by far the largest program I have yet written in Joy. Even with the ``incr_at`` function it is still a bear. There may be an arrangement of the parameters that would permit more elegant definitions, but it -still wouldn't be as efficient as something written in assembly, C, or +still wouldn’t be as efficient as something written in assembly, C, or even Python. diff --git a/docs/Advent_of_Code_2017_December_6th.rst b/docs/Advent_of_Code_2017_December_6th.rst index a71b9cf..d10fb37 100644 --- a/docs/Advent_of_Code_2017_December_6th.rst +++ b/docs/Advent_of_Code_2017_December_6th.rst @@ -6,7 +6,7 @@ December 6th :: - [0 2 7 0] dup max + [0 2 7 0] dup max .. code:: ipython2 @@ -77,16 +77,16 @@ December 6th -1 -Starting at ``index`` distribute ``count`` "blocks" to the "banks" in +Starting at ``index`` distribute ``count`` “blocks” to the “banks” in the sequence. :: - [...] count index distribute - ---------------------------- - [...] + [...] count index distribute + ---------------------------- + [...] -This seems like it would be a PITA to implement in Joypy... +This seems like it would be a PITA to implement in Joypy… .. code:: ipython2 @@ -168,25 +168,25 @@ This seems like it would be a PITA to implement in Joypy... [2 4 1 2] -Recalling "Generator Programs" +Recalling “Generator Programs” ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: - [a F] x - [a F] a F + [a F] x + [a F] a F - [a F] a swap [C] dip rest cons - a [a F] [C] dip rest cons - a C [a F] rest cons - a C [F] cons + [a F] a swap [C] dip rest cons + a [a F] [C] dip rest cons + a C [a F] rest cons + a C [F] cons - w/ C == dup G + w/ C == dup G - a dup G [F] cons - a a G [F] cons + a dup G [F] cons + a a G [F] cons - w/ G == dup max [index_of] nullary distribute + w/ G == dup max [index_of] nullary distribute .. code:: ipython2 @@ -217,53 +217,53 @@ First draft: :: - [] [GEN] x [pop index_of 0 >=] [pop size --] [[swons] dip x] primrec + [] [GEN] x [pop index_of 0 >=] [pop size --] [[swons] dip x] primrec (?) :: - [] [GEN] x [pop index_of 0 >=] [pop size --] [[swons] dip x] primrec - [] [...] [GEN] [pop index_of 0 >=] [pop size --] [[swons] dip x] primrec - [] [...] [GEN] pop index_of 0 >= - [] [...] index_of 0 >= - -1 0 >= - False + [] [GEN] x [pop index_of 0 >=] [pop size --] [[swons] dip x] primrec + [] [...] [GEN] [pop index_of 0 >=] [pop size --] [[swons] dip x] primrec + [] [...] [GEN] pop index_of 0 >= + [] [...] index_of 0 >= + -1 0 >= + False Base case :: - [] [...] [GEN] [pop index_of 0 >=] [pop size --] [[swons] dip x] primrec - [] [...] [GEN] pop size -- - [] [...] size -- - [] [...] size -- + [] [...] [GEN] [pop index_of 0 >=] [pop size --] [[swons] dip x] primrec + [] [...] [GEN] pop size -- + [] [...] size -- + [] [...] size -- A mistake, ``popop`` and no need for ``--`` :: - [] [...] [GEN] popop size - [] size - n + [] [...] [GEN] popop size + [] size + n Recursive case :: - [] [...] [GEN] [pop index_of 0 >=] [popop size] [[swons] dip x] primrec - [] [...] [GEN] [swons] dip x F - [] [...] swons [GEN] x F - [[...]] [GEN] x F - [[...]] [...] [GEN] F + [] [...] [GEN] [pop index_of 0 >=] [popop size] [[swons] dip x] primrec + [] [...] [GEN] [swons] dip x F + [] [...] swons [GEN] x F + [[...]] [GEN] x F + [[...]] [...] [GEN] F - [[...]] [...] [GEN] F + [[...]] [...] [GEN] F What have we learned? :: - F == [pop index_of 0 >=] [popop size] [[swons] dip x] primrec + F == [pop index_of 0 >=] [popop size] [[swons] dip x] primrec .. code:: ipython2 diff --git a/docs/Compiling_Joy.rst b/docs/Compiling_Joy.rst index e3ab753..b216c56 100644 --- a/docs/Compiling_Joy.rst +++ b/docs/Compiling_Joy.rst @@ -9,7 +9,7 @@ Given a Joy program like: :: - sqr == dup mul + sqr == dup mul .. code:: ipython2 @@ -58,7 +58,7 @@ The simplest thing would be to compose the functions from the library: 529 . -It's simple to write a function to emit this kind of crude "compiled" +It’s simple to write a function to emit this kind of crude “compiled” code. .. code:: ipython2 @@ -96,7 +96,7 @@ But what about literals? :: - quoted == [unit] dip + quoted == [unit] dip .. code:: ipython2 @@ -126,10 +126,10 @@ Compiling Yin Functions Call-chaining results in code that does too much work. For functions that operate on stacks and only rearrange values, what I like to call -"Yin Functions", we can do better. +“Yin Functions”, we can do better. -We can infer the stack effects of these functions (or "expressions" or -"programs") automatically, and the stack effects completely define the +We can infer the stack effects of these functions (or “expressions” or +“programs”) automatically, and the stack effects completely define the semantics of the functions, so we can directly write out a two-line Python function for them. This is already implemented in the ``joy.utils.types.compile_()`` function. @@ -162,7 +162,7 @@ loop. source = compile_('foo', stack_effects[0]) All Yin functions can be described in Python as a tuple-unpacking (or -"-destructuring") of the stack datastructure followed by building up the +“-destructuring”) of the stack datastructure followed by building up the new stack structure. .. code:: ipython2 @@ -205,16 +205,16 @@ new stack structure. Compiling from Stack Effects ---------------------------- -There are times when you're deriving a Joy program when you have a stack +There are times when you’re deriving a Joy program when you have a stack effect for a Yin function and you need to define it. For example, in the Ordered Binary Trees notebook there is a point where we must derive a function ``Ee``: :: - [key old_value left right] new_value key [Tree-add] Ee - ------------------------------------------------------------ - [key new_value left right] + [key old_value left right] new_value key [Tree-add] Ee + ------------------------------------------------------------ + [key new_value left right] While it is not hard to come up with this function manually, there is no necessity. This function can be defined (in Python) directly from its @@ -222,11 +222,11 @@ stack effect: :: - [a b c d] e a [f] Ee - -------------------------- - [a e c d] + [a b c d] e a [f] Ee + -------------------------- + [a e c d] -(I haven't yet implemented a simple interface for this yet. What follow +(I haven’t yet implemented a simple interface for this yet. What follow is an exploration of how to do it.) .. code:: ipython2 @@ -373,7 +373,7 @@ Now we can omit ``a3`` and ``a4`` if we like: stack_effect = eval('(((a1, (a2, s1)), (a5, (a6, (a7, s0)))), ((a1, (a5, s1)), s0))', tv) The ``right`` and ``left`` parts of the ordered binary tree node are -subsumed in the tail of the node's stack/list. +subsumed in the tail of the node’s stack/list. .. code:: ipython2 @@ -404,7 +404,7 @@ subsumed in the tail of the node's stack/list. return ((a1, (a5, s1)), s0) -Oops! The input stack is backwards... +Oops! The input stack is backwards… .. code:: ipython2 @@ -443,9 +443,9 @@ Compare: :: - [key old_value left right] new_value key [Tree-add] Ee - ------------------------------------------------------------ - [key new_value left right] + [key old_value left right] new_value key [Tree-add] Ee + ------------------------------------------------------------ + [key new_value left right] .. code:: ipython2 @@ -510,7 +510,7 @@ Then we would want something like this: -How about... +How about… .. code:: ipython2 @@ -561,7 +561,7 @@ How about... Compiling Yin~Yang Functions ---------------------------- -First, we need a source of Python identifiers. I'm going to reuse +First, we need a source of Python identifiers. I’m going to reuse ``Symbol`` class for this. .. code:: ipython2 @@ -579,7 +579,7 @@ First, we need a source of Python identifiers. I'm going to reuse names = _names().next Now we need an object that represents a Yang function that accepts two -args and return one result (we'll implement other kinds a little later.) +args and return one result (we’ll implement other kinds a little later.) .. code:: ipython2 @@ -594,7 +594,7 @@ args and return one result (we'll implement other kinds a little later.) code.append(('call', out, self.name, (in0, in1))) return (out, stack), expression, code -A crude "interpreter" that translates expressions of args and Yin and +A crude “interpreter” that translates expressions of args and Yin and Yang functions into a kind of simple dataflow graph. .. code:: ipython2 @@ -676,7 +676,7 @@ Something to convert the graph into Python code. ''' % (name, code_gen(I((), expression, []))) -A few functions to try it with... +A few functions to try it with… .. code:: ipython2 @@ -706,7 +706,7 @@ A few functions to try it with... def import_yin(): -... and there we are. +… and there we are. .. code:: ipython2 diff --git a/docs/Correcet_Programming.rst b/docs/Correcet_Programming.rst index e2fbf8a..aa57a4d 100644 --- a/docs/Correcet_Programming.rst +++ b/docs/Correcet_Programming.rst @@ -14,8 +14,8 @@ Expressions ○ SAT Solver ○ A Model of Computation Introduction ============ -In 1969 George Spencer-Brown (GSB) published `"Laws of -Form" `__ which presented a +In 1969 George Spencer-Brown (GSB) published `“Laws of +Form” `__ which presented a logical system based on a single action, a distinction, that is both an operation and a value. This notebook describes a Python implementation that mimics the Laws of Form notation and uses it to develop a model of @@ -31,21 +31,21 @@ Arithmetic :: - (()) = - ()() = () + (()) = + ()() = () Calculus ^^^^^^^^ :: - A((B)) = AB - A() = () - A(AB) = A(B) + A((B)) = AB + A() = () + A(AB) = A(B) I call these three laws the **Bricken Basis** after `William Bricken `__ who figured out that the third law is -complete with the other two. GSB had the first two laws and "Each Way" +complete with the other two. GSB had the first two laws and “Each Way” as the basis. (TODO: Find and include the references for all this.) (If anything here is unclear read `The Markable @@ -56,8 +56,8 @@ Python Sets and Strings as Laws of Form Calculus Expressions ------------------------------------------------------------ We can use data structures made solely out of Python ``frozenset`` and -string objects to represent the forms of the Laws of Form notation. I'm -going to use the terms "expression" and "form" interchangably in this +string objects to represent the forms of the Laws of Form notation. I’m +going to use the terms “expression” and “form” interchangably in this document. .. code:: ipython2 @@ -167,7 +167,7 @@ Order is irrelevant, again due to ``frozenset``. -It's prefectly okay to create forms out of other forms (not just +It’s prefectly okay to create forms out of other forms (not just strings.) .. code:: ipython2 @@ -266,7 +266,7 @@ Once the forms have been rendered to pure arithmetic we can use the return any(not void(i) for i in form) The ``void()`` function returns a Boolean value (Python ``True`` or -``False``), for convenience let's write a function that returns the Mark +``False``), for convenience let’s write a function that returns the Mark or Void value of a form. .. code:: ipython2 @@ -334,7 +334,7 @@ can evaluate an expression containing those names and compute its value. -This is a bit hard to read, so let's define a helper function to convert +This is a bit hard to read, so let’s define a helper function to convert an environment to a string format. .. code:: ipython2 @@ -373,7 +373,7 @@ just like a list of the eight three-bit binary numbers. Reify the Forms with Each Meaning --------------------------------- -Let's pick one of the expressions and iterate through the environments +Let’s pick one of the expressions and iterate through the environments showing the result of reifying that expression in that environment. .. code:: ipython2 @@ -402,7 +402,7 @@ showing the result of reifying that expression in that environment. Truth Table ----------- -Let's render the above as a `Truth +Let’s render the above as a `Truth Table `__. .. code:: ipython2 @@ -437,17 +437,17 @@ Table `__. This makes it clear that *each expression in Laws of Form calculus is describing a digital Boolean circuit*. The names are its inputs and its -Void/Mark value is its output. Each boundary is a `multi-input **NOR** +Void/Mark value is its output. Each boundary is a `multi-input NOR gate `__, known as the Peirce arrow or Quine dagger (See `Sheffer stroke `__ and `NOR gate `__.) Instead of two Boolean values there is only one value and non-existance. -Let's build Circuits +Let’s build Circuits ==================== -In order to work with expressions as digital circuits, let's define some +In order to work with expressions as digital circuits, let’s define some helper functions that will create logic circuits out of simpler forms. The names of the functions below reflect the choice of Mark as Boolean ``True`` but this is `just a convention <#Appendix:-Duals>`__. @@ -502,7 +502,7 @@ Some examples: ((((((((b) c) ((c) b)))) a) (((((b) c) ((c) b))) (a)))) -And let's rewrite the ``truth_table_3()`` function to make it work for +And let’s rewrite the ``truth_table_3()`` function to make it work for any number of variables. .. code:: ipython2 @@ -678,27 +678,27 @@ This is a `brute-force `__ `SAT `__ `solver `__ -that doesn't even bother to stop once it's found a solution. +that doesn’t even bother to stop once it’s found a solution. Expressions from Truth Tables ----------------------------- Sometimes we will have a function for which we know the behavior (truth table) but not an expression and we want the expression. For example, -imagine that we didn't just create the expression for this table: +imagine that we didn’t just create the expression for this table: :: - a b c | Value - ---------+------ - | - () | - () | - () () | () - () | - () () | () - () () | () - () () () | + a b c | Value + ---------+------ + | + () | + () | + () () | () + () | + () () | () + () () | () + () () () | Each Row can be Represented as an Expression ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -708,17 +708,17 @@ each row can be represented as an expression. :: - ⟶ ( a b c ) - () ⟶ ( a b (c)) - () ⟶ ( a (b) c ) - () () ⟶ ( a (b) (c)) - () ⟶ ((a) b c ) - () () ⟶ ((a) b (c)) - () () ⟶ ((a) (b) c ) - () () () ⟶ ((a) (b) (c)) + ⟶ ( a b c ) + () ⟶ ( a b (c)) + () ⟶ ( a (b) c ) + () () ⟶ ( a (b) (c)) + () ⟶ ((a) b c ) + () () ⟶ ((a) b (c)) + () () ⟶ ((a) (b) c ) + () () () ⟶ ((a) (b) (c)) Each of the above expressions will be true (Mark-valued) for only one -possible combination of the three input variables. For example, let's +possible combination of the three input variables. For example, let’s look at the sixth expression above: .. code:: ipython2 @@ -743,26 +743,26 @@ look at the sixth expression above: To make an expression that is Mark-valued for just certain rows of the -table, pick those rows' expressions, +table, pick those rows’ expressions, :: - () () | ( a (b) (c)) - () () | ((a) b (c)) - () () | ((a) (b) c ) + () () | ( a (b) (c)) + () () | ((a) b (c)) + () () | ((a) (b) c ) And write them down as terms in an **OR** expression: :: - E = (a(b)(c)) ((a)b(c)) ((a)(b)c) + E = (a(b)(c)) ((a)b(c)) ((a)(b)c) In conventional notation this is called `Disjunctive normal form `__: :: - E = (¬a ∧ b ∧ c) ∨ (a ∧ ¬b ∧ c) ∨ (a ∧ b ∧ ¬c) + E = (¬a ∧ b ∧ c) ∨ (a ∧ ¬b ∧ c) ∨ (a ∧ b ∧ ¬c) Here it is in action: @@ -800,13 +800,13 @@ E1 that has the same truth table, in other words: :: - ((((((a) (b)) ((b) (c)) ((c) (a))))) ((((a) (b) (c))))) + ((((((a) (b)) ((b) (c)) ((c) (a))))) ((((a) (b) (c))))) equals :: - (((a (b) (c)) ((a) b (c)) ((a) (b) c))) + (((a (b) (c)) ((a) b (c)) ((a) (b) c))) We can demonstrate this equivalence by evaluating the expression formed by ``eqiv()`` from these two. @@ -844,23 +844,23 @@ that the expression is a **tautology**. `Half-Bit Adder `__ ------------------------------------------------------------------------------------- -If you have two binary digits ("bits") and you are interested in the +If you have two binary digits (“bits”) and you are interested in the (binary) sum of these digits you will need two circuits, one for the -"ones place" and one for the "twos place" or "carry bit". +“ones place” and one for the “twos place” or “carry bit”. Consider: :: - a b | c s - ----+---- - 0 0 | 0 0 - 0 1 | 0 1 - 1 0 | 0 1 - 1 1 | 1 0 + a b | c s + ----+---- + 0 0 | 0 0 + 0 1 | 0 1 + 1 0 | 0 1 + 1 1 | 1 0 -Treating each output column ('c' for carry, 's' for sum) as a single -expression, it's easy to see that the carry bit is just **AND** and the +Treating each output column (‘c’ for carry, ‘s’ for sum) as a single +expression, it’s easy to see that the carry bit is just **AND** and the sum bit is just **XOR** of the two input bits. .. code:: ipython2 @@ -911,36 +911,36 @@ together and a carry bit from the previous addition: :: - a b Cin Sum Cout - 0 0 0 | 0 0 - 0 0 1 | 1 0 - 0 1 0 | 1 0 - 0 1 1 | 0 1 - 1 0 0 | 1 0 - 1 0 1 | 0 1 - 1 1 0 | 0 1 - 1 1 1 | 1 1 + a b Cin Sum Cout + 0 0 0 | 0 0 + 0 0 1 | 1 0 + 0 1 0 | 1 0 + 0 1 1 | 0 1 + 1 0 0 | 1 0 + 1 0 1 | 0 1 + 1 1 0 | 0 1 + 1 1 1 | 1 1 Looking back at our table of three-variable expressions: :: - ⟶ ( a b c ) - () ⟶ ( a b (c)) - () ⟶ ( a (b) c ) - () () ⟶ ( a (b) (c)) - () ⟶ ((a) b c ) - () () ⟶ ((a) b (c)) - () () ⟶ ((a) (b) c ) - () () () ⟶ ((a) (b) (c)) + ⟶ ( a b c ) + () ⟶ ( a b (c)) + () ⟶ ( a (b) c ) + () () ⟶ ( a (b) (c)) + () ⟶ ((a) b c ) + () () ⟶ ((a) b (c)) + () () ⟶ ((a) (b) c ) + () () () ⟶ ((a) (b) (c)) We can easily determine expressions for sum and carry: :: - Sum = (a b (c)) (a (b) c) ((a) b c) ((a) (b) (c)) + Sum = (a b (c)) (a (b) c) ((a) b c) ((a) (b) (c)) - Cout = (a (b) (c)) ((a) b (c)) ((a) (b) c) ((a) (b) (c)) + Cout = (a (b) (c)) ((a) b (c)) ((a) (b) c) ((a) (b) (c)) .. code:: ipython2 @@ -985,7 +985,7 @@ We can easily determine expressions for sum and carry: () () () | () -Let's make a ``full_bit_adder()`` function that can define new +Let’s make a ``full_bit_adder()`` function that can define new expressions in terms of variables (or expressions) passed into it. .. code:: ipython2 @@ -1033,8 +1033,8 @@ article `__: :: - S = A ⊕ B ⊕ C - Cout = (A ⋅ B) + (Cin ⋅ (A ⊕ B)) + S = A ⊕ B ⊕ C + Cout = (A ⋅ B) + (Cin ⋅ (A ⊕ B)) .. code:: ipython2 @@ -1122,11 +1122,11 @@ rules of the calculus automatically: :: - A((B)) = AB - A() = () - A(AB) = A(B) + A((B)) = AB + A() = () + A(AB) = A(B) -I'm going to specify the behaviour of the desired function in a +I’m going to specify the behaviour of the desired function in a unittest. .. code:: ipython2 @@ -1136,7 +1136,7 @@ unittest. Three Easy Cases ~~~~~~~~~~~~~~~~ -Let's deal with three easy cases first: string, the Mark, and the Void. +Let’s deal with three easy cases first: string, the Mark, and the Void. The ``simplify()`` function should just return them unchanged. .. code:: ipython2 @@ -1216,7 +1216,7 @@ Doubly-Wrapped Forms ~~~~~~~~~~~~~~~~~~~~ So far, so good. But what about ``((a))``? This should be returned as -just ``a``. And ``((a b))`` should remain ``((a b))`` because we can't +just ``a``. And ``((a b))`` should remain ``((a b))`` because we can’t represent just ``a b`` as a single Python object, so we have to retain the outer pair of containers to hold them without inverting the Mark/Void value (if we just used one container.) @@ -1330,7 +1330,7 @@ Does it work for ``(((a))) = (a)`` and ``((((a)))) = a`` and so on? Unwrapping Inner Forms ~~~~~~~~~~~~~~~~~~~~~~ -But now let's trick our function, it can't handle +But now let’s trick our function, it can’t handle ``(a ((b c))) = (a b c)`` yet. This is going to require an auxiliary helper function that is similar to ``simplify()`` but that yields terms into an outer context. @@ -1639,7 +1639,7 @@ So we have ``(()) = --`` and ``()A = ()`` what about ``A(AB) = A(B)``? TODO set up `Hypothesis `__ to generate test -cases... +cases… .. code:: ipython2 @@ -1658,14 +1658,14 @@ cases... OK -`Using "Each-Way" to Simplify Forms `__ +`Using “Each-Way” to Simplify Forms `__ ------------------------------------------------------------------------------------- -GSB called this "Each-Way": +GSB called this “Each-Way”: :: - a = ((a b) (a (b))) + a = ((a b) (a (b))) .. code:: ipython2 @@ -1683,8 +1683,8 @@ GSB called this "Each-Way": () () | () -The form says, "if b then a else a". I'll come back to the -interpretation of "Each-Way" as an ``if-then-else`` statement later. +The form says, “if b then a else a”. I’ll come back to the +interpretation of “Each-Way” as an ``if-then-else`` statement later. The thing to note here is that the value for ``a`` can be a whole expression which appears twice in the new form: once next to ``b`` and @@ -1695,20 +1695,20 @@ next to it :: - b (...(b c (d ...))) - b (...( c (d ...))) + b (...(b c (d ...))) + b (...( c (d ...))) and in the second case we can change any occurances of ``b`` to the Mark. :: - (b)(...(b c (d ...))) - (b)((b)(b c (d ...))) - (b)(...(b (b) c (d ...))) - (b)(...(b ( ) c (d ...))) - (b)(...( ( ) )) - (b)(... ) + (b)(...(b c (d ...))) + (b)((b)(b c (d ...))) + (b)(...(b (b) c (d ...))) + (b)(...(b ( ) c (d ...))) + (b)(...( ( ) )) + (b)(... ) We can send ``(b)`` into the form until it reaches and ``b``, at which point ``b(b)`` becomes ``()`` and sweeps out any siblings rendering its @@ -2004,7 +2004,7 @@ each. Try the following cells with both versions of the ``Sum`` and (((((b) a) (b)) c) ((c) a b)) -Let's redefine the ``full_bit_adder()`` function with the smallest +Let’s redefine the ``full_bit_adder()`` function with the smallest version of each above. .. code:: ipython2 @@ -2103,8 +2103,8 @@ other two. `Davis–Putnam–Logemann–Loveland (DPLL) algorithm `__ SAT Solver =============================================================================================================================================================== -This is something of an Interlude, we aren't going to use it below, but -it's too cool to omit mention. +This is something of an Interlude, we aren’t going to use it below, but +it’s too cool to omit mention. We can use the ``simplify()`` function to create a more efficient SAT solver along the lines of the DPLL algorithm. @@ -2113,7 +2113,7 @@ It works by selecting a name from the form, and simplifying the form with that name first as ``Void`` then as ``Mark``, then recursing with the new form and the next name. If the resulting simplified form becomes the ``Mark`` then our choices (of assigning ``Void`` or ``Mark`` to the -names selected so far) constitute a "solution" to the original form. +names selected so far) constitute a “solution” to the original form. That is, if we ``reify()`` the form with the *environment* returned by the ``dpll()`` function the result will be Mark-valued. @@ -2329,7 +2329,7 @@ solutions after the first. {'a': (), 'b': ()} ((((((()) ())) (c)) ((())))) = () -Notice that the reified form still has ``c`` in it but that doesn't +Notice that the reified form still has ``c`` in it but that doesn’t prevent the ``simplify()`` function from reducing the form to the Mark. This should be the case for all solutions generated by the ``dpll_iter()`` function. @@ -2350,11 +2350,11 @@ The form ``(((a5) a5))`` is Mark-valued: :: - (((a5) a5)) - ((( ) a5)) - ((( ) )) - ( ) - () + (((a5) a5)) + ((( ) a5)) + ((( ) )) + ( ) + () .. code:: ipython2 @@ -2377,7 +2377,7 @@ Now back to Circuits Using the Adder Circuits to Add ------------------------------- -In order to keep things tractable I'm going to use just four bits rather +In order to keep things tractable I’m going to use just four bits rather than eight. .. code:: ipython2 @@ -2948,18 +2948,18 @@ arranged to make it (relatively) easy to see the addition. A Model of Computation. ======================= -That was a bit steep, let's formalize it and make it a little easier to +That was a bit steep, let’s formalize it and make it a little easier to work with. -First let's have a *register* of named values: +First let’s have a *register* of named values: .. code:: ipython2 R = {name: Void for name in 'Cin a3 a2 a1 a0 b3 b2 b1 b0 Cout'.split()} -Let's have a *program* of named expressions that give new values when +Let’s have a *program* of named expressions that give new values when evaluated in terms of the current values in **R** (this is just the same -``CIRCUITS``, but feeding back the results into the "b" bits): +``CIRCUITS``, but feeding back the results into the “b” bits): .. code:: ipython2 @@ -2983,7 +2983,7 @@ the program with the current values in the register. rr = make_reify_reducer(register) return {bit: rr(expression) for bit, expression in program.iteritems()} -With all the register values at "zero" (Void) nothing happens. +With all the register values at “zero” (Void) nothing happens. .. code:: ipython2 @@ -3008,7 +3008,7 @@ With all the register values at "zero" (Void) nothing happens. -Let's make a nice display function to inspect our little adder computer. +Let’s make a nice display function to inspect our little adder computer. .. code:: ipython2 @@ -3050,7 +3050,7 @@ Let's make a nice display function to inspect our little adder computer. a: 0 b: 0 Cin: 0 Cout: 0 -Let's set one bit to true (Mark-valued in the chosen convention. We +Let’s set one bit to true (Mark-valued in the chosen convention. We could have Void be true but we would have to form the circuit expressions differently.) @@ -3058,7 +3058,7 @@ expressions differently.) R['a0'] = Mark -Now let's count to twenty. +Now let’s count to twenty. .. code:: ipython2 @@ -3092,7 +3092,7 @@ Now let's count to twenty. a: 1 b: 3 Cin: 0 Cout: 0 -You can see that at the sixteenth step the "Cout" carry bit is true and +You can see that at the sixteenth step the “Cout” carry bit is true and the count cycles back to zero. .. code:: ipython2 @@ -3133,12 +3133,12 @@ the count cycles back to zero. a: 3 b: 9 Cin: 0 Cout: 0 -You can see that the "b" bits are indeed counting by threes: 0, 3, 6, 9, +You can see that the “b” bits are indeed counting by threes: 0, 3, 6, 9, 12, 15 & carry, 2, 5, 8, 11, 14 & carry, 1, 4, 7, 10, 13 & carry, 0, 3, -6, 9, ... +6, 9, … This is my basic model for computation: A register, a program, and a -cycle function. Note that reducing the form on each cycle isn't +cycle function. Note that reducing the form on each cycle isn’t necessary, we can run the cycles and just ``reify()`` without reducing and we get new circuits that define bits in terms of the register values N cycles in the past. @@ -3285,7 +3285,7 @@ Simple One-Dimensional Cellular Automaton A More Efficient Implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Before building larger "computers" I want to switch to a more efficient +Before building larger “computers” I want to switch to a more efficient implementation based on a register as a ``set`` of names that are currently Mark-valued, and a ``set_solve()`` function that evaluates a form in terms of such a ``set``, and assuming all other names are @@ -3334,7 +3334,7 @@ Void-valued. To calculate the new R first collect all the names in R that are not mentioned in P (and so cannot be set to Void by it) then add the names -evaluated by solving P's expressions with the marks in R. +evaluated by solving P’s expressions with the marks in R. .. code:: ipython2 @@ -3489,8 +3489,8 @@ evaluated by solving P's expressions with the marks in R. return i return inner -Each-Way as If... Then... -~~~~~~~~~~~~~~~~~~~~~~~~~ +Each-Way as If… Then… +~~~~~~~~~~~~~~~~~~~~~ .. code:: ipython2 @@ -3528,21 +3528,21 @@ but if ``a`` is Void-valued the value of the whole form is that of :: - w/ a = () + w/ a = () - ((( a) b) ( a c)) - (((()) b) (() c)) - (( b) (() )) - (( b) ) - b + ((( a) b) ( a c)) + (((()) b) (() c)) + (( b) (() )) + (( b) ) + b - w/ a = + w/ a = - (((a) b) (a c)) - ((( ) b) ( c)) - ((( ) ) ( c)) - ( ( c)) - c + (((a) b) (a c)) + ((( ) b) ( c)) + ((( ) ) ( c)) + ( ( c)) + c Flip-Flops for Memory --------------------- @@ -3572,32 +3572,32 @@ Flip-Flops for Memory () () () | -This is a form that can be used in a circuit to "remember" a value. +This is a form that can be used in a circuit to “remember” a value. :: - w/ r = () + w/ r = () - ((q s) r) - ((q s) ()) - ( ()) + ((q s) r) + ((q s) ()) + ( ()) - w/ s = (), r = ___ + w/ s = (), r = ___ - ((q s) r) - ((q ()) ) - (( ()) ) - ( ) + ((q s) r) + ((q ()) ) + (( ()) ) + ( ) - w/ s = ___, r = ___ + w/ s = ___, r = ___ - ((q s) r) - ((q ) ) - q + ((q s) r) + ((q ) ) + q If both are Void then the form is just ``q``, if ``r`` is Mark then the form is Void, otherwise if ``s`` is Mark the form becomes Mark. This is -called a "flip-flop" circuit, and it comprises a simple machine to +called a “flip-flop” circuit, and it comprises a simple machine to remember one bit. Consider a simple computer: @@ -3700,8 +3700,8 @@ Consider a simple computer: You can see that ``q`` is stable unless ``s`` or ``r`` set or reset it. -Using Flip-Flops and If...Then...Else... to make RAM -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Using Flip-Flops and If…Then…Else… to make RAM +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ We can use the system we have developed so far to build addressable RAM. @@ -3717,7 +3717,7 @@ We can use the system we have developed so far to build addressable RAM. P = {} -We'll assume a single ``WRITE`` bit that sets a RAM location determined +We’ll assume a single ``WRITE`` bit that sets a RAM location determined by the ``ADDR`` sub-register to the contents of the ``DATA`` sub-register. @@ -4039,47 +4039,47 @@ w/ A = () :: - A(AB) = A(B) - ()(()B) = ()(B) - () = () + A(AB) = A(B) + ()(()B) = ()(B) + () = () w/ A = :: - A(AB) = A(B) - (B) = (B) + A(AB) = A(B) + (B) = (B) Be aware of the recursive nature of this rule: :: - A(...(...(A B))) - A(.A.(...(A B))) - A(.A.(.A.(A B))) - A(.A.(.A.( B))) - A(.A.(...( B))) - A(...(...( B))) + A(...(...(A B))) + A(.A.(...(A B))) + A(.A.(.A.(A B))) + A(.A.(.A.( B))) + A(.A.(...( B))) + A(...(...( B))) There is this too: :: - (A)(...(...(... A B))) - (A)((A)(...(... A B))) - (A)((A)((A)(... A B))) - (A)((A)((A)((A) A B))) - (A)((A)((A)(( ) A B))) - (A)((A)(...(( ) ))) - (A)(...(... )) + (A)(...(...(... A B))) + (A)((A)(...(... A B))) + (A)((A)((A)(... A B))) + (A)((A)((A)((A) A B))) + (A)((A)((A)(( ) A B))) + (A)((A)(...(( ) ))) + (A)(...(... )) Summarized: :: - (A)(...(...(... A ))) - (A)(...(...(... () ))) - (A)(...(... )) + (A)(...(...(... A ))) + (A)(...(...(... () ))) + (A)(...(... )) Appendix: Reduce String Expressions by Substitution --------------------------------------------------- @@ -4195,7 +4195,7 @@ terms of each other. Note that ``void()`` uses ``any()`` while ``mark()`` uses ``all()``. These functions implement a depth-first search. If we used versions of ``any()`` and ``all()`` that evaluated their arguments in parallel ``void()`` could return after the ``True`` -result while ``mark()`` depends on all terms's results so its runtime +result while ``mark()`` depends on all terms’s results so its runtime will be bound by term with the greatest runtime. .. code:: ipython2 @@ -4243,22 +4243,22 @@ Consider: :: - (A ∧ ¬B) ∨ (C ∧ D) + (A ∧ ¬B) ∨ (C ∧ D) -(This reads "(A and not B) or (C and D)" in case you have a hard time +(This reads “(A and not B) or (C and D)” in case you have a hard time remembering what the symbols mean like I do.) If we choose Mark to be true then the form is: :: - ((A) B) ((C)(D)) + ((A) B) ((C)(D)) If we choose Void to be true then the form is: :: - ((A (B)) (C D)) + ((A (B)) (C D)) As I said above, the notation works the same way either way, so once the translation is made you can forget about the Boolean true/false and just @@ -4273,32 +4273,32 @@ original statement: :: - ¬((¬A ∨ B) ∧ (¬C ∨ ¬D)) + ¬((¬A ∨ B) ∧ (¬C ∨ ¬D)) If we choose Mark to be true then the form is: :: - (( ((A) B) ((C)(D)) )) + (( ((A) B) ((C)(D)) )) The outer pair of containers can be deleted leaving the same form as above: :: - ((A) B) ((C)(D)) + ((A) B) ((C)(D)) Likewise, if we choose Void to be true then the form is: :: - ((((A)) (B)) (((C)) ((D)))) + ((((A)) (B)) (((C)) ((D)))) Again, A((B)) => AB reduces this form to the same one above: :: - ((A (B)) (C D)) + ((A (B)) (C D)) In the Laws of Form there are no De Morgan Dual statements. If you translate a logic statement and its dual into Laws of Form notation they @@ -4351,7 +4351,7 @@ Misc. Junk # pp.pprint(dict(Counter(yield_variables_of(E)))) # print '------' -Rather than manually calling ``standard_form()`` let's define a function +Rather than manually calling ``standard_form()`` let’s define a function that reduces a form to a (hopefully) smaller equivalent form by going through all the variables in the form and using ``standard_form()`` with each. Along with clean and unwrap we can drive an expression to a fixed @@ -4441,7 +4441,7 @@ It would be useful and fun to write a simple search algorithm that tried different ways to reduce a form to see if it could find particulaly compact versions. -Let's generate the expressions for the next two output bits, and the +Let’s generate the expressions for the next two output bits, and the carry bit. The ``sum3`` bit expression is pretty big. @@ -4450,7 +4450,7 @@ The ``sum3`` bit expression is pretty big. sum3 -But it's only about 1/9th of size of the previous version (which was +But it’s only about 1/9th of size of the previous version (which was 9261.) .. code:: ipython2 @@ -4463,13 +4463,13 @@ But it's only about 1/9th of size of the previous version (which was -Let's simplify the first one manually just for fun: +Let’s simplify the first one manually just for fun: :: - (((((())) (())) ((())))) - (( ) ) ( ) - ( ) + (((((())) (())) ((())))) + (( ) ) ( ) + ( ) Sure enough, it reduces to Mark after just a few applications of the rule ``(()) = __`` (the underscores indicates the absence of any value, @@ -4478,9 +4478,9 @@ original expression: :: - ((((a)b)(c))) - (( ) )( ) - ( ) + ((((a)b)(c))) + (( ) )( ) + ( ) .. code:: ipython2 @@ -4552,7 +4552,7 @@ expression. Once was enough (we should consider adding a call to ``simplify()`` in the ``full_bit_adder()`` function.) -Let's try using ``each_way()`` with the most common names in the form. +Let’s try using ``each_way()`` with the most common names in the form. .. code:: ipython2 diff --git a/docs/Derivatives_of_Regular_Expressions.rst b/docs/Derivatives_of_Regular_Expressions.rst index 1aa2cf1..29dc9fb 100644 --- a/docs/Derivatives_of_Regular_Expressions.rst +++ b/docs/Derivatives_of_Regular_Expressions.rst @@ -1,69 +1,69 @@ ∂RE === -Brzozowski's Derivatives of Regular Expressions +Brzozowski’s 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 +I’m 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 I’m 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. +Let’s 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... -------------------- +Let’s 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 you’ll 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 1’s and not ending in 01 nor composed of all 1’s.” .. 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 `__.) You'll see that only +`Dot from Graphviz `__.) You’ll 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: +There’s 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`` 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 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 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 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 arrow’s 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`` isn’t. .. 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)* diff --git a/docs/Generator_Programs.rst b/docs/Generator_Programs.rst index 8186159..55e1679 100644 --- a/docs/Generator_Programs.rst +++ b/docs/Generator_Programs.rst @@ -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: +Let’s 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: +Let’s 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 you’ll 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 +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 +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 @@ -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``: +Let’s 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 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. @@ -503,18 +502,18 @@ and sums the even ones. 4613732 -Here's the collected program definitions: +Here’s 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. diff --git a/docs/Hylo-,_Ana-,_Cata-,_and_Para-morphisms_-_Recursion_Combinators.rst b/docs/Hylo-,_Ana-,_Cata-,_and_Para-morphisms_-_Recursion_Combinators.rst index 1947ed8..5189887 100644 --- a/docs/Hylo-,_Ana-,_Cata-,_and_Para-morphisms_-_Recursion_Combinators.rst +++ b/docs/Hylo-,_Ana-,_Cata-,_and_Para-morphisms_-_Recursion_Combinators.rst @@ -1,5 +1,5 @@ -Cf. `"Bananas, Lenses, & Barbed -Wire" `__ +Cf. `“Bananas, Lenses, & Barbed +Wire” `__ `Hylomorphism `__ ==================================================================================== @@ -13,8 +13,8 @@ means of: - A combiner ``F :: (B, B) -> B`` - A predicate ``P :: A -> Bool`` to detect the base case - A base case value ``c :: B`` -- 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. @@ -37,7 +37,7 @@ code. Finding `Triangular Numbers `__ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -As a concrete example let's use a function that, given a positive +As a concrete example let’s use a function that, given a positive integer, returns the sum of all positive integers less than that one. (In this case the types A and B are both ``int``.) ### With ``range()`` and ``sum()`` @@ -110,7 +110,7 @@ As a hylomorphism If you were to run the above code in a debugger and check out the call stack you would find that the variable ``b`` in each call to ``H()`` is storing the intermediate values as ``H()`` recurses. This is what was -meant by "call stack in the form of a cons list". +meant by “call stack in the form of a cons list”. Joy Preamble ~~~~~~~~~~~~ @@ -127,7 +127,7 @@ hylomorphism combinator ``H`` from constituent parts. :: - H == c [F] [P] [G] hylomorphism + H == c [F] [P] [G] 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 @@ -136,37 +136,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] [pop c] [G] [dip F] genrec + H == [P] [pop c] [G] [dip F] genrec Derivation of ``hylomorphism`` ------------------------------ @@ -176,10 +176,10 @@ arguments out of the pieces given to the ``hylomorphism`` combinator. :: - 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 Working in reverse: - Use ``swoncat`` twice to decouple ``[c]`` and ``[F]``. - Use ``unit`` to dequote ``c``. - Use ``dipd`` to untangle @@ -190,7 +190,7 @@ 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 The order of parameters is different than the one we started with but that hardly matters, you can rearrange them or just supply them in the @@ -198,7 +198,7 @@ expected order. :: - [P] c [G] [F] hylomorphism == H + [P] c [G] [F] hylomorphism == H .. code:: ipython2 @@ -369,39 +369,39 @@ An anamorphism can be defined as a hylomorphism that uses ``[]`` for :: - [P] [G] anamorphism == [P] [] [G] [swons] hylomorphism == A + [P] [G] anamorphism == [P] [] [G] [swons] hylomorphism == A This allows us to define an anamorphism combinator in terms of the hylomorphism combinator. :: - [] swap [swons] hylomorphism == anamorphism + [] swap [swons] hylomorphism == anamorphism -Partial evaluation gives us a "pre-cooked" form. +Partial evaluation gives us a “pre-cooked” form. :: - [P] [G] . anamorphism - [P] [G] . [] swap [swons] hylomorphism - [P] [G] [] . swap [swons] hylomorphism - [P] [] [G] . [swons] hylomorphism - [P] [] [G] [swons] . hylomorphism - [P] [] [G] [swons] . [unit [pop] swoncat] dipd [dip] swoncat genrec - [P] [] [G] [swons] [unit [pop] swoncat] . dipd [dip] swoncat genrec - [P] [] . unit [pop] swoncat [G] [swons] [dip] swoncat genrec - [P] [[]] [pop] . swoncat [G] [swons] [dip] swoncat genrec - [P] [pop []] [G] [swons] [dip] . swoncat genrec + [P] [G] . anamorphism + [P] [G] . [] swap [swons] hylomorphism + [P] [G] [] . swap [swons] hylomorphism + [P] [] [G] . [swons] hylomorphism + [P] [] [G] [swons] . hylomorphism + [P] [] [G] [swons] . [unit [pop] swoncat] dipd [dip] swoncat genrec + [P] [] [G] [swons] [unit [pop] swoncat] . dipd [dip] swoncat genrec + [P] [] . unit [pop] swoncat [G] [swons] [dip] swoncat genrec + [P] [[]] [pop] . swoncat [G] [swons] [dip] swoncat genrec + [P] [pop []] [G] [swons] [dip] . swoncat genrec - [P] [pop []] [G] [dip swons] genrec + [P] [pop []] [G] [dip swons] genrec (We could also have just substituted for ``c`` and ``F`` in the definition of ``H``.) :: - H == [P] [pop c ] [G] [dip F ] genrec - A == [P] [pop []] [G] [dip swons] genrec + H == [P] [pop c ] [G] [dip F ] genrec + A == [P] [pop []] [G] [dip swons] genrec The partial evaluation is overkill in this case but it serves as a reminder that this sort of program specialization can, in many cases, be @@ -411,20 +411,20 @@ Untangle ``[G]`` from ``[pop []]`` using ``swap``. :: - [P] [G] [pop []] swap [dip swons] genrec + [P] [G] [pop []] swap [dip swons] genrec All of the arguments to ``anamorphism`` are to the left, so we have a definition for it. :: - anamorphism == [pop []] swap [dip swons] genrec + anamorphism == [pop []] swap [dip swons] genrec An example of an anamorphism is the range function. :: - range == [0 <=] [1 - dup] anamorphism + range == [0 <=] [1 - dup] anamorphism Catamorphism ============ @@ -434,48 +434,48 @@ A catamorphism can be defined as a hylomorphism that uses :: - c [F] catamorphism == [[] =] c [uncons swap] [F] hylomorphism == C + c [F] catamorphism == [[] =] c [uncons swap] [F] hylomorphism == C This allows us to define a ``catamorphism`` combinator in terms of the ``hylomorphism`` combinator. :: - [[] =] roll> [uncons swap] swap hylomorphism == catamorphism + [[] =] roll> [uncons swap] swap hylomorphism == catamorphism -Partial evaluation doesn't help much. +Partial evaluation doesn’t help much. :: - c [F] . catamorphism - c [F] . [[] =] roll> [uncons swap] swap hylomorphism - c [F] [[] =] . roll> [uncons swap] swap hylomorphism - [[] =] c [F] [uncons swap] . swap hylomorphism - [[] =] c [uncons swap] [F] . hylomorphism - [[] =] c [uncons swap] [F] [unit [pop] swoncat] . dipd [dip] swoncat genrec - [[] =] c . unit [pop] swoncat [uncons swap] [F] [dip] swoncat genrec - [[] =] [c] [pop] . swoncat [uncons swap] [F] [dip] swoncat genrec - [[] =] [pop c] [uncons swap] [F] [dip] . swoncat genrec - [[] =] [pop c] [uncons swap] [dip F] genrec + c [F] . catamorphism + c [F] . [[] =] roll> [uncons swap] swap hylomorphism + c [F] [[] =] . roll> [uncons swap] swap hylomorphism + [[] =] c [F] [uncons swap] . swap hylomorphism + [[] =] c [uncons swap] [F] . hylomorphism + [[] =] c [uncons swap] [F] [unit [pop] swoncat] . dipd [dip] swoncat genrec + [[] =] c . unit [pop] swoncat [uncons swap] [F] [dip] swoncat genrec + [[] =] [c] [pop] . swoncat [uncons swap] [F] [dip] swoncat genrec + [[] =] [pop c] [uncons swap] [F] [dip] . swoncat genrec + [[] =] [pop c] [uncons swap] [dip F] genrec Because the arguments to catamorphism have to be prepared (unlike the arguments to anamorphism, which only need to be rearranged slightly) -there isn't much point to "pre-cooking" the definition. +there isn’t much point to “pre-cooking” the definition. :: - catamorphism == [[] =] roll> [uncons swap] swap hylomorphism + catamorphism == [[] =] roll> [uncons swap] swap hylomorphism An example of a catamorphism is the sum function. :: - sum == 0 [+] catamorphism + sum == 0 [+] catamorphism -"Fusion Law" for catas (UNFINISHED!!!) +“Fusion Law” for catas (UNFINISHED!!!) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -I'm not sure exactly how to translate the "Fusion Law" for catamorphisms +I’m not sure exactly how to translate the “Fusion Law” for catamorphisms into Joy. I know that a ``map`` composed with a cata can be expressed as a new @@ -483,95 +483,95 @@ cata: :: - [F] map b [B] cata == b [F B] cata + [F] map b [B] cata == b [F B] cata -But this isn't the one described in "Bananas...". That's more like: +But this isn’t the one described in “Bananas…”. That’s more like: A cata composed with some function can be expressed as some other cata: :: - b [B] catamorphism F == c [C] catamorphism + b [B] catamorphism F == c [C] catamorphism Given: :: - b F == c + b F == c - ... + ... - B F == [F] dip C + B F == [F] dip C - ... + ... - b[B]cata F == c[C]cata + b[B]cata F == c[C]cata - F(B(head, tail)) == C(head, F(tail)) + F(B(head, tail)) == C(head, F(tail)) - 1 [2 3] B F 1 [2 3] F C + 1 [2 3] B F 1 [2 3] F C - b F == c - B F == F C + b F == c + B F == F C - b [B] catamorphism F == c [C] catamorphism - b [B] catamorphism F == b F [C] catamorphism + b [B] catamorphism F == c [C] catamorphism + b [B] catamorphism F == b F [C] catamorphism - ... + ... Or maybe, :: - [F] map b [B] cata == c [C] cata ??? + [F] map b [B] cata == c [C] cata ??? - [F] map b [B] cata == b [F B] cata I think this is generally true, unless F consumes stack items - instead of just transforming TOS. Of course, there's always [F] unary. - b [F] unary [[F] unary B] cata + [F] map b [B] cata == b [F B] cata I think this is generally true, unless F consumes stack items + instead of just transforming TOS. Of course, there's always [F] unary. + b [F] unary [[F] unary B] cata - [10 *] map 0 swap [+] step == 0 swap [10 * +] step + [10 *] map 0 swap [+] step == 0 swap [10 * +] step For example: :: - F == 10 * - b == 0 - B == + - c == 0 - C == F + + F == 10 * + b == 0 + B == + + c == 0 + C == F + - b F == c - 0 10 * == 0 + b F == c + 0 10 * == 0 - B F == [F] dip C - + 10 * == [10 *] dip F + - + 10 * == [10 *] dip 10 * + + B F == [F] dip C + + 10 * == [10 *] dip F + + + 10 * == [10 *] dip 10 * + - n m + 10 * == 10(n+m) + n m + 10 * == 10(n+m) - n m [10 *] dip 10 * + - n 10 * m 10 * + - 10n m 10 * + - 10n 10m + - 10n+10m + n m [10 *] dip 10 * + + n 10 * m 10 * + + 10n m 10 * + + 10n 10m + + 10n+10m - 10n+10m = 10(n+m) + 10n+10m = 10(n+m) Ergo: :: - 0 [+] catamorphism 10 * == 0 [10 * +] catamorphism + 0 [+] catamorphism 10 * == 0 [10 * +] catamorphism The ``step`` combinator will usually be better to use than ``catamorphism``. ---------------------------------------------------------------------------- :: - sum == 0 swap [+] step - sum == 0 [+] catamorphism + sum == 0 swap [+] step + sum == 0 [+] catamorphism anamorphism catamorphism == hylomorphism ======================================== @@ -582,26 +582,26 @@ An anamorphism followed by (composed with) a catamorphism is a hylomorphism, with the advantage that the hylomorphism does not create the intermediate list structure. The values are stored in either the call stack, for those implementations that use one, or in the pending -expression ("continuation") for the Joypy interpreter. They still have +expression (“continuation”) for the Joypy interpreter. They still have to be somewhere, converting from an anamorphism and catamorphism to a hylomorphism just prevents using additional storage and doing additional processing. :: - range == [0 <=] [1 - dup] anamorphism - sum == 0 [+] catamorphism + range == [0 <=] [1 - dup] anamorphism + sum == 0 [+] catamorphism - range sum == [0 <=] [1 - dup] anamorphism 0 [+] catamorphism - == [0 <=] 0 [1 - dup] [+] hylomorphism + range sum == [0 <=] [1 - dup] anamorphism 0 [+] catamorphism + == [0 <=] 0 [1 - dup] [+] hylomorphism We can let the ``hylomorphism`` combinator build ``range_sum`` for us or just substitute ourselves. :: - H == [P] [pop c] [G] [dip F] genrec - range_sum == [0 <=] [pop 0] [1 - dup] [dip +] genrec + H == [P] [pop c] [G] [dip F] genrec + range_sum == [0 <=] [pop 0] [1 - dup] [dip +] genrec .. code:: ipython2 @@ -1394,9 +1394,9 @@ A paramorphism ``P :: B -> A`` is a recursion combinator that uses :: - n swap [P] [pop] [[F] dupdip G] primrec + n swap [P] [pop] [[F] dupdip G] primrec -With - ``n :: A`` is the "identity" for ``F`` (like 1 for +With - ``n :: A`` is the “identity” for ``F`` (like 1 for multiplication, 0 for addition) - ``F :: (A, B) -> A`` - ``G :: B -> B`` generates the next ``B`` value. - and lastly ``P :: B -> Bool`` detects the end of the series. @@ -1405,10 +1405,10 @@ For Factorial function (types ``A`` and ``B`` are both integer): :: - n == 1 - F == * - G == -- - P == 1 <= + n == 1 + F == * + G == -- + P == 1 <= .. code:: ipython2 @@ -1418,23 +1418,23 @@ Try it with input 3 (omitting evaluation of predicate): :: - 3 1 swap [1 <=] [pop] [[*] dupdip --] primrec - 1 3 [1 <=] [pop] [[*] dupdip --] primrec + 3 1 swap [1 <=] [pop] [[*] dupdip --] primrec + 1 3 [1 <=] [pop] [[*] dupdip --] primrec - 1 3 [*] dupdip -- - 1 3 * 3 -- - 3 3 -- - 3 2 + 1 3 [*] dupdip -- + 1 3 * 3 -- + 3 3 -- + 3 2 - 3 2 [*] dupdip -- - 3 2 * 2 -- - 6 2 -- - 6 1 + 3 2 [*] dupdip -- + 3 2 * 2 -- + 6 2 -- + 6 1 - 6 1 [1 <=] [pop] [[*] dupdip --] primrec + 6 1 [1 <=] [pop] [[*] dupdip --] primrec - 6 1 pop - 6 + 6 1 pop + 6 .. code:: ipython2 @@ -1451,15 +1451,15 @@ Derive ``paramorphism`` from the form above. :: - n swap [P] [pop] [[F] dupdip G] primrec + n swap [P] [pop] [[F] dupdip G] primrec - n swap [P] [pop] [[F] dupdip G] primrec - n [P] [swap] dip [pop] [[F] dupdip G] primrec - n [P] [[F] dupdip G] [[swap] dip [pop]] dip primrec - n [P] [F] [dupdip G] cons [[swap] dip [pop]] dip primrec - n [P] [F] [G] [dupdip] swoncat cons [[swap] dip [pop]] dip primrec + n swap [P] [pop] [[F] dupdip G] primrec + n [P] [swap] dip [pop] [[F] dupdip G] primrec + n [P] [[F] dupdip G] [[swap] dip [pop]] dip primrec + n [P] [F] [dupdip G] cons [[swap] dip [pop]] dip primrec + n [P] [F] [G] [dupdip] swoncat cons [[swap] dip [pop]] dip primrec - paramorphism == [dupdip] swoncat cons [[swap] dip [pop]] dip primrec + paramorphism == [dupdip] swoncat cons [[swap] dip [pop]] dip primrec .. code:: ipython2 @@ -1479,24 +1479,24 @@ Derive ``paramorphism`` from the form above. ``tails`` ========= -An example of a paramorphism for lists given in the `"Bananas..." +An example of a paramorphism for lists given in the `“Bananas…” paper `__ -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]] Using ``paramorphism`` we would write: :: - n == [] - F == rest swons - G == rest - P == not + n == [] + F == rest swons + G == rest + P == not - tails == [] [not] [rest swons] [rest] paramorphism + tails == [] [not] [rest swons] [rest] paramorphism .. code:: ipython2 @@ -1539,22 +1539,22 @@ Right before the recursion begins we have: :: - [] [1 2 3] [not] [pop] [[rest swons] dupdip rest] primrec + [] [1 2 3] [not] [pop] [[rest swons] dupdip rest] primrec But we might prefer to factor ``rest`` in the quote: :: - [] [1 2 3] [not] [pop] [rest [swons] dupdip] primrec + [] [1 2 3] [not] [pop] [rest [swons] dupdip] primrec -There's no way to do that with the ``paramorphism`` combinator as +There’s no way to do that with the ``paramorphism`` combinator as defined. We would have to write and use a slightly different recursion -combinator that accepted an additional "preprocessor" function ``[H]`` +combinator that accepted an additional “preprocessor” function ``[H]`` and built: :: - n swap [P] [pop] [H [F] dupdip G] primrec + n swap [P] [pop] [H [F] dupdip G] primrec Or just write it out manually. This is yet another place where the *sufficiently smart compiler* will one day automatically refactor the @@ -1564,7 +1564,7 @@ and ``[G]`` for common prefix and extracted it. Patterns of Recursion ===================== -Our story so far... +Our story so far… - A combiner ``F :: (B, B) -> B`` - A predicate ``P :: A -> Bool`` to detect the base case @@ -1575,22 +1575,22 @@ Hylo-, Ana-, Cata- :: - w/ G :: A -> (A, B) + w/ G :: A -> (A, B) - H == [P ] [pop c ] [G ] [dip F ] genrec - A == [P ] [pop []] [G ] [dip swons] genrec - C == [[] =] [pop c ] [uncons swap] [dip F ] genrec + H == [P ] [pop c ] [G ] [dip F ] genrec + A == [P ] [pop []] [G ] [dip swons] genrec + C == [[] =] [pop c ] [uncons swap] [dip F ] genrec Para-, ?-, ?- ~~~~~~~~~~~~~ :: - w/ G :: B -> B + w/ G :: B -> B - P == c swap [P ] [pop] [[F ] dupdip G ] primrec - ? == [] swap [P ] [pop] [[swons] dupdip G ] primrec - ? == c swap [[] =] [pop] [[F ] dupdip uncons swap] primrec + P == c swap [P ] [pop] [[F ] dupdip G ] primrec + ? == [] swap [P ] [pop] [[swons] dupdip G ] primrec + ? == c swap [[] =] [pop] [[F ] dupdip uncons swap] primrec Four Generalizations ==================== @@ -1598,54 +1598,54 @@ Four Generalizations There are at least four kinds of recursive combinator, depending on two choices. The first choice is whether the combiner function 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 +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. +generator’s output. :: - H == [P] [pop c] [G ] [dip F] genrec - H == c swap [P] [pop] [G [F] dip ] [i] genrec - H == [P] [pop c] [ [G] dupdip ] [dip F] genrec - H == c swap [P] [pop] [ [F] dupdip G] [i] genrec + H == [P] [pop c] [G ] [dip F] genrec + H == c swap [P] [pop] [G [F] dip ] [i] genrec + H == [P] [pop c] [ [G] dupdip ] [dip F] genrec + H == c swap [P] [pop] [ [F] dupdip G] [i] genrec Consider: :: - ... a G [H] dip F w/ a G == a' b - ... c a G [F] dip H a G == b a' - ... a [G] dupdip [H] dip F a G == a' - ... c a [F] dupdip G H a G == a' + ... a G [H] dip F w/ a G == a' b + ... c a G [F] dip H a G == b a' + ... a [G] dupdip [H] dip F a G == a' + ... c a [F] dupdip G H a G == a' 1 ~ :: - H == [P] [pop c] [G] [dip F] genrec + H == [P] [pop c] [G] [dip F] genrec Iterate n times. :: - ... a [P] [pop c] [G] [dip F] genrec - ... a G [H] dip F - ... a' b [H] dip F - ... a' H b F - ... a' G [H] dip F b F - ... a'' b [H] dip F b F - ... a'' H b F b F - ... a'' G [H] dip F b F b F - ... a''' b [H] dip F b F b F - ... a''' H b F b F b F - ... a''' pop c b F b F b F - ... c b F b F b F + ... a [P] [pop c] [G] [dip F] genrec + ... a G [H] dip F + ... a' b [H] dip F + ... a' H b F + ... a' G [H] dip F b F + ... a'' b [H] dip F b F + ... a'' H b F b F + ... a'' G [H] dip F b F b F + ... a''' b [H] dip F b F b F + ... a''' H b F b F b F + ... a''' pop c b F b F b F + ... c b F b F b F This form builds up a 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. +continuation “collapses” into the final result. 2 ~ @@ -1658,21 +1658,21 @@ reverse order. :: - H == c swap [P] [pop] [G [F] dip] primrec + H == c swap [P] [pop] [G [F] dip] primrec - ... c a G [F] dip H - ... c b a' [F] dip H - ... c b F a' H - ... c b F a' G [F] dip H - ... c b F b a'' [F] dip H - ... c b F b F a'' H - ... c b F b F a'' G [F] dip H - ... c b F b F b a''' [F] dip H - ... c b F b F b F a''' H - ... c b F b F b F a''' pop - ... c b F b F b F + ... c a G [F] dip H + ... c b a' [F] dip H + ... c b F a' H + ... c b F a' G [F] dip H + ... c b F b a'' [F] dip H + ... c b F b F a'' H + ... c b F b F a'' G [F] dip H + ... c b F b F b a''' [F] dip H + ... c b F b F b F a''' H + ... c b F b F b F a''' pop + ... c b F b F b F -The end line here is the same as for above, but only because we didn't +The end line here is the same as for above, but only because we didn’t evaluate ``F`` when it normally would have been. 3 @@ -1684,22 +1684,22 @@ one item instead of two (the b is instead the duplicate of a.) :: - H == [P] [pop c] [[G] dupdip] [dip F] genrec + H == [P] [pop c] [[G] dupdip] [dip F] genrec - ... a [G] dupdip [H] dip F - ... a G a [H] dip F - ... a' a [H] dip F - ... a' H a F - ... a' [G] dupdip [H] dip F a F - ... a' G a' [H] dip F a F - ... a'' a' [H] dip F a F - ... a'' H a' F a F - ... a'' [G] dupdip [H] dip F a' F a F - ... a'' G a'' [H] dip F a' F a F - ... a''' a'' [H] dip F a' F a F - ... a''' H a'' F a' F a F - ... a''' pop c a'' F a' F a F - ... c a'' F a' F a F + ... a [G] dupdip [H] dip F + ... a G a [H] dip F + ... a' a [H] dip F + ... a' H a F + ... a' [G] dupdip [H] dip F a F + ... a' G a' [H] dip F a F + ... a'' a' [H] dip F a F + ... a'' H a' F a F + ... a'' [G] dupdip [H] dip F a' F a F + ... a'' G a'' [H] dip F a' F a F + ... a''' a'' [H] dip F a' F a F + ... a''' H a'' F a' F a F + ... a''' pop c a'' F a' F a F + ... c a'' F a' F a F 4 ~ @@ -1709,21 +1709,21 @@ and the combiner needs to work on the current item, this is the form: :: - W == c swap [P] [pop] [[F] dupdip G] primrec + W == c swap [P] [pop] [[F] dupdip G] primrec - ... a c swap [P] [pop] [[F] dupdip G] primrec - ... c a [P] [pop] [[F] dupdip G] primrec - ... c a [F] dupdip G W - ... c a F a G W - ... c a F a' W - ... c a F a' [F] dupdip G W - ... c a F a' F a' G W - ... c a F a' F a'' W - ... c a F a' F a'' [F] dupdip G W - ... c a F a' F a'' F a'' G W - ... c a F a' F a'' F a''' W - ... c a F a' F a'' F a''' pop - ... c a F a' F a'' F + ... a c swap [P] [pop] [[F] dupdip G] primrec + ... c a [P] [pop] [[F] dupdip G] primrec + ... c a [F] dupdip G W + ... c a F a G W + ... c a F a' W + ... c a F a' [F] dupdip G W + ... c a F a' F a' G W + ... c a F a' F a'' W + ... c a F a' F a'' [F] dupdip G W + ... c a F a' F a'' F a'' G W + ... c a F a' F a'' F a''' W + ... c a F a' F a'' F a''' pop + ... c a F a' F a'' F Each of the four variations above can be specialized to ana- and catamorphic forms. @@ -1761,7 +1761,7 @@ catamorphic forms. :: - H == [P ] [pop c ] [G ] [dip F ] genrec + H == [P ] [pop c ] [G ] [dip F ] genrec .. code:: ipython2 @@ -2092,15 +2092,15 @@ Appendix - Fun with Symbols :: - |[ (c, F), (G, P) ]| == (|c, F|) • [(G, P)] + |[ (c, F), (G, P) ]| == (|c, F|) • [(G, P)] -`"Bananas, Lenses, & Barbed -Wire" `__ +`“Bananas, Lenses, & Barbed +Wire” `__ :: - (|...|) [(...)] [<...>] + (|...|) [(...)] [<...>] I think they are having slightly too much fun with the symbols. -"Too much is always better than not enough." +“Too much is always better than not enough.” diff --git a/docs/Newton-Raphson.rst b/docs/Newton-Raphson.rst index 8484a45..6fd662b 100644 --- a/docs/Newton-Raphson.rst +++ b/docs/Newton-Raphson.rst @@ -1,10 +1,10 @@ -`Newton's method `__ +`Newton’s method `__ ===================================================================== -Let's use the Newton-Raphson method for finding the root of an equation +Let’s 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 `__ .. 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 we’re 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... +Let’s 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 `__: - 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 diff --git a/docs/Ordered_Binary_Trees.rst b/docs/Ordered_Binary_Trees.rst index 8285262..569d665 100644 --- a/docs/Ordered_Binary_Trees.rst +++ b/docs/Ordered_Binary_Trees.rst @@ -13,26 +13,26 @@ notation `__, is: :: - Tree :: [] | [key value Tree Tree] + Tree :: [] | [key value Tree Tree] That says that a Tree is either the empty quote ``[]`` or a quote with four items: a key, a value, and two Trees representing the left and right branches of the tree. -We're going to derive some recursive functions to work with such +We’re going to derive some recursive functions to work with such datastructures: :: - Tree-add - Tree-delete - Tree-get - Tree-iter - Tree-iter-order + Tree-add + Tree-delete + Tree-get + Tree-iter + Tree-iter-order -Once these functions are defined we have a new "type" to work with, and +Once these functions are defined we have a new “type” to work with, and the Sufficiently Smart Compiler can be modified to use an optimized -implementation under the hood. (Where does the "type" come from? It has +implementation under the hood. (Where does the “type” come from? It has a contingent existence predicated on the disciplined use of these functions on otherwise undistinguished Joy datastructures.) @@ -43,13 +43,13 @@ functions on otherwise undistinguished Joy datastructures.) Adding Nodes to the Tree ------------------------ -Let's consider adding nodes to a Tree structure. +Let’s consider adding nodes to a Tree structure. :: - Tree value key Tree-add - ----------------------------- - Tree′ + Tree value key Tree-add + ----------------------------- + Tree′ Adding to an empty node. ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -59,7 +59,7 @@ If the current node is ``[]`` then you just return :: - Tree-add == [popop not] [[pop] dipd Tree-new] [R0] [R1] genrec + Tree-add == [popop not] [[pop] dipd Tree-new] [R0] [R1] genrec ``Tree-new`` ^^^^^^^^^^^^ @@ -68,24 +68,24 @@ Where ``Tree-new`` is defined as: :: - value key Tree-new - ------------------------ - [key value [] []] + value key Tree-new + ------------------------ + [key value [] []] Example: :: - value key swap [[] []] cons cons - key value [[] []] cons cons - key [value [] []] cons - [key value [] []] + value key swap [[] []] cons cons + key value [[] []] cons cons + key [value [] []] cons + [key value [] []] Definition: :: - Tree-new == swap [[] []] cons cons + Tree-new == swap [[] []] cons cons .. code:: ipython2 @@ -104,7 +104,7 @@ Definition: (As an implementation detail, the ``[[] []]`` literal used in the definition of ``Tree-new`` will be reused to supply the *constant* tail for *all* new nodes produced by it. This is one of those cases where you -get amortized storage "for free" by using `persistent +get amortized storage “for free” by using `persistent datastructures `__. Because the tail, which is ``((), ((), ()))`` in Python, is immutable and embedded in the definition body for ``Tree-new``, all new nodes can @@ -118,50 +118,50 @@ We now have to derive ``R0`` and ``R1``, consider: :: - [key_n value_n left right] value key R0 [Tree-add] R1 + [key_n value_n left right] value key R0 [Tree-add] R1 In this case, there are three possibilites: the key can be greater or -less than or equal to the node's key. In two of those cases we will need +less than or equal to the node’s key. In two of those cases we will need to apply a copy of ``Tree-add``, so ``R0`` is pretty much out of the picture. :: - [R0] == [] + [R0] == [] A predicate to compare keys. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: - [key_n value_n left right] value key [BTree-add] R1 + [key_n value_n left right] value key [BTree-add] R1 -The first thing we need to do is compare the the key we're adding to the +The first thing we need to do is compare the the key we’re adding to the node key and ``branch`` accordingly: :: - [key_n value_n left right] value key [BTree-add] [P] [T] [E] ifte + [key_n value_n left right] value key [BTree-add] [P] [T] [E] ifte That would suggest something like: :: - [key_n value_n left right] value key [BTree-add] P - [key_n value_n left right] value key [BTree-add] pop roll> pop first > - [key_n value_n left right] value key roll> pop first > - key [key_n value_n left right] value roll> pop first > - key key_n > - Boolean + [key_n value_n left right] value key [BTree-add] P + [key_n value_n left right] value key [BTree-add] pop roll> pop first > + [key_n value_n left right] value key roll> pop first > + key [key_n value_n left right] value roll> pop first > + key key_n > + Boolean -Let's abstract the predicate just a little to let us specify the +Let’s abstract the predicate just a little to let us specify the comparison operator: :: - P > == pop roll> pop first > - P < == pop roll> pop first < - P == pop roll> pop first + P > == pop roll> pop first > + P < == pop roll> pop first < + P == pop roll> pop first .. code:: ipython2 @@ -177,7 +177,7 @@ comparison operator: 'new_key' 'old_key' -If the key we're adding is greater than the node's key. +If the key we’re adding is greater than the node’s key. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Here the parentheses are meant to signify that the expression is not @@ -185,62 +185,62 @@ literal, the code in the parentheses is meant to have been evaluated: :: - [key_n value_n left right] value key [Tree-add] T - ------------------------------------------------------- - [key_n value_n left (Tree-add key value right)] + [key_n value_n left right] value key [Tree-add] T + ------------------------------------------------------- + [key_n value_n left (Tree-add key value right)] -So how do we do this? We're going to want to use ``infra`` on some +So how do we do this? We’re going to want to use ``infra`` on some function ``K`` that has the key and value to work with, as well as the quoted copy of ``Tree-add`` to apply somehow. Considering the node as a stack: :: - right left value_n key_n value key [Tree-add] K - ----------------------------------------------------- - right value key Tree-add left value_n key_n + right left value_n key_n value key [Tree-add] K + ----------------------------------------------------- + right value key Tree-add left value_n key_n Pretty easy: :: - right left value_n key_n value key [Tree-add] cons cons dipdd - right left value_n key_n [value key Tree-add] dipdd - right value key Tree-add left value_n key_n + right left value_n key_n value key [Tree-add] cons cons dipdd + right left value_n key_n [value key Tree-add] dipdd + right value key Tree-add left value_n key_n So: :: - K == cons cons dipdd + K == cons cons dipdd Looking at it from the point-of-view of the node as node again: :: - [key_n value_n left right] [value key [Tree-add] K] infra + [key_n value_n left right] [value key [Tree-add] K] infra Expand ``K`` and evaluate a little: :: - [key_n value_n left right] [value key [Tree-add] K] infra - [key_n value_n left right] [value key [Tree-add] cons cons dipdd] infra - [key_n value_n left right] [[value key Tree-add] dipdd] infra + [key_n value_n left right] [value key [Tree-add] K] infra + [key_n value_n left right] [value key [Tree-add] cons cons dipdd] infra + [key_n value_n left right] [[value key Tree-add] dipdd] infra Then, working backwards: :: - [key_n value_n left right] [[value key Tree-add] dipdd] infra - [key_n value_n left right] [value key Tree-add] [dipdd] cons infra - [key_n value_n left right] value key [Tree-add] cons cons [dipdd] cons infra + [key_n value_n left right] [[value key Tree-add] dipdd] infra + [key_n value_n left right] [value key Tree-add] [dipdd] cons infra + [key_n value_n left right] value key [Tree-add] cons cons [dipdd] cons infra And so ``T`` is just: :: - T == cons cons [dipdd] cons infra + T == cons cons [dipdd] cons infra .. code:: ipython2 @@ -256,15 +256,15 @@ And so ``T`` is just: ['old_k' 'old_value' 'left' 'Tree-add' 'new_key' 'new_value' 'right'] -If the key we're adding is less than the node's key. +If the key we’re adding is less than the node’s key. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This is very very similar to the above: :: - [key_n value_n left right] value key [Tree-add] E - [key_n value_n left right] value key [Tree-add] [P <] [Te] [Ee] ifte + [key_n value_n left right] value key [Tree-add] E + [key_n value_n left right] value key [Tree-add] [P <] [Te] [Ee] ifte .. code:: ipython2 @@ -276,7 +276,7 @@ instead of the right, so the only difference is that it must use :: - Te == cons cons [dipd] cons infra + Te == cons cons [dipd] cons infra .. code:: ipython2 @@ -299,26 +299,26 @@ This means we must find: :: - [key old_value left right] new_value key [Tree-add] Ee - ------------------------------------------------------------ - [key new_value left right] + [key old_value left right] new_value key [Tree-add] Ee + ------------------------------------------------------------ + [key new_value left right] This is another easy one: :: - Ee == pop swap roll< rest rest cons cons + Ee == pop swap roll< rest rest cons cons Example: :: - [key old_value left right] new_value key [Tree-add] pop swap roll< rest rest cons cons - [key old_value left right] new_value key swap roll< rest rest cons cons - [key old_value left right] key new_value roll< rest rest cons cons - key new_value [key old_value left right] rest rest cons cons - key new_value [ left right] cons cons - [key new_value left right] + [key old_value left right] new_value key [Tree-add] pop swap roll< rest rest cons cons + [key old_value left right] new_value key swap roll< rest rest cons cons + [key old_value left right] key new_value roll< rest rest cons cons + key new_value [key old_value left right] rest rest cons cons + key new_value [ left right] cons cons + [key new_value left right] .. code:: ipython2 @@ -339,21 +339,21 @@ Now we can define ``Tree-add`` :: - Tree-add == [popop not] [[pop] dipd Tree-new] [] [[P >] [T] [E] ifte] genrec + Tree-add == [popop not] [[pop] dipd Tree-new] [] [[P >] [T] [E] ifte] genrec Putting it all together: :: - Tree-new == swap [[] []] cons cons - P == pop roll> pop first - T == cons cons [dipdd] cons infra - Te == cons cons [dipd] cons infra - Ee == pop swap roll< rest rest cons cons - E == [P <] [Te] [Ee] ifte - R == [P >] [T] [E] ifte + Tree-new == swap [[] []] cons cons + P == pop roll> pop first + T == cons cons [dipdd] cons infra + Te == cons cons [dipd] cons infra + Ee == pop swap roll< rest rest cons cons + E == [P <] [Te] [Ee] ifte + R == [P >] [T] [E] ifte - Tree-add == [popop not] [[pop] dipd Tree-new] [] [R] genrec + Tree-add == [popop not] [[pop] dipd Tree-new] [] [R] genrec .. code:: ipython2 @@ -425,24 +425,24 @@ Examples Interlude: ``cmp`` combinator ----------------------------- -Instead of mucking about with nested ``ifte`` combinators let's use +Instead of mucking about with nested ``ifte`` combinators let’s use ``cmp`` which takes two values and three quoted programs on the stack and runs one of the three depending on the results of comparing the two values: :: - a b [G] [E] [L] cmp - ------------------------- a > b - G + a b [G] [E] [L] cmp + ------------------------- a > b + G - a b [G] [E] [L] cmp - ------------------------- a = b - E + a b [G] [E] [L] cmp + ------------------------- a = b + E - a b [G] [E] [L] cmp - ------------------------- a < b - L + a b [G] [E] [L] cmp + ------------------------- a < b + L .. code:: ipython2 @@ -481,38 +481,38 @@ We need a new non-destructive predicate ``P``: :: - [node_key node_value left right] value key [Tree-add] P - ------------------------------------------------------------------------ - [node_key node_value left right] value key [Tree-add] key node_key + [node_key node_value left right] value key [Tree-add] P + ------------------------------------------------------------------------ + [node_key node_value left right] value key [Tree-add] key node_key -Let's start with ``over`` to get a copy of the key and then apply some +Let’s start with ``over`` to get a copy of the key and then apply some function ``Q`` with the ``nullary`` combinator so it can dig out the node key (by throwing everything else away): :: - P == over [Q] nullary + P == over [Q] nullary - [node_key node_value left right] value key [Tree-add] over [Q] nullary - [node_key node_value left right] value key [Tree-add] key [Q] nullary + [node_key node_value left right] value key [Tree-add] over [Q] nullary + [node_key node_value left right] value key [Tree-add] key [Q] nullary And ``Q`` would be: :: - Q == popop popop first + Q == popop popop first - [node_key node_value left right] value key [Tree-add] key Q - [node_key node_value left right] value key [Tree-add] key popop popop first - [node_key node_value left right] value key popop first - [node_key node_value left right] first - node_key + [node_key node_value left right] value key [Tree-add] key Q + [node_key node_value left right] value key [Tree-add] key popop popop first + [node_key node_value left right] value key popop first + [node_key node_value left right] first + node_key Or just: :: - P == over [popop popop first] nullary + P == over [popop popop first] nullary .. code:: ipython2 @@ -523,23 +523,23 @@ Using ``cmp`` to simplify `our code above at :: - [node_key node_value left right] value key [Tree-add] R1 - [node_key node_value left right] value key [Tree-add] P [T] [E] [Te] cmp + [node_key node_value left right] value key [Tree-add] R1 + [node_key node_value left right] value key [Tree-add] P [T] [E] [Te] cmp The line above becomes one of the three lines below: :: - [node_key node_value left right] value key [Tree-add] T - [node_key node_value left right] value key [Tree-add] E - [node_key node_value left right] value key [Tree-add] Te + [node_key node_value left right] value key [Tree-add] T + [node_key node_value left right] value key [Tree-add] E + [node_key node_value left right] value key [Tree-add] Te The definition is a little longer but, I think, more elegant and easier to understand: :: - Tree-add == [popop not] [[pop] dipd Tree-new] [] [P [T] [Ee] [Te] cmp] genrec + Tree-add == [popop not] [[pop] dipd Tree-new] [] [P [T] [Ee] [Te] cmp] genrec .. code:: ipython2 @@ -558,7 +558,7 @@ to understand: A Function to Traverse this Structure ------------------------------------- -Let's take a crack at writing a function that can recursively iterate or +Let’s take a crack at writing a function that can recursively iterate or traverse these trees. Base case ``[]`` @@ -568,13 +568,13 @@ The stopping predicate just has to detect the empty list: :: - Tree-iter == [not] [E] [R0] [R1] genrec + Tree-iter == [not] [E] [R0] [R1] genrec -And since there's nothing at this node, we just ``pop`` it: +And since there’s nothing at this node, we just ``pop`` it: :: - Tree-iter == [not] [pop] [R0] [R1] genrec + Tree-iter == [not] [pop] [R0] [R1] genrec Node case ``[key value left right]`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -583,14 +583,14 @@ Now we need to figure out ``R0`` and ``R1``: :: - Tree-iter == [not] [pop] [R0] [R1] genrec - == [not] [pop] [R0 [Tree-iter] R1] ifte + Tree-iter == [not] [pop] [R0] [R1] genrec + == [not] [pop] [R0 [Tree-iter] R1] ifte -Let's look at it *in situ*: +Let’s look at it *in situ*: :: - [key value left right] R0 [Tree-iter] R1 + [key value left right] R0 [Tree-iter] R1 Processing the current node. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -600,18 +600,18 @@ node and then ``dip`` on some function to process the copy with it: :: - [key value left right] [F] dupdip [Tree-iter] R1 - [key value left right] F [key value left right] [Tree-iter] R1 + [key value left right] [F] dupdip [Tree-iter] R1 + [key value left right] F [key value left right] [Tree-iter] R1 -For example, if we're getting all the keys ``F`` would be ``first``: +For example, if we’re getting all the keys ``F`` would be ``first``: :: - R0 == [first] dupdip + R0 == [first] dupdip - [key value left right] [first] dupdip [Tree-iter] R1 - [key value left right] first [key value left right] [Tree-iter] R1 - key [key value left right] [Tree-iter] R1 + [key value left right] [first] dupdip [Tree-iter] R1 + [key value left right] first [key value left right] [Tree-iter] R1 + key [key value left right] [Tree-iter] R1 Recur ^^^^^ @@ -622,26 +622,26 @@ with an interesting situation: :: - key [key value left right] [Tree-iter] R1 - key [key value left right] [Tree-iter] [rest rest] dip - key [key value left right] rest rest [Tree-iter] - key [left right] [Tree-iter] + key [key value left right] [Tree-iter] R1 + key [key value left right] [Tree-iter] [rest rest] dip + key [key value left right] rest rest [Tree-iter] + key [left right] [Tree-iter] Hmm, will ``step`` do? :: - key [left right] [Tree-iter] step - key left Tree-iter [right] [Tree-iter] step - key left-keys [right] [Tree-iter] step - key left-keys right Tree-iter - key left-keys right-keys + key [left right] [Tree-iter] step + key left Tree-iter [right] [Tree-iter] step + key left-keys [right] [Tree-iter] step + key left-keys right Tree-iter + key left-keys right-keys Neat. So: :: - R1 == [rest rest] dip step + R1 == [rest rest] dip step Putting it together ~~~~~~~~~~~~~~~~~~~ @@ -650,14 +650,14 @@ We have: :: - Tree-iter == [not] [pop] [[F] dupdip] [[rest rest] dip step] genrec + Tree-iter == [not] [pop] [[F] dupdip] [[rest rest] dip step] genrec When I was reading this over I realized ``rest rest`` could go in ``R0``: :: - Tree-iter == [not] [pop] [[F] dupdip rest rest] [step] genrec + Tree-iter == [not] [pop] [[F] dupdip rest rest] [step] genrec (And ``[step] genrec`` is such a cool and suggestive combinator!) @@ -666,24 +666,24 @@ Parameterizing the ``F`` per-node processing function. :: - [F] Tree-iter - ------------------------------------------------------ - [not] [pop] [[F] dupdip rest rest] [step] genrec + [F] Tree-iter + ------------------------------------------------------ + [not] [pop] [[F] dupdip rest rest] [step] genrec Working backward: :: - [not] [pop] [[F] dupdip rest rest] [step] genrec - [not] [pop] [F] [dupdip rest rest] cons [step] genrec - [F] [not] [pop] roll< [dupdip rest rest] cons [step] genrec + [not] [pop] [[F] dupdip rest rest] [step] genrec + [not] [pop] [F] [dupdip rest rest] cons [step] genrec + [F] [not] [pop] roll< [dupdip rest rest] cons [step] genrec ``Tree-iter`` ~~~~~~~~~~~~~ :: - Tree-iter == [not] [pop] roll< [dupdip rest rest] cons [step] genrec + Tree-iter == [not] [pop] roll< [dupdip rest rest] cons [step] genrec .. code:: ipython2 @@ -726,7 +726,7 @@ Interlude: A Set-like Datastructure ----------------------------------- We can use this to make a set-like datastructure by just setting values -to e.g. 0 and ignoring them. It's set-like in that duplicate items added +to e.g. 0 and ignoring them. It’s set-like in that duplicate items added to it will only occur once within it, and we can query it in `:math:`O(\log_2 N)` `__ time. @@ -782,14 +782,14 @@ the right child. This will allow us to traverse the tree in sort order. :: - Tree-iter-order == [not] [pop] [R0] [R1] genrec + Tree-iter-order == [not] [pop] [R0] [R1] genrec To define ``R0`` and ``R1`` it helps to look at them as they will appear when they run: :: - [key value left right] R0 [BTree-iter-order] R1 + [key value left right] R0 [BTree-iter-order] R1 Process the left child. ~~~~~~~~~~~~~~~~~~~~~~~ @@ -798,38 +798,38 @@ Staring at this for a bit suggests ``dup third`` to start: :: - [key value left right] R0 [Tree-iter-order] R1 - [key value left right] dup third [Tree-iter-order] R1 - [key value left right] left [Tree-iter-order] R1 + [key value left right] R0 [Tree-iter-order] R1 + [key value left right] dup third [Tree-iter-order] R1 + [key value left right] left [Tree-iter-order] R1 Now maybe: :: - [key value left right] left [Tree-iter-order] [cons dip] dupdip - [key value left right] left [Tree-iter-order] cons dip [Tree-iter-order] - [key value left right] [left Tree-iter-order] dip [Tree-iter-order] - left Tree-iter-order [key value left right] [Tree-iter-order] + [key value left right] left [Tree-iter-order] [cons dip] dupdip + [key value left right] left [Tree-iter-order] cons dip [Tree-iter-order] + [key value left right] [left Tree-iter-order] dip [Tree-iter-order] + left Tree-iter-order [key value left right] [Tree-iter-order] Process the current node. ~~~~~~~~~~~~~~~~~~~~~~~~~ -So far, so good. Now we need to process the current node's values: +So far, so good. Now we need to process the current node’s values: :: - left Tree-iter-order [key value left right] [Tree-iter-order] [[F] dupdip] dip - left Tree-iter-order [key value left right] [F] dupdip [Tree-iter-order] - left Tree-iter-order [key value left right] F [key value left right] [Tree-iter-order] + left Tree-iter-order [key value left right] [Tree-iter-order] [[F] dupdip] dip + left Tree-iter-order [key value left right] [F] dupdip [Tree-iter-order] + left Tree-iter-order [key value left right] F [key value left right] [Tree-iter-order] If ``F`` needs items from the stack below the left stuff it should have -``cons``'d them before beginning maybe? For functions like ``first`` it -works fine as-is. +``cons``\ ’d them before beginning maybe? For functions like ``first`` +it works fine as-is. :: - left Tree-iter-order [key value left right] first [key value left right] [Tree-iter-order] - left Tree-iter-order key [key value left right] [Tree-iter-order] + left Tree-iter-order [key value left right] first [key value left right] [Tree-iter-order] + left Tree-iter-order key [key value left right] [Tree-iter-order] Process the right child. ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -838,16 +838,16 @@ First ditch the rest of the node and get the right child: :: - left Tree-iter-order key [key value left right] [Tree-iter-order] [rest rest rest first] dip - left Tree-iter-order key right [Tree-iter-order] + left Tree-iter-order key [key value left right] [Tree-iter-order] [rest rest rest first] dip + left Tree-iter-order key right [Tree-iter-order] Then, of course, we just need ``i`` to run ``Tree-iter-order`` on the right side: :: - left Tree-iter-order key right [Tree-iter-order] i - left Tree-iter-order key right Tree-iter-order + left Tree-iter-order key right [Tree-iter-order] i + left Tree-iter-order key right Tree-iter-order Defining ``Tree-iter-order`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -856,19 +856,19 @@ The result is a little awkward: :: - R1 == [cons dip] dupdip [[F] dupdip] dip [rest rest rest first] dip i + R1 == [cons dip] dupdip [[F] dupdip] dip [rest rest rest first] dip i -Let's do a little semantic factoring: +Let’s do a little semantic factoring: :: - fourth == rest rest rest first + fourth == rest rest rest first - proc_left == [cons dip] dupdip - proc_current == [[F] dupdip] dip - proc_right == [fourth] dip i + proc_left == [cons dip] dupdip + proc_current == [[F] dupdip] dip + proc_right == [fourth] dip i - Tree-iter-order == [not] [pop] [dup third] [proc_left proc_current proc_right] genrec + Tree-iter-order == [not] [pop] [dup third] [proc_left proc_current proc_right] genrec Now we can sort sequences. @@ -908,33 +908,33 @@ reader. Getting values by key --------------------- -Let's derive a function that accepts a tree and a key and returns the +Let’s derive a function that accepts a tree and a key and returns the value associated with that key. :: - tree key Tree-get - ----------------------- - value + tree key Tree-get + ----------------------- + value -But what do we do if the key isn't in the tree? In Python we might raise -a ``KeyError`` but I'd like to avoid exceptions in Joy if possible, and -here I think it's possible. (Division by zero is an example of where I -think it's probably better to let Python crash Joy. Sometimes the -machinery fails and you have to "stop the line", I think.) +But what do we do if the key isn’t in the tree? In Python we might raise +a ``KeyError`` but I’d like to avoid exceptions in Joy if possible, and +here I think it’s possible. (Division by zero is an example of where I +think it’s probably better to let Python crash Joy. Sometimes the +machinery fails and you have to “stop the line”, I think.) -Let's pass the buck to the caller by making the base case a given, you +Let’s pass the buck to the caller by making the base case a given, you have to decide for yourself what ``[E]`` should be. :: - tree key [E] Tree-get - ---------------------------- key in tree - value + tree key [E] Tree-get + ---------------------------- key in tree + value - tree key [E] Tree-get - ---------------------------- key not in tree - [] key E + tree key [E] Tree-get + ---------------------------- key not in tree + [] key E The base case ``[]`` ~~~~~~~~~~~~~~~~~~~~ @@ -943,13 +943,13 @@ As before, the stopping predicate just has to detect the empty list: :: - Tree-get == [pop not] [E] [R0] [R1] genrec + Tree-get == [pop not] [E] [R0] [R1] genrec So we define: :: - Tree-get == [pop not] swap [R0] [R1] genrec + Tree-get == [pop not] swap [R0] [R1] genrec Note that this ``Tree-get`` creates a slightly different function than itself and *that function* does the actual recursion. This kind of @@ -958,14 +958,14 @@ Joy. :: - tree key [E] [pop not] swap [R0] [R1] genrec - tree key [pop not] [E] [R0] [R1] genrec + tree key [E] [pop not] swap [R0] [R1] genrec + tree key [pop not] [E] [R0] [R1] genrec The anonymous specialized recursive function that will do the real work. :: - [pop not] [E] [R0] [R1] genrec + [pop not] [E] [R0] [R1] genrec Node case ``[key value left right]`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -974,27 +974,27 @@ Now we need to figure out ``R0`` and ``R1``: :: - [key value left right] key R0 [BTree-get] R1 + [key value left right] key R0 [BTree-get] R1 We want to compare the search key with the key in the node, and if they are the same return the value, otherwise recur on one of the child -nodes. So it's very similar to the above funtion, with ``[R0] == []`` +nodes. So it’s very similar to the above funtion, with ``[R0] == []`` and ``R1 == P [T>] [E] [T<] cmp``: :: - [key value left right] key [BTree-get] P [T>] [E] [T<] cmp + [key value left right] key [BTree-get] P [T>] [E] [T<] cmp Predicate ^^^^^^^^^ :: - P == over [get-node-key] nullary - get-node-key == pop popop first + P == over [get-node-key] nullary + get-node-key == pop popop first The only difference is that ``get-node-key`` does one less ``pop`` -because there's no value to discard. +because there’s no value to discard. Branches ^^^^^^^^ @@ -1003,9 +1003,9 @@ Now we have to derive the branches: :: - [key_n value_n left right] key [BTree-get] T> - [key_n value_n left right] key [BTree-get] E - [key_n value_n left right] key [BTree-get] T< + [key_n value_n left right] key [BTree-get] T> + [key_n value_n left right] key [BTree-get] E + [key_n value_n left right] key [BTree-get] T< Greater than and less than ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1015,44 +1015,44 @@ The cases of ``T>`` and ``T<`` are similar to above but instead of using :: - [key_n value_n left right] key [BTree-get] T> - --------------------------------------------------- - right key BTree-get + [key_n value_n left right] key [BTree-get] T> + --------------------------------------------------- + right key BTree-get And: :: - [key_n value_n left right] key [BTree-get] T< - --------------------------------------------------- - left key BTree-get + [key_n value_n left right] key [BTree-get] T< + --------------------------------------------------- + left key BTree-get So: :: - T> == [fourth] dipd i - T< == [third] dipd i + T> == [fourth] dipd i + T< == [third] dipd i E.g.: :: - [key_n value_n left right] key [BTree-get] [fourth] dipd i - [key_n value_n left right] fourth key [BTree-get] i - right key [BTree-get] i - right key BTree-get + [key_n value_n left right] key [BTree-get] [fourth] dipd i + [key_n value_n left right] fourth key [BTree-get] i + right key [BTree-get] i + right key BTree-get Equal keys ^^^^^^^^^^ -Return the node's value: +Return the node’s value: :: - [key_n value_n left right] key [BTree-get] E == value_n + [key_n value_n left right] key [BTree-get] E == value_n - E == popop second + E == popop second ``Tree-get`` ~~~~~~~~~~~~ @@ -1061,14 +1061,14 @@ So: :: - fourth == rest rest rest first - get-node-key == pop popop first - P == over [get-node-key] nullary - T> == [fourth] dipd i - T< == [third] dipd i - E == popop second + fourth == rest rest rest first + get-node-key == pop popop first + P == over [get-node-key] nullary + T> == [fourth] dipd i + T< == [third] dipd i + E == popop second - Tree-get == [pop not] swap [] [P [T>] [E] [T<] cmp] genrec + Tree-get == [pop not] swap [] [P [T>] [E] [T<] cmp] genrec .. code:: ipython2 @@ -1143,14 +1143,14 @@ So: Tree-delete ----------- -Now let's write a function that can return a tree datastructure with a +Now let’s write a function that can return a tree datastructure with a key, value pair deleted: :: - tree key Tree-delete - --------------------------- - tree + tree key Tree-delete + --------------------------- + tree If the key is not in tree it just returns the tree unchanged. @@ -1161,42 +1161,42 @@ Same as above. :: - Tree-Delete == [pop not] [pop] [R0] [R1] genrec + Tree-Delete == [pop not] [pop] [R0] [R1] genrec Recur ~~~~~ -Now we get to figure out the recursive case. We need the node's key to +Now we get to figure out the recursive case. We need the node’s key to compare and we need to carry the key into recursive branches. Let ``D`` be shorthand for ``Tree-Delete``: :: - D == Tree-Delete == [pop not] [pop] [R0] [R1] genrec + D == Tree-Delete == [pop not] [pop] [R0] [R1] genrec - [node_key node_value left right] key R0 [D] R1 - [node_key node_value left right] key over first swap dup [D] cons R1′ - [node_key node_value left right] key [...] first swap dup [D] cons R1′ - [node_key node_value left right] key node_key swap dup [D] cons R1′ - [node_key node_value left right] node_key key dup [D] cons R1′ - [node_key node_value left right] node_key key key [D] cons R1′ - [node_key node_value left right] node_key key [key D] R1′ + [node_key node_value left right] key R0 [D] R1 + [node_key node_value left right] key over first swap dup [D] cons R1′ + [node_key node_value left right] key [...] first swap dup [D] cons R1′ + [node_key node_value left right] key node_key swap dup [D] cons R1′ + [node_key node_value left right] node_key key dup [D] cons R1′ + [node_key node_value left right] node_key key key [D] cons R1′ + [node_key node_value left right] node_key key [key D] R1′ And then: :: - [node_key node_value left right] node_key key [key D] R1′ - [node_key node_value left right] node_key key [key D] roll> [T>] [E] [T<] cmp - [node_key node_value left right] node_key key [key D] roll> [T>] [E] [T<] cmp - [node_key node_value left right] [key D] node_key key [T>] [E] [T<] cmp + [node_key node_value left right] node_key key [key D] R1′ + [node_key node_value left right] node_key key [key D] roll> [T>] [E] [T<] cmp + [node_key node_value left right] node_key key [key D] roll> [T>] [E] [T<] cmp + [node_key node_value left right] [key D] node_key key [T>] [E] [T<] cmp So: :: - R0 == over first swap dup - R1 == cons roll> [T>] [E] [T<] cmp + R0 == over first swap dup + R1 == cons roll> [T>] [E] [T<] cmp Compare Keys ~~~~~~~~~~~~ @@ -1205,50 +1205,50 @@ The last line above: :: - [node_key node_value left right] [key D] node_key key [T>] [E] [T<] cmp + [node_key node_value left right] [key D] node_key key [T>] [E] [T<] cmp Then becomes one of these three: :: - [node_key node_value left right] [key D] T> - [node_key node_value left right] [key D] E - [node_key node_value left right] [key D] T< + [node_key node_value left right] [key D] T> + [node_key node_value left right] [key D] E + [node_key node_value left right] [key D] T< Greater than case and less than case ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: - [node_key node_value left right] [F] T> - ------------------------------------------------- - [node_key node_value (left F) right] + [node_key node_value left right] [F] T> + ------------------------------------------------- + [node_key node_value (left F) right] - [node_key node_value left right] [F] T< - ------------------------------------------------- - [node_key node_value left (right F)] + [node_key node_value left right] [F] T< + ------------------------------------------------- + [node_key node_value left (right F)] First, treating the node as a stack: :: - right left node_value node_key [key D] dipd - right left key D node_value node_key - right left' node_value node_key + right left node_value node_key [key D] dipd + right left key D node_value node_key + right left' node_value node_key Ergo: :: - [node_key node_value left right] [key D] [dipd] cons infra + [node_key node_value left right] [key D] [dipd] cons infra So: :: - T> == [dipd] cons infra - T< == [dipdd] cons infra + T> == [dipd] cons infra + T< == [dipdd] cons infra The else case ~~~~~~~~~~~~~ @@ -1258,11 +1258,11 @@ need to replace the current node with something :: - [node_key node_value left right] [key D] E - ------------------------------------------------ - tree + [node_key node_value left right] [key D] E + ------------------------------------------------ + tree -We have to handle three cases, so let's use ``cond``. +We have to handle three cases, so let’s use ``cond``. One or more child nodes are ``[]`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1272,11 +1272,11 @@ node return it. If both child nodes are empty return an empty node. :: - E == [ - [[pop third not] pop fourth] - [[pop fourth not] pop third] - [default] - ] cond + E == [ + [[pop third not] pop fourth] + [[pop fourth not] pop third] + [default] + ] cond Both child nodes are non-empty. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1295,92 +1295,92 @@ The initial structure of the default function: :: - default == [E′] cons infra + default == [E′] cons infra - [node_key node_value left right] [key D] default - [node_key node_value left right] [key D] [E′] cons infra - [node_key node_value left right] [[key D] E′] infra + [node_key node_value left right] [key D] default + [node_key node_value left right] [key D] [E′] cons infra + [node_key node_value left right] [[key D] E′] infra - right left node_value node_key [key D] E′ + right left node_value node_key [key D] E′ -First things first, we no longer need this node's key and value: +First things first, we no longer need this node’s key and value: :: - right left node_value node_key [key D] roll> popop E″ - right left [key D] node_value node_key popop E″ - right left [key D] E″ + right left node_value node_key [key D] roll> popop E″ + right left [key D] node_value node_key popop E″ + right left [key D] E″ We have to we find the highest (right-most) node in our lower (left) sub-tree: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: - right left [key D] E″ + right left [key D] E″ Ditch the key: :: - right left [key D] rest E‴ - right left [D] E‴ + right left [key D] rest E‴ + right left [D] E‴ Find the right-most node: :: - right left [D] [dup W] dip E⁗ - right left dup W [D] E⁗ - right left left W [D] E⁗ + right left [D] [dup W] dip E⁗ + right left dup W [D] E⁗ + right left left W [D] E⁗ Consider: :: - left W + left W We know left is not empty: :: - [L_key L_value L_left L_right] W + [L_key L_value L_left L_right] W We want to keep extracting the right node as long as it is not empty: :: - W.rightmost == [P] [B] while + W.rightmost == [P] [B] while - left W.rightmost W′ + left W.rightmost W′ The predicate: :: - [L_key L_value L_left L_right] P - [L_key L_value L_left L_right] fourth - L_right + [L_key L_value L_left L_right] P + [L_key L_value L_left L_right] fourth + L_right This can run on ``[]`` so must be guarded: :: - ?fourth == [] [fourth] [] ifte + ?fourth == [] [fourth] [] ifte -( if\_not\_empty == [] swap [] ifte ?fourth == [fourth] if\_not\_empty ) +( if_not_empty == [] swap [] ifte ?fourth == [fourth] if_not_empty ) The body is just ``fourth``: :: - left [?fourth] [fourth] while W′ - rightest W′ + left [?fourth] [fourth] while W′ + rightest W′ So: :: - W.rightmost == [?fourth] [fourth] while + W.rightmost == [?fourth] [fourth] while Found right-most node in our left sub-tree ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1389,25 +1389,25 @@ We know rightest is not empty: :: - [R_key R_value R_left R_right] W′ - [R_key R_value R_left R_right] W′ - [R_key R_value R_left R_right] uncons uncons pop - R_key [R_value R_left R_right] uncons pop - R_key R_value [R_left R_right] pop - R_key R_value + [R_key R_value R_left R_right] W′ + [R_key R_value R_left R_right] W′ + [R_key R_value R_left R_right] uncons uncons pop + R_key [R_value R_left R_right] uncons pop + R_key R_value [R_left R_right] pop + R_key R_value So: :: - W == [?fourth] [fourth] while uncons uncons pop + W == [?fourth] [fourth] while uncons uncons pop And: :: - right left left W [D] E⁗ - right left R_key R_value [D] E⁗ + right left left W [D] E⁗ + right left R_key R_value [D] E⁗ Replace current node key and value, recursively delete rightmost ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1416,88 +1416,88 @@ Final stretch. We want to end up with something like: :: - right left [R_key D] i R_value R_key - right left R_key D R_value R_key - right left′ R_value R_key + right left [R_key D] i R_value R_key + right left R_key D R_value R_key + right left′ R_value R_key If we adjust our definition of ``W`` to include ``over`` at the end: :: - W == [fourth] [fourth] while uncons uncons pop over + W == [fourth] [fourth] while uncons uncons pop over That will give us: :: - right left R_key R_value R_key [D] E⁗ + right left R_key R_value R_key [D] E⁗ - right left R_key R_value R_key [D] cons dipd E⁗′ - right left R_key R_value [R_key D] dipd E⁗′ - right left R_key D R_key R_value E⁗′ - right left′ R_key R_value E⁗′ - right left′ R_key R_value swap - right left′ R_value R_key + right left R_key R_value R_key [D] cons dipd E⁗′ + right left R_key R_value [R_key D] dipd E⁗′ + right left R_key D R_key R_value E⁗′ + right left′ R_key R_value E⁗′ + right left′ R_key R_value swap + right left′ R_value R_key So: :: - E′ == roll> popop E″ + E′ == roll> popop E″ - E″ == rest E‴ + E″ == rest E‴ - E‴ == [dup W] dip E⁗ + E‴ == [dup W] dip E⁗ - E⁗ == cons dipdd swap + E⁗ == cons dipdd swap Substituting: :: - W == [fourth] [fourth] while uncons uncons pop over - E′ == roll> popop rest [dup W] dip cons dipd swap - E == [ - [[pop third not] pop fourth] - [[pop fourth not] pop third] - [[E′] cons infra] - ] cond + W == [fourth] [fourth] while uncons uncons pop over + E′ == roll> popop rest [dup W] dip cons dipd swap + E == [ + [[pop third not] pop fourth] + [[pop fourth not] pop third] + [[E′] cons infra] + ] cond Minor rearrangement, move ``dup`` into ``W``: :: - W == dup [fourth] [fourth] while uncons uncons pop over - E′ == roll> popop rest [W] dip cons dipd swap - E == [ - [[pop third not] pop fourth] - [[pop fourth not] pop third] - [[E′] cons infra] - ] cond + W == dup [fourth] [fourth] while uncons uncons pop over + E′ == roll> popop rest [W] dip cons dipd swap + E == [ + [[pop third not] pop fourth] + [[pop fourth not] pop third] + [[E′] cons infra] + ] cond Refactoring ~~~~~~~~~~~ :: - W.rightmost == [fourth] [fourth] while - W.unpack == uncons uncons pop - W == dup W.rightmost W.unpack over - E.clear_stuff == roll> popop rest - E.delete == cons dipd - E.0 == E.clear_stuff [W] dip E.delete swap - E == [ - [[pop third not] pop fourth] - [[pop fourth not] pop third] - [[E.0] cons infra] - ] cond - T> == [dipd] cons infra - T< == [dipdd] cons infra - R0 == over first swap dup - R1 == cons roll> [T>] [E] [T<] cmp - BTree-Delete == [pop not] swap [R0] [R1] genrec + W.rightmost == [fourth] [fourth] while + W.unpack == uncons uncons pop + W == dup W.rightmost W.unpack over + E.clear_stuff == roll> popop rest + E.delete == cons dipd + E.0 == E.clear_stuff [W] dip E.delete swap + E == [ + [[pop third not] pop fourth] + [[pop fourth not] pop third] + [[E.0] cons infra] + ] cond + T> == [dipd] cons infra + T< == [dipdd] cons infra + R0 == over first swap dup + R1 == cons roll> [T>] [E] [T<] cmp + BTree-Delete == [pop not] swap [R0] [R1] genrec -By the standards of the code I've written so far, this is a *huge* Joy +By the standards of the code I’ve written so far, this is a *huge* Joy program. .. code:: ipython2 @@ -1594,46 +1594,46 @@ Appendix: The source code. :: - fourth == rest_two rest first - ?fourth == [] [fourth] [] ifte - first_two == uncons uncons pop - ccons == cons cons - cinf == cons infra - rest_two == rest rest + fourth == rest_two rest first + ?fourth == [] [fourth] [] ifte + first_two == uncons uncons pop + ccons == cons cons + cinf == cons infra + rest_two == rest rest - _Tree_T> == [dipd] cinf - _Tree_T< == [dipdd] cinf + _Tree_T> == [dipd] cinf + _Tree_T< == [dipdd] cinf - _Tree_add_P == over [popop popop first] nullary - _Tree_add_T> == ccons _Tree_T< - _Tree_add_T< == ccons _Tree_T> - _Tree_add_Ee == pop swap roll< rest_two ccons - _Tree_add_R == _Tree_add_P [_Tree_add_T>] [_Tree_add_Ee] [_Tree_add_T<] cmp - _Tree_add_E == [pop] dipd Tree-new + _Tree_add_P == over [popop popop first] nullary + _Tree_add_T> == ccons _Tree_T< + _Tree_add_T< == ccons _Tree_T> + _Tree_add_Ee == pop swap roll< rest_two ccons + _Tree_add_R == _Tree_add_P [_Tree_add_T>] [_Tree_add_Ee] [_Tree_add_T<] cmp + _Tree_add_E == [pop] dipd Tree-new - _Tree_iter_order_left == [cons dip] dupdip - _Tree_iter_order_current == [[F] dupdip] dip - _Tree_iter_order_right == [fourth] dip i - _Tree_iter_order_R == _Tree_iter_order_left _Tree_iter_order_current _Tree_iter_order_right + _Tree_iter_order_left == [cons dip] dupdip + _Tree_iter_order_current == [[F] dupdip] dip + _Tree_iter_order_right == [fourth] dip i + _Tree_iter_order_R == _Tree_iter_order_left _Tree_iter_order_current _Tree_iter_order_right - _Tree_get_P == over [pop popop first] nullary - _Tree_get_T> == [fourth] dipd i - _Tree_get_T< == [third] dipd i - _Tree_get_E == popop second - _Tree_get_R == _Tree_get_P [_Tree_get_T>] [_Tree_get_E] [_Tree_get_T<] cmp + _Tree_get_P == over [pop popop first] nullary + _Tree_get_T> == [fourth] dipd i + _Tree_get_T< == [third] dipd i + _Tree_get_E == popop second + _Tree_get_R == _Tree_get_P [_Tree_get_T>] [_Tree_get_E] [_Tree_get_T<] cmp - _Tree_delete_rightmost == [?fourth] [fourth] while - _Tree_delete_clear_stuff == roll> popop rest - _Tree_delete_del == dip cons dipd swap - _Tree_delete_W == dup _Tree_delete_rightmost first_two over - _Tree_delete_E.0 == _Tree_delete_clear_stuff [_Tree_delete_W] _Tree_delete_del - _Tree_delete_E == [[[pop third not] pop fourth] [[pop fourth not] pop third] [[_Tree_delete_E.0] cinf]] cond - _Tree_delete_R0 == over first swap dup - _Tree_delete_R1 == cons roll> [_Tree_T>] [_Tree_delete_E] [_Tree_T<] cmp + _Tree_delete_rightmost == [?fourth] [fourth] while + _Tree_delete_clear_stuff == roll> popop rest + _Tree_delete_del == dip cons dipd swap + _Tree_delete_W == dup _Tree_delete_rightmost first_two over + _Tree_delete_E.0 == _Tree_delete_clear_stuff [_Tree_delete_W] _Tree_delete_del + _Tree_delete_E == [[[pop third not] pop fourth] [[pop fourth not] pop third] [[_Tree_delete_E.0] cinf]] cond + _Tree_delete_R0 == over first swap dup + _Tree_delete_R1 == cons roll> [_Tree_T>] [_Tree_delete_E] [_Tree_T<] cmp - Tree-new == swap [[] []] ccons - Tree-add == [popop not] [_Tree_add_E] [] [_Tree_add_R] genrec - Tree-iter == [not] [pop] roll< [dupdip rest_two] cons [step] genrec - Tree-iter-order == [not] [pop] [dup third] [_Tree_iter_order_R] genrec - Tree-get == [pop not] swap [] [_Tree_get_R] genrec - Tree-delete == [pop not] [pop] [_Tree_delete_R0] [_Tree_delete_R1] genrec + Tree-new == swap [[] []] ccons + Tree-add == [popop not] [_Tree_add_E] [] [_Tree_add_R] genrec + Tree-iter == [not] [pop] roll< [dupdip rest_two] cons [step] genrec + Tree-iter-order == [not] [pop] [dup third] [_Tree_iter_order_R] genrec + Tree-get == [pop not] swap [] [_Tree_get_R] genrec + Tree-delete == [pop not] [pop] [_Tree_delete_R0] [_Tree_delete_R1] genrec diff --git a/docs/Quadratic.rst b/docs/Quadratic.rst index 0d952d7..3262e84 100644 --- a/docs/Quadratic.rst +++ b/docs/Quadratic.rst @@ -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 it’s 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: +Let’s try it out: .. code:: ipython2 diff --git a/docs/Recursion_Combinators.rst b/docs/Recursion_Combinators.rst index f2d3331..9159882 100644 --- a/docs/Recursion_Combinators.rst +++ b/docs/Recursion_Combinators.rst @@ -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 `__ ------------------------------------------------------------------------------------ @@ -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" `__ +Cf. `“Bananas, Lenses, & Barbed +Wire” `__ 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 `__ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Let's write a function that, given a positive integer, returns the sum +Let’s 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: +Let’s 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 +generator’s 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 you’ll 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 `__ -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" `__ +`“Bananas, Lenses, & Barbed +Wire” `__ :: - (|...|) [(...)] [<...>] + (|...|) [(...)] [<...>] 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.” diff --git a/docs/Replacing.rst b/docs/Replacing.rst index 6689dba..0f90445 100644 --- a/docs/Replacing.rst +++ b/docs/Replacing.rst @@ -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, there’s 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 diff --git a/docs/The_Four_Operations.rst b/docs/The_Four_Operations.rst index 4af0772..b345b0e 100644 --- a/docs/The_Four_Operations.rst +++ b/docs/The_Four_Operations.rst @@ -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 doesn’t *have* +to be Boolean because Python’s 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 couldn’t 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.) +Elliott’s “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 you’d 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 +(I’m 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`` couldn’t 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 I’m not going to worry about it. (And I think the Categories can deal with it anyhow? Incremental evaluation, yeah?) diff --git a/docs/Trees.rst b/docs/Trees.rst index 7fe19c9..922d1c6 100644 --- a/docs/Trees.rst +++ b/docs/Trees.rst @@ -13,7 +13,7 @@ notation `__, is: :: - BTree :: [] | [key value BTree BTree] + BTree :: [] | [key value BTree BTree] That says that a BTree is either the empty quote ``[]`` or a quote with four items: a key, a value, and two BTrees representing the left and @@ -22,7 +22,7 @@ right branches of the tree. A Function to Traverse this Structure ------------------------------------- -Let's take a crack at writing a function that can recursively iterate or +Let’s take a crack at writing a function that can recursively iterate or traverse these trees. Base case ``[]`` @@ -32,13 +32,13 @@ The stopping predicate just has to detect the empty list: :: - BTree-iter == [not] [E] [R0] [R1] genrec + BTree-iter == [not] [E] [R0] [R1] genrec -And since there's nothing at this node, we just ``pop`` it: +And since there’s nothing at this node, we just ``pop`` it: :: - BTree-iter == [not] [pop] [R0] [R1] genrec + BTree-iter == [not] [pop] [R0] [R1] genrec Node case ``[key value left right]`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -47,14 +47,14 @@ Now we need to figure out ``R0`` and ``R1``: :: - BTree-iter == [not] [pop] [R0] [R1] genrec - == [not] [pop] [R0 [BTree-iter] R1] ifte + BTree-iter == [not] [pop] [R0] [R1] genrec + == [not] [pop] [R0 [BTree-iter] R1] ifte -Let's look at it *in situ*: +Let’s look at it *in situ*: :: - [key value left right] R0 [BTree-iter] R1 + [key value left right] R0 [BTree-iter] R1 Processing the current node. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -64,18 +64,18 @@ node and then ``dip`` on some function to process the copy with it: :: - [key value left right] [F] dupdip [BTree-iter] R1 - [key value left right] F [key value left right] [BTree-iter] R1 + [key value left right] [F] dupdip [BTree-iter] R1 + [key value left right] F [key value left right] [BTree-iter] R1 -For example, if we're getting all the keys ``F`` would be ``first``: +For example, if we’re getting all the keys ``F`` would be ``first``: :: - R0 == [first] dupdip + R0 == [first] dupdip - [key value left right] [first] dupdip [BTree-iter] R1 - [key value left right] first [key value left right] [BTree-iter] R1 - key [key value left right] [BTree-iter] R1 + [key value left right] [first] dupdip [BTree-iter] R1 + [key value left right] first [key value left right] [BTree-iter] R1 + key [key value left right] [BTree-iter] R1 Recur ^^^^^ @@ -86,26 +86,26 @@ with an interesting situation: :: - key [key value left right] [BTree-iter] R1 - key [key value left right] [BTree-iter] [rest rest] dip - key [key value left right] rest rest [BTree-iter] - key [left right] [BTree-iter] + key [key value left right] [BTree-iter] R1 + key [key value left right] [BTree-iter] [rest rest] dip + key [key value left right] rest rest [BTree-iter] + key [left right] [BTree-iter] Hmm, will ``step`` do? :: - key [left right] [BTree-iter] step - key left BTree-iter [right] [BTree-iter] step - key left-keys [right] [BTree-iter] step - key left-keys right BTree-iter - key left-keys right-keys + key [left right] [BTree-iter] step + key left BTree-iter [right] [BTree-iter] step + key left-keys [right] [BTree-iter] step + key left-keys right BTree-iter + key left-keys right-keys Wow. So: :: - R1 == [rest rest] dip step + R1 == [rest rest] dip step Putting it together ^^^^^^^^^^^^^^^^^^^ @@ -114,14 +114,14 @@ We have: :: - BTree-iter == [not] [pop] [[F] dupdip] [[rest rest] dip step] genrec + BTree-iter == [not] [pop] [[F] dupdip] [[rest rest] dip step] genrec When I was reading this over I realized ``rest rest`` could go in ``R0``: :: - BTree-iter == [not] [pop] [[F] dupdip rest rest] [step] genrec + BTree-iter == [not] [pop] [[F] dupdip rest rest] [step] genrec (And ``[step] genrec`` is such a cool and suggestive combinator!) @@ -130,21 +130,21 @@ Parameterizing the ``F`` per-node processing function. :: - [F] BTree-iter == [not] [pop] [[F] dupdip rest rest] [step] genrec + [F] BTree-iter == [not] [pop] [[F] dupdip rest rest] [step] genrec Working backward: :: - [not] [pop] [[F] dupdip rest rest] [step] genrec - [not] [pop] [F] [dupdip rest rest] cons [step] genrec - [F] [not] [pop] roll< [dupdip rest rest] cons [step] genrec + [not] [pop] [[F] dupdip rest rest] [step] genrec + [not] [pop] [F] [dupdip rest rest] cons [step] genrec + [F] [not] [pop] roll< [dupdip rest rest] cons [step] genrec Ergo: :: - BTree-iter == [not] [pop] roll< [dupdip rest rest] cons [step] genrec + BTree-iter == [not] [pop] roll< [dupdip rest rest] cons [step] genrec .. code:: ipython2 @@ -197,11 +197,11 @@ Ergo: Adding Nodes to the BTree ========================= -Let's consider adding nodes to a BTree structure. +Let’s consider adding nodes to a BTree structure. :: - BTree value key BTree-add == BTree + BTree value key BTree-add == BTree Adding to an empty node. ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -211,20 +211,20 @@ If the current node is ``[]`` then you just return :: - BTree-add == [popop not] [[pop] dipd BTree-new] [R0] [R1] genrec + BTree-add == [popop not] [[pop] dipd BTree-new] [R0] [R1] genrec Where ``BTree-new`` is: :: - value key BTree-new == [key value [] []] + value key BTree-new == [key value [] []] - value key swap [[] []] cons cons - key value [[] []] cons cons - key [value [] []] cons - [key value [] []] + value key swap [[] []] cons cons + key value [[] []] cons cons + key [value [] []] cons + [key value [] []] - BTree-new == swap [[] []] cons cons + BTree-new == swap [[] []] cons cons .. code:: ipython2 @@ -250,54 +250,54 @@ Where ``BTree-new`` is: (As an implementation detail, the ``[[] []]`` literal used in the definition of ``BTree-new`` will be reused to supply the *constant* tail for *all* new nodes produced by it. This is one of those cases where you -get amortized storage "for free" by using `persistent +get amortized storage “for free” by using `persistent datastructures `__. Because the tail, which is ``((), ((), ()))`` in Python, is immutable and embedded in the definition body for ``BTree-new``, all new nodes can reuse it as their own tail without fear that some other code somewhere will change it.) -If the current node isn't empty. +If the current node isn’t empty. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ We now have to derive ``R0`` and ``R1``, consider: :: - [key_n value_n left right] value key R0 [BTree-add] R1 + [key_n value_n left right] value key R0 [BTree-add] R1 In this case, there are three possibilites: the key can be greater or -less than or equal to the node's key. In two of those cases we will need +less than or equal to the node’s key. In two of those cases we will need to apply a copy of ``BTree-add``, so ``R0`` is pretty much out of the picture. :: - [R0] == [] + [R0] == [] A predicate to compare keys. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The first thing we need to do is compare the the key we're adding to see +The first thing we need to do is compare the the key we’re adding to see if it is greater than the node key and ``branch`` accordingly, although -in this case it's easier to write a destructive predicate and then use +in this case it’s easier to write a destructive predicate and then use ``ifte`` to apply it ``nullary``: :: - [key_n value_n left right] value key [BTree-add] R1 - [key_n value_n left right] value key [BTree-add] [P >] [T] [E] ifte + [key_n value_n left right] value key [BTree-add] R1 + [key_n value_n left right] value key [BTree-add] [P >] [T] [E] ifte - [key_n value_n left right] value key [BTree-add] P > - [key_n value_n left right] value key [BTree-add] pop roll> pop first > - [key_n value_n left right] value key roll> pop first > - key [key_n value_n left right] value roll> pop first > - key key_n > - Boolean + [key_n value_n left right] value key [BTree-add] P > + [key_n value_n left right] value key [BTree-add] pop roll> pop first > + [key_n value_n left right] value key roll> pop first > + key [key_n value_n left right] value roll> pop first > + key key_n > + Boolean - P > == pop roll> pop first > - P < == pop roll> pop first < - P == pop roll> pop first + P > == pop roll> pop first > + P < == pop roll> pop first < + P == pop roll> pop first .. code:: ipython2 @@ -323,7 +323,7 @@ in this case it's easier to write a destructive predicate and then use True . -If the key we're adding is greater than the node's key. +If the key we’re adding is greater than the node’s key. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Here the parantheses are meant to signify that the right-hand side (RHS) @@ -332,58 +332,58 @@ evaluated: :: - [key_n value_n left right] value key [BTree-add] T == [key_n value_n left (BTree-add key value right)] + [key_n value_n left right] value key [BTree-add] T == [key_n value_n left (BTree-add key value right)] Use ``infra`` on ``K``. ^^^^^^^^^^^^^^^^^^^^^^^ -So how do we do this? We know we're going to want to use ``infra`` on +So how do we do this? We know we’re going to want to use ``infra`` on some function ``K`` that has the key and value to work with, as well as the quoted copy of ``BTree-add`` to apply somehow: :: - right left value_n key_n value key [BTree-add] K - ... - right value key BTree-add left value_n key_n + right left value_n key_n value key [BTree-add] K + ... + right value key BTree-add left value_n key_n Pretty easy: :: - right left value_n key_n value key [BTree-add] cons cons dipdd - right left value_n key_n [value key BTree-add] dipdd - right value key BTree-add left value_n key_n + right left value_n key_n value key [BTree-add] cons cons dipdd + right left value_n key_n [value key BTree-add] dipdd + right value key BTree-add left value_n key_n So: :: - K == cons cons dipdd + K == cons cons dipdd And: :: - [key_n value_n left right] [value key [BTree-add] K] infra + [key_n value_n left right] [value key [BTree-add] K] infra Derive ``T``. ^^^^^^^^^^^^^ -So now we're at getting from this to this: +So now we’re at getting from this to this: :: - [key_n value_n left right] value key [BTree-add] T - ... - [key_n value_n left right] [value key [BTree-add] K] infra + [key_n value_n left right] value key [BTree-add] T + ... + [key_n value_n left right] [value key [BTree-add] K] infra And so ``T`` is just: :: - value key [BTree-add] T == [value key [BTree-add] K] infra - T == [ K] cons cons cons infra + value key [BTree-add] T == [value key [BTree-add] K] infra + T == [ K] cons cons cons infra .. code:: ipython2 @@ -452,15 +452,15 @@ And so ``T`` is just: ['k' 'v' 'l' 0 'kk' 'vv' 'r'] . -If the key we're adding is less than the node's key. +If the key we’re adding is less than the node’s key. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This is very very similar to the above: :: - [key_n value_n left right] value key [BTree-add] E - [key_n value_n left right] value key [BTree-add] [P <] [Te] [Ee] ifte + [key_n value_n left right] value key [BTree-add] E + [key_n value_n left right] value key [BTree-add] [P <] [Te] [Ee] ifte In this case ``Te`` works that same as ``T`` but on the left child tree instead of the right, so the only difference is that it must use @@ -468,15 +468,15 @@ instead of the right, so the only difference is that it must use :: - Te == [cons cons dipd] cons cons cons infra + Te == [cons cons dipd] cons cons cons infra This suggests an alternate factorization: :: - ccons == cons cons - T == [ccons dipdd] ccons cons infra - Te == [ccons dipd] ccons cons infra + ccons == cons cons + T == [ccons dipdd] ccons cons infra + Te == [ccons dipd] ccons cons infra But whatever. @@ -524,22 +524,22 @@ This means we must find: :: - [key_n value_n left right] value key [BTree-add] Ee - ... - [key value left right] + [key_n value_n left right] value key [BTree-add] Ee + ... + [key value left right] This is another easy one: :: - Ee == pop swap roll< rest rest cons cons + Ee == pop swap roll< rest rest cons cons - [key_n value_n left right] value key [BTree-add] pop swap roll< rest rest cons cons - [key_n value_n left right] value key swap roll< rest rest cons cons - [key_n value_n left right] key value roll< rest rest cons cons - key value [key_n value_n left right] rest rest cons cons - key value [ left right] cons cons - [key value left right] + [key_n value_n left right] value key [BTree-add] pop swap roll< rest rest cons cons + [key_n value_n left right] value key swap roll< rest rest cons cons + [key_n value_n left right] key value roll< rest rest cons cons + key value [key_n value_n left right] rest rest cons cons + key value [ left right] cons cons + [key value left right] .. code:: ipython2 @@ -576,20 +576,20 @@ Now we can define ``BTree-add`` :: - BTree-add == [popop not] [[pop] dipd BTree-new] [] [[P >] [T] [E] ifte] genrec + BTree-add == [popop not] [[pop] dipd BTree-new] [] [[P >] [T] [E] ifte] genrec Putting it all together: :: - BTree-new == swap [[] []] cons cons - P == pop roll> pop first - T == [cons cons dipdd] cons cons cons infra - Te == [cons cons dipd] cons cons cons infra - Ee == pop swap roll< rest rest cons cons - E == [P <] [Te] [Ee] ifte + BTree-new == swap [[] []] cons cons + P == pop roll> pop first + T == [cons cons dipdd] cons cons cons infra + Te == [cons cons dipd] cons cons cons infra + Ee == pop swap roll< rest rest cons cons + E == [P <] [Te] [Ee] ifte - BTree-add == [popop not] [[pop] dipd BTree-new] [] [[P >] [T] [E] ifte] genrec + BTree-add == [popop not] [[pop] dipd BTree-new] [] [[P >] [T] [E] ifte] genrec .. code:: ipython2 @@ -646,7 +646,7 @@ Putting it all together: We can use this to make a set-like datastructure by just setting values -to e.g. 0 and ignoring them. It's set-like in that duplicate items added +to e.g. 0 and ignoring them. It’s set-like in that duplicate items added to it will only occur once within it, and we can query it in `:math:`O(\log_2 N)` `__ time. @@ -695,74 +695,74 @@ from a list. ``cmp`` combinator ================== -Instead of all this mucking about with nested ``ifte`` let's just go +Instead of all this mucking about with nested ``ifte`` let’s just go whole hog and define ``cmp`` which takes two values and three quoted programs on the stack and runs one of the three depending on the results of comparing the two values: :: - a b [G] [E] [L] cmp - ------------------------- a > b - G + a b [G] [E] [L] cmp + ------------------------- a > b + G - a b [G] [E] [L] cmp - ------------------------- a = b - E + a b [G] [E] [L] cmp + ------------------------- a = b + E - a b [G] [E] [L] cmp - ------------------------- a < b - L + a b [G] [E] [L] cmp + ------------------------- a < b + L We need a new non-destructive predicate ``P``: :: - [key_n value_n left right] value key [BTree-add] P - [key_n value_n left right] value key [BTree-add] over [Q] nullary - [key_n value_n left right] value key [BTree-add] key [Q] nullary - [key_n value_n left right] value key [BTree-add] key Q - [key_n value_n left right] value key [BTree-add] key popop popop first - [key_n value_n left right] value key popop first - [key_n value_n left right] first - key_n - [key_n value_n left right] value key [BTree-add] key [Q] nullary - [key_n value_n left right] value key [BTree-add] key key_n + [key_n value_n left right] value key [BTree-add] P + [key_n value_n left right] value key [BTree-add] over [Q] nullary + [key_n value_n left right] value key [BTree-add] key [Q] nullary + [key_n value_n left right] value key [BTree-add] key Q + [key_n value_n left right] value key [BTree-add] key popop popop first + [key_n value_n left right] value key popop first + [key_n value_n left right] first + key_n + [key_n value_n left right] value key [BTree-add] key [Q] nullary + [key_n value_n left right] value key [BTree-add] key key_n - P == over [popop popop first] nullary + P == over [popop popop first] nullary Here are the definitions again, pruned and renamed in some cases: :: - BTree-new == swap [[] []] cons cons - P == over [popop popop first] nullary - T> == [cons cons dipdd] cons cons cons infra - T< == [cons cons dipd] cons cons cons infra - E == pop swap roll< rest rest cons cons + BTree-new == swap [[] []] cons cons + P == over [popop popop first] nullary + T> == [cons cons dipdd] cons cons cons infra + T< == [cons cons dipd] cons cons cons infra + E == pop swap roll< rest rest cons cons Using ``cmp`` to simplify `our code above at ``R1`` <#If-the-current-node-isn't-empty.>`__: :: - [key_n value_n left right] value key [BTree-add] R1 - [key_n value_n left right] value key [BTree-add] P [T>] [E] [T<] cmp + [key_n value_n left right] value key [BTree-add] R1 + [key_n value_n left right] value key [BTree-add] P [T>] [E] [T<] cmp The line above becomes one of the three lines below: :: - [key_n value_n left right] value key [BTree-add] T> - [key_n value_n left right] value key [BTree-add] E - [key_n value_n left right] value key [BTree-add] T< + [key_n value_n left right] value key [BTree-add] T> + [key_n value_n left right] value key [BTree-add] E + [key_n value_n left right] value key [BTree-add] T< The definition is a little longer but, I think, more elegant and easier to understand: :: - BTree-add == [popop not] [[pop] dipd BTree-new] [] [P [T>] [E] [T<] cmp] genrec + BTree-add == [popop not] [[pop] dipd BTree-new] [] [P [T>] [E] [T<] cmp] genrec .. code:: ipython2 @@ -945,17 +945,17 @@ names carefully the resulting definitions can take on a semantic role. :: - get-node-key == popop popop first - remove-key-and-value-from-node == rest rest - pack-key-and-value == cons cons - prep-new-key-and-value == pop swap roll< - pack-and-apply == [pack-key-and-value] swoncat cons pack-key-and-value infra + get-node-key == popop popop first + remove-key-and-value-from-node == rest rest + pack-key-and-value == cons cons + prep-new-key-and-value == pop swap roll< + pack-and-apply == [pack-key-and-value] swoncat cons pack-key-and-value infra - BTree-new == swap [[] []] pack-key-and-value - P == over [get-node-key] nullary - T> == [dipdd] pack-and-apply - T< == [dipd] pack-and-apply - E == prep-new-key-and-value remove-key-and-value-from-node pack-key-and-value + BTree-new == swap [[] []] pack-key-and-value + P == over [get-node-key] nullary + T> == [dipdd] pack-and-apply + T< == [dipd] pack-and-apply + E == prep-new-key-and-value remove-key-and-value-from-node pack-key-and-value A Version of ``BTree-iter`` that does In-Order Traversal ======================================================== @@ -967,14 +967,14 @@ the right child. This will allow us to traverse the tree in sort order. :: - BTree-iter-order == [not] [pop] [R0 [BTree-iter] R1] ifte + BTree-iter-order == [not] [pop] [R0 [BTree-iter] R1] ifte To define ``R0`` and ``R1`` it helps to look at them as they will appear when they run: :: - [key value left right] R0 [BTree-iter-order] R1 + [key value left right] R0 [BTree-iter-order] R1 Process the left child. ^^^^^^^^^^^^^^^^^^^^^^^ @@ -983,38 +983,38 @@ Staring at this for a bit suggests ``dup third`` to start: :: - [key value left right] R0 [BTree-iter-order] R1 - [key value left right] dup third [BTree-iter-order] R1 - [key value left right] left [BTree-iter-order] R1 + [key value left right] R0 [BTree-iter-order] R1 + [key value left right] dup third [BTree-iter-order] R1 + [key value left right] left [BTree-iter-order] R1 Now maybe: :: - [key value left right] left [BTree-iter-order] [cons dip] dupdip - [key value left right] left [BTree-iter-order] cons dip [BTree-iter-order] - [key value left right] [left BTree-iter-order] dip [BTree-iter-order] - left BTree-iter-order [key value left right] [BTree-iter-order] + [key value left right] left [BTree-iter-order] [cons dip] dupdip + [key value left right] left [BTree-iter-order] cons dip [BTree-iter-order] + [key value left right] [left BTree-iter-order] dip [BTree-iter-order] + left BTree-iter-order [key value left right] [BTree-iter-order] Process the current node. ^^^^^^^^^^^^^^^^^^^^^^^^^ -So far, so good. Now we need to process the current node's values: +So far, so good. Now we need to process the current node’s values: :: - left BTree-iter-order [key value left right] [BTree-iter-order] [[F] dupdip] dip - left BTree-iter-order [key value left right] [F] dupdip [BTree-iter-order] - left BTree-iter-order [key value left right] F [key value left right] [BTree-iter-order] + left BTree-iter-order [key value left right] [BTree-iter-order] [[F] dupdip] dip + left BTree-iter-order [key value left right] [F] dupdip [BTree-iter-order] + left BTree-iter-order [key value left right] F [key value left right] [BTree-iter-order] If ``F`` needs items from the stack below the left stuff it should have -``cons``'d them before beginning maybe? For functions like ``first`` it -works fine as-is. +``cons``\ ’d them before beginning maybe? For functions like ``first`` +it works fine as-is. :: - left BTree-iter-order [key value left right] first [key value left right] [BTree-iter-order] - left BTree-iter-order key [key value left right] [BTree-iter-order] + left BTree-iter-order [key value left right] first [key value left right] [BTree-iter-order] + left BTree-iter-order key [key value left right] [BTree-iter-order] Process the right child. ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1023,16 +1023,16 @@ First ditch the rest of the node and get the right child: :: - left BTree-iter-order key [key value left right] [BTree-iter-order] [rest rest rest first] dip - left BTree-iter-order key right [BTree-iter-order] + left BTree-iter-order key [key value left right] [BTree-iter-order] [rest rest rest first] dip + left BTree-iter-order key right [BTree-iter-order] Then, of course, we just need ``i`` to run ``BTree-iter-order`` on the right side: :: - left BTree-iter-order key right [BTree-iter-order] i - left BTree-iter-order key right BTree-iter-order + left BTree-iter-order key right [BTree-iter-order] i + left BTree-iter-order key right BTree-iter-order Defining ``BTree-iter-order`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1041,19 +1041,19 @@ The result is a little awkward: :: - R1 == [cons dip] dupdip [[F] dupdip] dip [rest rest rest first] dip i + R1 == [cons dip] dupdip [[F] dupdip] dip [rest rest rest first] dip i -Let's do a little semantic factoring: +Let’s do a little semantic factoring: :: - fourth == rest rest rest first + fourth == rest rest rest first - proc_left == [cons dip] dupdip - proc_current == [[F] dupdip] dip - proc_right == [fourth] dip i + proc_left == [cons dip] dupdip + proc_current == [[F] dupdip] dip + proc_right == [fourth] dip i - BTree-iter-order == [not] [pop] [dup third] [proc_left proc_current proc_right] genrec + BTree-iter-order == [not] [pop] [dup third] [proc_left proc_current proc_right] genrec Now we can sort sequences. @@ -1074,14 +1074,14 @@ Now we can sort sequences. Getting values by key ===================== -Let's derive a function that accepts a tree and a key and returns the +Let’s derive a function that accepts a tree and a key and returns the value associated with that key. :: - tree key BTree-get - ------------------------ - value + tree key BTree-get + ------------------------ + value The base case ``[]`` ^^^^^^^^^^^^^^^^^^^^ @@ -1090,32 +1090,32 @@ As before, the stopping predicate just has to detect the empty list: :: - BTree-get == [pop not] [E] [R0] [R1] genrec + BTree-get == [pop not] [E] [R0] [R1] genrec -But what do we do if the key isn't in the tree? In Python we might raise -a ``KeyError`` but I'd like to avoid exceptions in Joy if possible, and -here I think it's possible. (Division by zero is an example of where I -think it's probably better to let Python crash Joy. Sometimes the -machinery fails and you have to "stop the line", methinks.) +But what do we do if the key isn’t in the tree? In Python we might raise +a ``KeyError`` but I’d like to avoid exceptions in Joy if possible, and +here I think it’s possible. (Division by zero is an example of where I +think it’s probably better to let Python crash Joy. Sometimes the +machinery fails and you have to “stop the line”, methinks.) -Let's pass the buck to the caller by making the base case a given, you +Let’s pass the buck to the caller by making the base case a given, you have to decide for yourself what ``[E]`` should be. :: - tree key [E] BTree-get - ---------------------------- key in tree - value + tree key [E] BTree-get + ---------------------------- key in tree + value - tree key [E] BTree-get - ---------------------------- key not in tree - tree key E + tree key [E] BTree-get + ---------------------------- key not in tree + tree key E Now we define: :: - BTree-get == [pop not] swap [R0] [R1] genrec + BTree-get == [pop not] swap [R0] [R1] genrec Note that this ``BTree-get`` creates a slightly different function than itself and *that function* does the actual recursion. This kind of @@ -1124,14 +1124,14 @@ Joy. :: - tree key [E] [pop not] swap [R0] [R1] genrec - tree key [pop not] [E] [R0] [R1] genrec + tree key [E] [pop not] swap [R0] [R1] genrec + tree key [pop not] [E] [R0] [R1] genrec The anonymous specialized recursive function that will do the real work. :: - [pop not] [E] [R0] [R1] genrec + [pop not] [E] [R0] [R1] genrec Node case ``[key value left right]`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1140,77 +1140,77 @@ Now we need to figure out ``R0`` and ``R1``: :: - [key value left right] key R0 [BTree-get] R1 + [key value left right] key R0 [BTree-get] R1 We want to compare the search key with the key in the node, and if they are the same return the value and if they differ then recurse on one of -the child nodes. So it's very similar to the above funtion, with +the child nodes. So it’s very similar to the above funtion, with ``[R0] == []`` and ``R1 == P [T>] [E] [T<] cmp``: :: - [key value left right] key [BTree-get] P [T>] [E] [T<] cmp + [key value left right] key [BTree-get] P [T>] [E] [T<] cmp So: :: - get-node-key == pop popop first - P == over [get-node-key] nullary + get-node-key == pop popop first + P == over [get-node-key] nullary The only difference is that ``get-node-key`` does one less ``pop`` -because there's no value to discard. Now we have to derive the branches: +because there’s no value to discard. Now we have to derive the branches: :: - [key_n value_n left right] key [BTree-get] T> - [key_n value_n left right] key [BTree-get] E - [key_n value_n left right] key [BTree-get] T< + [key_n value_n left right] key [BTree-get] T> + [key_n value_n left right] key [BTree-get] E + [key_n value_n left right] key [BTree-get] T< The cases of ``T>`` and ``T<`` are similar to above but instead of using ``infra`` we have to discard the rest of the structure: :: - [key_n value_n left right] key [BTree-get] T> == right key BTree-get - [key_n value_n left right] key [BTree-get] T< == left key BTree-get + [key_n value_n left right] key [BTree-get] T> == right key BTree-get + [key_n value_n left right] key [BTree-get] T< == left key BTree-get So: :: - T> == [fourth] dipd i - T< == [third] dipd i + T> == [fourth] dipd i + T< == [third] dipd i E.g.: :: - [key_n value_n left right] key [BTree-get] [fourth] dipd i - [key_n value_n left right] fourth key [BTree-get] i - right key [BTree-get] i - right key BTree-get + [key_n value_n left right] key [BTree-get] [fourth] dipd i + [key_n value_n left right] fourth key [BTree-get] i + right key [BTree-get] i + right key BTree-get And: :: - [key_n value_n left right] key [BTree-get] E == value_n + [key_n value_n left right] key [BTree-get] E == value_n - E == popop second + E == popop second So: :: - fourth == rest rest rest first - get-node-key == pop popop first - P == over [get-node-key] nullary - T> == [fourth] dipd i - T< == [third] dipd i - E == popop second + fourth == rest rest rest first + get-node-key == pop popop first + P == over [get-node-key] nullary + T> == [fourth] dipd i + T< == [third] dipd i + E == popop second - BTree-get == [pop not] swap [] [P [T>] [E] [T<] cmp] genrec + BTree-get == [pop not] swap [] [P [T>] [E] [T<] cmp] genrec .. code:: ipython2 @@ -1269,14 +1269,14 @@ So: BTree-delete ============ -Now let's write a function that can return a tree datastructure with a +Now let’s write a function that can return a tree datastructure with a key, value pair deleted: :: - tree key BTree-delete - --------------------------- - tree + tree key BTree-delete + --------------------------- + tree If the key is not in tree it just returns the tree unchanged. @@ -1284,102 +1284,102 @@ So: :: - BTree-Delete == [pop not] swap [R0] [R1] genrec + BTree-Delete == [pop not] swap [R0] [R1] genrec :: - [Er] BTree-delete - ------------------------------------- - [pop not] [Er] [R0] [R1] genrec + [Er] BTree-delete + ------------------------------------- + [pop not] [Er] [R0] [R1] genrec :: - [n_key n_value left right] [BTree-get] - [n_key n_value left right] [BTree-get] E - [n_key n_value left right] [BTree-get] T< + [n_key n_value left right] [BTree-get] + [n_key n_value left right] [BTree-get] E + [n_key n_value left right] [BTree-get] T< Now we get to figure out the recursive case: :: - w/ D == [pop not] [Er] [R0] [R1] genrec + w/ D == [pop not] [Er] [R0] [R1] genrec - [node_key node_value left right] key R0 [D] R1 - [node_key node_value left right] key over first swap dup [D] R1 - [node_key node_value left right] node_key key key [D] R1 + [node_key node_value left right] key R0 [D] R1 + [node_key node_value left right] key over first swap dup [D] R1 + [node_key node_value left right] node_key key key [D] R1 And then: :: - [node_key node_value left right] node_key key key [D] R1 - [node_key node_value left right] node_key key key [D] cons roll> [T>] [E] [T<] cmp - [node_key node_value left right] node_key key [key D] roll> [T>] [E] [T<] cmp - [node_key node_value left right] [key D] node_key key [T>] [E] [T<] cmp + [node_key node_value left right] node_key key key [D] R1 + [node_key node_value left right] node_key key key [D] cons roll> [T>] [E] [T<] cmp + [node_key node_value left right] node_key key [key D] roll> [T>] [E] [T<] cmp + [node_key node_value left right] [key D] node_key key [T>] [E] [T<] cmp Now this:; :: - [node_key node_value left right] [key D] node_key key [T>] [E] [T<] cmp + [node_key node_value left right] [key D] node_key key [T>] [E] [T<] cmp Becomes one of these three:; :: - [node_key node_value left right] [key D] T> - [node_key node_value left right] [key D] E - [node_key node_value left right] [key D] T< + [node_key node_value left right] [key D] T> + [node_key node_value left right] [key D] E + [node_key node_value left right] [key D] T< Greater than case and less than case ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: - [node_key node_value left right] [key D] T> - ------------------------------------------------- - [node_key node_value left key D right] + [node_key node_value left right] [key D] T> + ------------------------------------------------- + [node_key node_value left key D right] First: :: - right left node_value node_key [key D] dipd - right left key D node_value node_key - right left' node_value node_key + right left node_value node_key [key D] dipd + right left key D node_value node_key + right left' node_value node_key Ergo: :: - [node_key node_value left right] [key D] [dipd] cons infra + [node_key node_value left right] [key D] [dipd] cons infra So: :: - T> == [dipd] cons infra - T< == [dipdd] cons infra + T> == [dipd] cons infra + T< == [dipdd] cons infra The else case ~~~~~~~~~~~~~ :: - [node_key node_value left right] [key D] E + [node_key node_value left right] [key D] E -We have to handle three cases, so let's use ``cond``. +We have to handle three cases, so let’s use ``cond``. The first two cases are symmetrical, if we only have one non-empty child node return it. :: - E == [ - [[pop third not] pop fourth] - [[pop fourth not] pop third] - [default] - ] cond + E == [ + [[pop third not] pop fourth] + [[pop fourth not] pop third] + [default] + ] cond (If both child nodes are empty return an empty node.) @@ -1387,13 +1387,13 @@ The initial structure of the default function: :: - default == [E'] cons infra + default == [E'] cons infra - [node_key node_value left right] [key D] default - [node_key node_value left right] [key D] [E'] cons infra - [node_key node_value left right] [[key D] E'] infra + [node_key node_value left right] [key D] default + [node_key node_value left right] [key D] [E'] cons infra + [node_key node_value left right] [[key D] E'] infra - right left node_value node_key [key D] E' + right left node_value node_key [key D] E' If both child nodes are non-empty, we find the highest node in our lower sub-tree, take its key and value to replace (delete) our own, then get @@ -1405,187 +1405,187 @@ key and value and delete it. I only implemented one of these two symmetrical options. Over a lot of deletions this might make the tree more unbalanced. Oh well.) -First things first, we no longer need this node's key and value: +First things first, we no longer need this node’s key and value: :: - right left node_value node_key [key D] roll> popop E'' - right left [key D] node_value node_key popop E'' - right left [key D] E'' + right left node_value node_key [key D] roll> popop E'' + right left [key D] node_value node_key popop E'' + right left [key D] E'' Then we have to we find the highest (right-most) node in our lower (left) sub-tree: :: - right left [key D] E'' + right left [key D] E'' Ditch the key: :: - right left [key D] rest E''' - right left [D] E''' + right left [key D] rest E''' + right left [D] E''' Find the right-most node: :: - right left [D] [dup W] dip E'''' - right left dup W [D] E'''' - right left left W [D] E'''' + right left [D] [dup W] dip E'''' + right left dup W [D] E'''' + right left left W [D] E'''' Consider: :: - left W + left W We know left is not empty: :: - [L_key L_value L_left L_right] W + [L_key L_value L_left L_right] W We want to keep extracting the right node as long as it is not empty: :: - left [P] [B] while W' + left [P] [B] while W' The predicate: :: - [L_key L_value L_left L_right] P - [L_key L_value L_left L_right] fourth - L_right - + [L_key L_value L_left L_right] P + [L_key L_value L_left L_right] fourth + L_right + (This has a bug, can run on ``[]`` so must be guarded: :: - if_not_empty == [] swap [] ifte - ?fourth == [fourth] if_not_empty - W.rightmost == [?fourth] [fourth] while + if_not_empty == [] swap [] ifte + ?fourth == [fourth] if_not_empty + W.rightmost == [?fourth] [fourth] while The body is also ``fourth``: :: - left [fourth] [fourth] while W' - rightest W' + left [fourth] [fourth] while W' + rightest W' We know rightest is not empty: :: - [R_key R_value R_left R_right] W' - [R_key R_value R_left R_right] uncons uncons pop - R_key [R_value R_left R_right] uncons pop - R_key R_value [R_left R_right] pop - R_key R_value + [R_key R_value R_left R_right] W' + [R_key R_value R_left R_right] uncons uncons pop + R_key [R_value R_left R_right] uncons pop + R_key R_value [R_left R_right] pop + R_key R_value So: :: - W == [fourth] [fourth] while uncons uncons pop + W == [fourth] [fourth] while uncons uncons pop And: :: - right left left W [D] E'''' - right left R_key R_value [D] E'''' + right left left W [D] E'''' + right left R_key R_value [D] E'''' Final stretch. We want to end up with something like: :: - right left [R_key D] i R_value R_key - right left R_key D R_value R_key - right left' R_value R_key + right left [R_key D] i R_value R_key + right left R_key D R_value R_key + right left' R_value R_key If we adjust our definition of ``W`` to include ``over`` at the end: :: - W == [fourth] [fourth] while uncons uncons pop over + W == [fourth] [fourth] while uncons uncons pop over That will give us: :: - right left R_key R_value R_key [D] E'''' + right left R_key R_value R_key [D] E'''' - right left R_key R_value R_key [D] cons dipdd E''''' - right left R_key R_value [R_key D] dipdd E''''' - right left R_key D R_key R_value E''''' - right left' R_key R_value E''''' - right left' R_key R_value swap - right left' R_value R_key + right left R_key R_value R_key [D] cons dipdd E''''' + right left R_key R_value [R_key D] dipdd E''''' + right left R_key D R_key R_value E''''' + right left' R_key R_value E''''' + right left' R_key R_value swap + right left' R_value R_key So: :: - E' == roll> popop E'' + E' == roll> popop E'' - E'' == rest E''' + E'' == rest E''' - E''' == [dup W] dip E'''' + E''' == [dup W] dip E'''' - E'''' == cons dipdd swap + E'''' == cons dipdd swap Substituting: :: - W == [fourth] [fourth] while uncons uncons pop over - E' == roll> popop rest [dup W] dip cons dipdd swap - E == [ - [[pop third not] pop fourth] - [[pop fourth not] pop third] - [[E'] cons infra] - ] cond + W == [fourth] [fourth] while uncons uncons pop over + E' == roll> popop rest [dup W] dip cons dipdd swap + E == [ + [[pop third not] pop fourth] + [[pop fourth not] pop third] + [[E'] cons infra] + ] cond Minor rearrangement: :: - W == dup [fourth] [fourth] while uncons uncons pop over - E' == roll> popop rest [W] dip cons dipdd swap - E == [ - [[pop third not] pop fourth] - [[pop fourth not] pop third] - [[E'] cons infra] - ] cond + W == dup [fourth] [fourth] while uncons uncons pop over + E' == roll> popop rest [W] dip cons dipdd swap + E == [ + [[pop third not] pop fourth] + [[pop fourth not] pop third] + [[E'] cons infra] + ] cond Refactoring ~~~~~~~~~~~ :: - W.rightmost == [fourth] [fourth] while - W.unpack == uncons uncons pop - E.clear_stuff == roll> popop rest - E.delete == cons dipdd - W == dup W.rightmost W.unpack over - E.0 == E.clear_stuff [W] dip E.delete swap - E == [ - [[pop third not] pop fourth] - [[pop fourth not] pop third] - [[E.0] cons infra] - ] cond - T> == [dipd] cons infra - T< == [dipdd] cons infra - R0 == over first swap dup - R1 == cons roll> [T>] [E] [T<] cmp - BTree-Delete == [pop not] swap [R0] [R1] genrec + W.rightmost == [fourth] [fourth] while + W.unpack == uncons uncons pop + E.clear_stuff == roll> popop rest + E.delete == cons dipdd + W == dup W.rightmost W.unpack over + E.0 == E.clear_stuff [W] dip E.delete swap + E == [ + [[pop third not] pop fourth] + [[pop fourth not] pop third] + [[E.0] cons infra] + ] cond + T> == [dipd] cons infra + T< == [dipdd] cons infra + R0 == over first swap dup + R1 == cons roll> [T>] [E] [T<] cmp + BTree-Delete == [pop not] swap [R0] [R1] genrec -By the standards of the code I've written so far, this is a *huge* Joy +By the standards of the code I’ve written so far, this is a *huge* Joy program. .. code:: ipython2 @@ -1662,7 +1662,7 @@ One bug, I forgot to put ``not`` in the first two clauses of the The behavior of the ``[Er]`` function should maybe be different: either just silently fail, or maybe implement some sort of function that can grab the pending expression up to a sentinel value or something, -allowing for a kind of "except"-ish control-flow? +allowing for a kind of “except”-ish control-flow? Then, once we have add, get, and delete we can see about abstracting them. @@ -1670,8 +1670,8 @@ them. Tree with node and list of trees. ================================= -Let's consider a tree structure, similar to one described `"Why -functional programming matters" by John +Let’s consider a tree structure, similar to one described `“Why +functional programming matters” by John Hughes `__, that consists of a node value and a sequence of zero or more child trees. (The asterisk is meant to indicate the `Kleene @@ -1679,7 +1679,7 @@ star `__.) :: - tree = [] | [node [tree*]] + tree = [] | [node [tree*]] ``treestep`` ~~~~~~~~~~~~ @@ -1690,16 +1690,16 @@ base-case value ``z``, and two quoted programs ``[C]`` and ``[N]``. :: - tree z [C] [N] treestep + tree z [C] [N] treestep If the current tree node is empty then just leave ``z`` on the stack in lieu: :: - [] z [C] [N] treestep - --------------------------- - z + [] z [C] [N] treestep + --------------------------- + z Otherwise, evaluate ``N`` on the node value, ``map`` the whole function (abbreviated here as ``k``) over the child trees recursively, and then @@ -1707,9 +1707,9 @@ combine the result with ``C``. :: - [node [tree*]] z [C] [N] treestep - --------------------------------------- w/ K == z [C] [N] treestep - node N [tree*] [K] map C + [node [tree*]] z [C] [N] treestep + --------------------------------------- w/ K == z [C] [N] treestep + node N [tree*] [K] map C Derive the recursive form. ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1720,75 +1720,75 @@ base-case functions are trivial, so we just have to derive ``J``. :: - K == [not] [pop z] [J] ifte + K == [not] [pop z] [J] ifte 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``. +Let’s dive in. First, unquote the node and ``dip`` ``N``. :: - [node [tree*]] i [N] dip - node [tree*] [N] dip - node N [tree*] + [node [tree*]] i [N] dip + node [tree*] [N] dip + node N [tree*] Next, ``map`` ``K`` over teh 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 == i [N] dip [K] map C + J == i [N] dip [K] map C Plug it in and convert to ``genrec``: :: - K == [not] [pop z] [i [N] dip [K] map C] ifte - K == [not] [pop z] [i [N] dip] [map C] genrec + K == [not] [pop z] [i [N] dip [K] map C] ifte + K == [not] [pop z] [i [N] dip] [map C] genrec Extract the givens to parameterize the program. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: - [not] [pop z] [i [N] dip] [map C] genrec + [not] [pop z] [i [N] dip] [map C] genrec - [not] [pop z] [i [N] dip] [map C] genrec - [not] [z] [pop] swoncat [i [N] dip] [map C] genrec - [not] z unit [pop] swoncat [i [N] dip] [map C] genrec - z [not] swap unit [pop] swoncat [i [N] dip] [map C] genrec - \ .........TS0............./ - \/ - z TS0 [i [N] dip] [map C] genrec - z [i [N] dip] [TS0] dip [map C] genrec - z [[N] dip] [i] swoncat [TS0] dip [map C] genrec - z [N] [dip] cons [i] swoncat [TS0] dip [map C] genrec - \ ......TS1........./ - \/ - z [N] TS1 [TS0] dip [map C] genrec - z [N] [map C] [TS1 [TS0] dip] dip genrec - z [N] [C] [map] swoncat [TS1 [TS0] dip] dip genrec - z [C] [N] swap [map] swoncat [TS1 [TS0] dip] dip genrec + [not] [pop z] [i [N] dip] [map C] genrec + [not] [z] [pop] swoncat [i [N] dip] [map C] genrec + [not] z unit [pop] swoncat [i [N] dip] [map C] genrec + z [not] swap unit [pop] swoncat [i [N] dip] [map C] genrec + \ .........TS0............./ + \/ + z TS0 [i [N] dip] [map C] genrec + z [i [N] dip] [TS0] dip [map C] genrec + z [[N] dip] [i] swoncat [TS0] dip [map C] genrec + z [N] [dip] cons [i] swoncat [TS0] dip [map C] genrec + \ ......TS1........./ + \/ + z [N] TS1 [TS0] dip [map C] genrec + z [N] [map C] [TS1 [TS0] dip] dip genrec + z [N] [C] [map] swoncat [TS1 [TS0] dip] dip genrec + z [C] [N] swap [map] swoncat [TS1 [TS0] dip] dip genrec The givens are all to the left so we have our definition. @@ -1797,9 +1797,9 @@ Define ``treestep`` :: - TS0 == [not] swap unit [pop] swoncat - TS1 == [dip] cons [i] swoncat - treestep == swap [map] swoncat [TS1 [TS0] dip] dip genrec + TS0 == [not] swap unit [pop] swoncat + TS1 == [dip] cons [i] swoncat + treestep == swap [map] swoncat [TS1 [TS0] dip] dip genrec .. code:: ipython2 @@ -1813,14 +1813,14 @@ Define ``treestep`` :: - [] 0 [C] [N] treestep - --------------------------- - 0 + [] 0 [C] [N] treestep + --------------------------- + 0 - [n [tree*]] 0 [sum +] [] treestep - -------------------------------------------------- - n [tree*] [0 [sum +] [] treestep] map sum + + [n [tree*]] 0 [sum +] [] treestep + -------------------------------------------------- + n [tree*] [0 [sum +] [] treestep] map sum + .. code:: ipython2 @@ -1855,31 +1855,31 @@ Define ``treestep`` A slight modification. ---------------------- -Let's simplify the tree datastructure definition slightly by just +Let’s simplify the tree datastructure definition slightly by just letting the children be the ``rest`` of the tree: :: - tree = [] | [node tree*] + tree = [] | [node tree*] The ``J`` function changes slightly. :: - [node tree*] J - ------------------------------ - node N [tree*] [K] map C + [node tree*] J + ------------------------------ + node N [tree*] [K] map C - [node tree*] uncons [N] dip [K] map C - node [tree*] [N] dip [K] map C - node N [tree*] [K] map C - node N [tree*] [K] map C - node N [K.tree*] C + [node tree*] uncons [N] dip [K] map C + node [tree*] [N] dip [K] map C + node N [tree*] [K] map C + node N [tree*] [K] map C + node N [K.tree*] C - J == uncons [N] dip [K] map C + J == uncons [N] dip [K] map C - K == [not] [pop z] [uncons [N] dip] [map C] genrec + K == [not] [pop z] [uncons [N] dip] [map C] genrec .. code:: ipython2 @@ -1912,27 +1912,27 @@ Redefining our BTree in terms of this form. :: - BTree = [] | [[key value] left right] + BTree = [] | [[key value] left right] What kind of functions can we write for this with our ``treestep``? 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 - [key value] uncons pop [left right] [K] map i - key [value] pop [left right] [K] map i - key [left right] [K] map i - key [lkey rkey ] i - key lkey rkey + [key value] uncons pop [left right] [K] map i + key [value] pop [left right] [K] map i + key [left right] [K] map i + key [lkey rkey ] i + key lkey rkey .. code:: ipython2 @@ -1944,25 +1944,25 @@ Plugging in our BTree structure: 3 23 23 -Doesn't work because ``map`` extracts the ``first`` item of whatever its +Doesn’t 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: :: - [] [flatten cons] [first] treestep + [] [flatten cons] [first] treestep .. code:: ipython2 @@ -1980,18 +1980,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 @@ -2009,66 +2009,66 @@ Miscellaneous Crap Toy with it. ~~~~~~~~~~~~ -Let's reexamine: +Let’s reexamine: :: - [key value left right] R0 [BTree-iter-order] R1 - ... - left BTree-iter-order key value F right BTree-iter-order + [key value left right] R0 [BTree-iter-order] R1 + ... + left BTree-iter-order key value F right BTree-iter-order - [key value left right] unstack swap - key value left right swap - key value right left + [key value left right] unstack swap + key value left right swap + key value right left - key value right left [BTree-iter-order] [cons dipdd] dupdip - key value right left [BTree-iter-order] cons dipdd [BTree-iter-order] - key value right [left BTree-iter-order] dipdd [BTree-iter-order] - left BTree-iter-order key value right [BTree-iter-order] + key value right left [BTree-iter-order] [cons dipdd] dupdip + key value right left [BTree-iter-order] cons dipdd [BTree-iter-order] + key value right [left BTree-iter-order] dipdd [BTree-iter-order] + left BTree-iter-order key value right [BTree-iter-order] - left BTree-iter-order key value right [F] dip [BTree-iter-order] - left BTree-iter-order key value F right [BTree-iter-order] i - left BTree-iter-order key value F right BTree-iter-order + left BTree-iter-order key value right [F] dip [BTree-iter-order] + left BTree-iter-order key value F right [BTree-iter-order] i + left BTree-iter-order key value F right BTree-iter-order So: :: - R0 == unstack swap - R1 == [cons dipdd [F] dip] dupdip i + R0 == unstack swap + R1 == [cons dipdd [F] dip] dupdip i - [key value left right] R0 [BTree-iter-order] R1 - [key value left right] unstack swap [BTree-iter-order] [cons dipdd [F] dip] dupdip i - key value right left [BTree-iter-order] [cons dipdd [F] dip] dupdip i + [key value left right] R0 [BTree-iter-order] R1 + [key value left right] unstack swap [BTree-iter-order] [cons dipdd [F] dip] dupdip i + key value right left [BTree-iter-order] [cons dipdd [F] dip] dupdip i - key value right left [BTree-iter-order] cons dipdd [F] dip [BTree-iter-order] i - key value right [left BTree-iter-order] dipdd [F] dip [BTree-iter-order] i - left BTree-iter-order key value right [F] dip [BTree-iter-order] i - left BTree-iter-order key value F right [BTree-iter-order] i - left BTree-iter-order key value F right BTree-iter-order + key value right left [BTree-iter-order] cons dipdd [F] dip [BTree-iter-order] i + key value right [left BTree-iter-order] dipdd [F] dip [BTree-iter-order] i + left BTree-iter-order key value right [F] dip [BTree-iter-order] i + left BTree-iter-order key value F right [BTree-iter-order] i + left BTree-iter-order key value F right BTree-iter-order - BTree-iter-order == [not] [pop] [unstack swap] [[cons dipdd [F] dip] dupdip i] genrec + BTree-iter-order == [not] [pop] [unstack swap] [[cons dipdd [F] dip] dupdip i] genrec Refactor ``cons cons`` ^^^^^^^^^^^^^^^^^^^^^^ :: - cons2 == cons cons + cons2 == cons cons Refactoring: :: - BTree-new == swap [[] []] cons2 - T == [cons2 dipdd] cons2 cons infra - Te == [cons2 dipd] cons2 cons infra - Ee == pop swap roll< rest rest cons2 + BTree-new == swap [[] []] cons2 + T == [cons2 dipdd] cons2 cons infra + Te == [cons2 dipd] cons2 cons infra + Ee == pop swap roll< rest rest cons2 -It's used a lot because it's tied to the fact that there are two "data -items" in each node. This point to a more general factorization that +It’s used a lot because it’s tied to the fact that there are two “data +items” in each node. This point to a more general factorization that would render a combinator that could work for other geometries of trees. A General Form for Trees @@ -2078,18 +2078,18 @@ A general form for tree data with N children per node: :: - [[data] [child0] ... [childN-1]] + [[data] [child0] ... [childN-1]] Suggests a general form of recursive iterator, but I have to go walk the -dogs at the mo'. +dogs at the mo’. For a given structure, you would have a structure of operator functions and sort of merge them and run them, possibly in a different order (pre- -post- in- y'know). The ``Cn`` functions could all be the same and use +post- in- y’know). The ``Cn`` functions could all be the same and use the ``step`` trick if the children nodes are all of the right kind. If they are heterogeneous then we need a way to get the different ``Cn`` into the structure in the right order. If I understand correctly, the -"Bananas..." paper shows how to do this automatically from a type +“Bananas…” paper shows how to do this automatically from a type description. They present, if I have it right, a tiny machine that accepts `some sort of algebraic data type description and returns a function that can recusre over @@ -2098,11 +2098,11 @@ think. :: - [data.. [c0] [c1] ... [cN]] [F C0 C1 ... CN] infil - -------------------------------------------------------- - data F [c0] C0 [c1] C1 ... [cN] CN - - + [data.. [c0] [c1] ... [cN]] [F C0 C1 ... CN] infil + -------------------------------------------------------- + data F [c0] C0 [c1] C1 ... [cN] CN + + Just make ``[F]`` a parameter. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -2111,102 +2111,102 @@ We can generalize to a sort of pure form: :: - BTree-iter == [not] [pop] [[F]] [R1] genrec - == [not] [pop] [[F] [BTree-iter] R1] ifte + BTree-iter == [not] [pop] [[F]] [R1] genrec + == [not] [pop] [[F] [BTree-iter] R1] ifte Putting ``[F]`` to the left as a given: :: - [F] unit [not] [pop] roll< [R1] genrec - [[F]] [not] [pop] roll< [R1] genrec - [not] [pop] [[F]] [R1] genrec + [F] unit [not] [pop] roll< [R1] genrec + [[F]] [not] [pop] roll< [R1] genrec + [not] [pop] [[F]] [R1] genrec -Let's us define a parameterized form: +Let’s us define a parameterized form: :: - BTree-iter == unit [not] [pop] roll< [R1] genrec + BTree-iter == unit [not] [pop] roll< [R1] genrec So in the general case of non-empty nodes: :: - [key value left right] [F] [BTree-iter] R1 + [key value left right] [F] [BTree-iter] R1 We just define ``R1`` to do whatever it has to to process the node. For example: :: - [key value left right] [F] [BTree-iter] R1 - ... - key value F left BTree-iter right BTree-iter - left BTree-iter key value F right BTree-iter - left BTree-iter right BTree-iter key value F + [key value left right] [F] [BTree-iter] R1 + ... + key value F left BTree-iter right BTree-iter + left BTree-iter key value F right BTree-iter + left BTree-iter right BTree-iter key value F Pre-, ??-, post-order traversals. :: - [key value left right] uncons uncons - key value [left right] + [key value left right] uncons uncons + key value [left right] For pre- and post-order we can use the ``step`` trick: :: - [left right] [BTree-iter] step - ... - left BTree-iter right BTree-iter + [left right] [BTree-iter] step + ... + left BTree-iter right BTree-iter We worked out one scheme for ?in-order? traversal above, but maybe we can do better? :: - [key value left right] [F] [BTree-iter] [unstack] dipd - [key value left right] unstack [F] [BTree-iter] - key value left right [F] [BTree-iter] + [key value left right] [F] [BTree-iter] [unstack] dipd + [key value left right] unstack [F] [BTree-iter] + key value left right [F] [BTree-iter] - key value left right [F] [BTree-iter] R1.1 + key value left right [F] [BTree-iter] R1.1 -Hmm... +Hmm… :: - key value left right [F] [BTree-iter] tuck - key value left right [BTree-iter] [F] [BTree-iter] + key value left right [F] [BTree-iter] tuck + key value left right [BTree-iter] [F] [BTree-iter] - [key value left right] [F] [BTree-iter] [unstack [roll>] dip] dipd - [key value left right] unstack [roll>] dip [F] [BTree-iter] - key value left right [roll>] dip [F] [BTree-iter] - key value left roll> right [F] [BTree-iter] - left key value right [F] [BTree-iter] + [key value left right] [F] [BTree-iter] [unstack [roll>] dip] dipd + [key value left right] unstack [roll>] dip [F] [BTree-iter] + key value left right [roll>] dip [F] [BTree-iter] + key value left roll> right [F] [BTree-iter] + left key value right [F] [BTree-iter] - left key value right [F] [BTree-iter] tuck foo - left key value right [BTree-iter] [F] [BTree-iter] foo - ... - left BTree-iter key value F right BTree-iter + left key value right [F] [BTree-iter] tuck foo + left key value right [BTree-iter] [F] [BTree-iter] foo + ... + left BTree-iter key value F right BTree-iter We could just let ``[R1]`` be a parameter too, for maximum flexibility. Automatically deriving the recursion combinator for a data type? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -If I understand it correctly, the "Bananas..." paper talks about a way -to build the processor function automatically from the description of -the type. I think if we came up with an elegant way for the Joy code to +If I understand it correctly, the “Bananas…” paper talks about a way to +build the processor function automatically from the description of the +type. I think if we came up with an elegant way for the Joy code to express that, it would be cool. In Joypy the definitions can be circular because lookup happens at evaluation, not parsing. E.g.: :: - A == ... B ... - B == ... A ... + A == ... B ... + B == ... A ... -That's fine. Circular datastructures can't be made though. +That’s fine. Circular datastructures can’t be made though. diff --git a/docs/Treestep.rst b/docs/Treestep.rst index 853e80d..6b9081f 100644 --- a/docs/Treestep.rst +++ b/docs/Treestep.rst @@ -1,8 +1,8 @@ Treating Trees II: ``treestep`` =============================== -Let's consider a tree structure, similar to one described `"Why -functional programming matters" by John +Let’s consider a tree structure, similar to one described `“Why +functional programming matters” by John Hughes `__, 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 `__.) :: - 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 we’ll 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``. +Let’s 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 doesn’t 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 +Doesn’t 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 doesn’t 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 +We’ll 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``: +Let’s 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. diff --git a/docs/TypeChecking.rst b/docs/TypeChecking.rst index ea8dd5c..cd85c67 100644 --- a/docs/TypeChecking.rst +++ b/docs/TypeChecking.rst @@ -98,7 +98,7 @@ An Example (... [3 4 ] 2 1 0 -- ... [1 2 ]) -Unification Works "in Reverse" +Unification Works “in Reverse” ------------------------------ .. code:: ipython2 diff --git a/docs/Types.rst b/docs/Types.rst index 8c76e83..4c91600 100644 --- a/docs/Types.rst +++ b/docs/Types.rst @@ -2,35 +2,35 @@ The Blissful Elegance of Typing Joy =================================== This notebook presents a simple type inferencer for Joy code. It can -infer the stack effect of most Joy expressions. It's built largely by +infer the stack effect of most Joy expressions. It’s built largely by means of existing ideas and research. (A great overview of the existing -knowledge is a talk `"Type Inference in Stack-Based Programming -Languages" `__ +knowledge is a talk `“Type Inference in Stack-Based Programming +Languages” `__ given by Rob Kleffner on or about 2017-03-10 as part of a course on the history of programming languages.) The notebook starts with a simple inferencer based on the work of Jaanus Pöial which we then progressively elaborate to cover more Joy semantics. -Along the way we write a simple "compiler" that emits Python code for +Along the way we write a simple “compiler” that emits Python code for what I like to call Yin functions. (Yin functions are those that only rearrange values in stacks, as opposed to Yang functions that actually work on the values themselves.) -Part I: Pöial's Rules +Part I: Pöial’s Rules --------------------- -`"Typing Tools for Typeless Stack Languages" by Jaanus +`“Typing Tools for Typeless Stack Languages” by Jaanus Pöial `__ :: - @INPROCEEDINGS{Pöial06typingtools, - author = {Jaanus Pöial}, - title = {Typing tools for typeless stack languages}, - booktitle = {In 23rd Euro-Forth Conference}, - year = {2006}, - pages = {40--46} - } + @INPROCEEDINGS{Pöial06typingtools, + author = {Jaanus Pöial}, + title = {Typing tools for typeless stack languages}, + booktitle = {In 23rd Euro-Forth Conference}, + year = {2006}, + pages = {40--46} + } First Rule ~~~~~~~~~~ @@ -40,9 +40,9 @@ stack ``(-- d)``: :: - (a -- b)∘(-- d) - --------------------- - (a -- b d) + (a -- b)∘(-- d) + --------------------- + (a -- b d) Second Rule ~~~~~~~~~~~ @@ -52,9 +52,9 @@ This rule deals with functions that consume items from the stack :: - (a --)∘(c -- d) - --------------------- - (c a -- d) + (a --)∘(c -- d) + --------------------- + (c a -- d) Third Rule ~~~~~~~~~~ @@ -62,84 +62,84 @@ Third Rule The third rule is actually two rules. These two rules deal with composing functions when the second one will consume one of items the first one produces. The two types must be -`*unified* `__ +`unified `__ or a type conflict declared. :: - (a -- b t[i])∘(c u[j] -- d) t <= u (t is subtype of u) - ------------------------------- - (a -- b )∘(c -- d) t[i] == t[k] == u[j] - ^ + (a -- b t[i])∘(c u[j] -- d) t <= u (t is subtype of u) + ------------------------------- + (a -- b )∘(c -- d) t[i] == t[k] == u[j] + ^ - (a -- b t[i])∘(c u[j] -- d) u <= t (u is subtype of t) - ------------------------------- - (a -- b )∘(c -- d) t[i] == u[k] == u[j] + (a -- b t[i])∘(c u[j] -- d) u <= t (u is subtype of t) + ------------------------------- + (a -- b )∘(c -- d) t[i] == u[k] == u[j] -Let's work through some examples by hand to develop an intuition for the +Let’s work through some examples by hand to develop an intuition for the algorithm. -There's a function in one of the other notebooks. +There’s a function in one of the other notebooks. :: - F == pop swap roll< rest rest cons cons + F == pop swap roll< rest rest cons cons -It's all "stack chatter" and list manipulation so we should be able to +It’s all “stack chatter” and list manipulation so we should be able to deduce its type. Stack Effect Comments ~~~~~~~~~~~~~~~~~~~~~ Joy function types will be represented by Forth-style stack effect -comments. I'm going to use numbers instead of names to keep track of the +comments. I’m going to use numbers instead of names to keep track of the stack arguments. (A little bit like `De Bruijn index `__, at least it reminds me of them): :: - pop (1 --) + pop (1 --) - swap (1 2 -- 2 1) + swap (1 2 -- 2 1) - roll< (1 2 3 -- 2 3 1) + roll< (1 2 3 -- 2 3 1) -These commands alter the stack but don't "look at" the values so these -numbers represent an "Any type". +These commands alter the stack but don’t “look at” the values so these +numbers represent an “Any type”. ``pop swap`` ~~~~~~~~~~~~ :: - (1 --) (1 2 -- 2 1) + (1 --) (1 2 -- 2 1) Here we encounter a complication. The argument numbers need to be made -unique among both sides. For this let's change ``pop`` to use 0: +unique among both sides. For this let’s change ``pop`` to use 0: :: - (0 --) (1 2 -- 2 1) + (0 --) (1 2 -- 2 1) Following the second rule: :: - (1 2 0 -- 2 1) + (1 2 0 -- 2 1) ``pop∘swap roll<`` ~~~~~~~~~~~~~~~~~~ :: - (1 2 0 -- 2 1) (1 2 3 -- 2 3 1) + (1 2 0 -- 2 1) (1 2 3 -- 2 3 1) -Let's re-label them: +Let’s re-label them: :: - (1a 2a 0a -- 2a 1a) (1b 2b 3b -- 2b 3b 1b) + (1a 2a 0a -- 2a 1a) (1b 2b 3b -- 2b 3b 1b) Now we follow the rules. @@ -148,41 +148,41 @@ terms in the forms: :: - (1a 2a 0a -- 2a 1a) (1b 2b 3b -- 2b 3b 1b) - w/ {1a: 3b} - (3b 2a 0a -- 2a ) (1b 2b -- 2b 3b 1b) - w/ {2a: 2b} - (3b 2b 0a -- ) (1b -- 2b 3b 1b) + (1a 2a 0a -- 2a 1a) (1b 2b 3b -- 2b 3b 1b) + w/ {1a: 3b} + (3b 2a 0a -- 2a ) (1b 2b -- 2b 3b 1b) + w/ {2a: 2b} + (3b 2b 0a -- ) (1b -- 2b 3b 1b) Here we must apply the second rule: :: - (3b 2b 0a --) (1b -- 2b 3b 1b) - ----------------------------------- - (1b 3b 2b 0a -- 2b 3b 1b) + (3b 2b 0a --) (1b -- 2b 3b 1b) + ----------------------------------- + (1b 3b 2b 0a -- 2b 3b 1b) Now we de-label the type, uh, labels: :: - (1b 3b 2b 0a -- 2b 3b 1b) + (1b 3b 2b 0a -- 2b 3b 1b) - w/ { - 1b: 1, - 3b: 2, - 2b: 3, - 0a: 0, - } + w/ { + 1b: 1, + 3b: 2, + 2b: 3, + 0a: 0, + } - (1 2 3 0 -- 3 2 1) + (1 2 3 0 -- 3 2 1) And now we have the stack effect comment for ``pop∘swap∘roll<``. Compiling ``pop∘swap∘roll<`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The simplest way to "compile" this function would be something like: +The simplest way to “compile” this function would be something like: .. code:: ipython2 @@ -196,7 +196,7 @@ Looking ahead for a moment, from the stack effect comment: :: - (1 2 3 0 -- 3 2 1) + (1 2 3 0 -- 3 2 1) We should be able to directly write out a Python function like: @@ -207,7 +207,7 @@ We should be able to directly write out a Python function like: return (c, (b, (a, stack))) This eliminates the internal work of the first version. Because this -function only rearranges the stack and doesn't do any actual processing +function only rearranges the stack and doesn’t do any actual processing on the stack items themselves all the information needed to implement it is in the stack effect comment. @@ -218,94 +218,94 @@ These are slightly tricky. :: - rest ( [1 ...] -- [...] ) + rest ( [1 ...] -- [...] ) - cons ( 1 [...] -- [1 ...] ) + cons ( 1 [...] -- [1 ...] ) ``pop∘swap∘roll< rest`` ~~~~~~~~~~~~~~~~~~~~~~~ :: - (1 2 3 0 -- 3 2 1) ([1 ...] -- [...]) + (1 2 3 0 -- 3 2 1) ([1 ...] -- [...]) -Re-label (instead of adding left and right tags I'm just taking the next +Re-label (instead of adding left and right tags I’m just taking the next available index number for the right-side stack effect comment): :: - (1 2 3 0 -- 3 2 1) ([4 ...] -- [...]) + (1 2 3 0 -- 3 2 1) ([4 ...] -- [...]) Unify and update: :: - (1 2 3 0 -- 3 2 1) ([4 ...] -- [...]) - w/ {1: [4 ...]} - ([4 ...] 2 3 0 -- 3 2 ) ( -- [...]) + (1 2 3 0 -- 3 2 1) ([4 ...] -- [...]) + w/ {1: [4 ...]} + ([4 ...] 2 3 0 -- 3 2 ) ( -- [...]) Apply the first rule: :: - ([4 ...] 2 3 0 -- 3 2) (-- [...]) - --------------------------------------- - ([4 ...] 2 3 0 -- 3 2 [...]) + ([4 ...] 2 3 0 -- 3 2) (-- [...]) + --------------------------------------- + ([4 ...] 2 3 0 -- 3 2 [...]) And there we are. ``pop∘swap∘roll<∘rest rest`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Let's do it again. +Let’s do it again. :: - ([4 ...] 2 3 0 -- 3 2 [...]) ([1 ...] -- [...]) + ([4 ...] 2 3 0 -- 3 2 [...]) ([1 ...] -- [...]) Re-label (the tails of the lists on each side each get their own label): :: - ([4 .0.] 2 3 0 -- 3 2 [.0.]) ([5 .1.] -- [.1.]) + ([4 .0.] 2 3 0 -- 3 2 [.0.]) ([5 .1.] -- [.1.]) Unify and update (note the opening square brackets have been omited in -the substitution dict, this is deliberate and I'll explain below): +the substitution dict, this is deliberate and I’ll explain below): :: - ([4 .0.] 2 3 0 -- 3 2 [.0.] ) ([5 .1.] -- [.1.]) - w/ { .0.] : 5 .1.] } - ([4 5 .1.] 2 3 0 -- 3 2 [5 .1.]) ([5 .1.] -- [.1.]) + ([4 .0.] 2 3 0 -- 3 2 [.0.] ) ([5 .1.] -- [.1.]) + w/ { .0.] : 5 .1.] } + ([4 5 .1.] 2 3 0 -- 3 2 [5 .1.]) ([5 .1.] -- [.1.]) How do we find ``.0.]`` in ``[4 .0.]`` and replace it with ``5 .1.]`` getting the result ``[4 5 .1.]``? This might seem hard, but because the -underlying structure of the Joy list is a cons-list in Python it's -actually pretty easy. I'll explain below. +underlying structure of the Joy list is a cons-list in Python it’s +actually pretty easy. I’ll explain below. Next we unify and find our two terms are the same already: ``[5 .1.]``: :: - ([4 5 .1.] 2 3 0 -- 3 2 [5 .1.]) ([5 .1.] -- [.1.]) + ([4 5 .1.] 2 3 0 -- 3 2 [5 .1.]) ([5 .1.] -- [.1.]) Giving us: :: - ([4 5 .1.] 2 3 0 -- 3 2) (-- [.1.]) + ([4 5 .1.] 2 3 0 -- 3 2) (-- [.1.]) From here we apply the first rule and get: :: - ([4 5 .1.] 2 3 0 -- 3 2 [.1.]) + ([4 5 .1.] 2 3 0 -- 3 2 [.1.]) Cleaning up the labels: :: - ([4 5 ...] 2 3 1 -- 3 2 [...]) + ([4 5 ...] 2 3 1 -- 3 2 [...]) This is the stack effect of ``pop∘swap∘roll<∘rest∘rest``. @@ -314,35 +314,35 @@ This is the stack effect of ``pop∘swap∘roll<∘rest∘rest``. :: - ([4 5 ...] 2 3 1 -- 3 2 [...]) (1 [...] -- [1 ...]) + ([4 5 ...] 2 3 1 -- 3 2 [...]) (1 [...] -- [1 ...]) Re-label: :: - ([4 5 .1.] 2 3 1 -- 3 2 [.1.]) (6 [.2.] -- [6 .2.]) + ([4 5 .1.] 2 3 1 -- 3 2 [.1.]) (6 [.2.] -- [6 .2.]) Unify: :: - ([4 5 .1.] 2 3 1 -- 3 2 [.1.]) (6 [.2.] -- [6 .2.]) - w/ { .1.] : .2.] } - ([4 5 .2.] 2 3 1 -- 3 2 ) (6 -- [6 .2.]) - w/ {2: 6} - ([4 5 .2.] 6 3 1 -- 3 ) ( -- [6 .2.]) + ([4 5 .1.] 2 3 1 -- 3 2 [.1.]) (6 [.2.] -- [6 .2.]) + w/ { .1.] : .2.] } + ([4 5 .2.] 2 3 1 -- 3 2 ) (6 -- [6 .2.]) + w/ {2: 6} + ([4 5 .2.] 6 3 1 -- 3 ) ( -- [6 .2.]) First rule: :: - ([4 5 .2.] 6 3 1 -- 3 [6 .2.]) + ([4 5 .2.] 6 3 1 -- 3 [6 .2.]) Re-label: :: - ([4 5 ...] 2 3 1 -- 3 [2 ...]) + ([4 5 ...] 2 3 1 -- 3 [2 ...]) Done. @@ -353,42 +353,42 @@ One more time. :: - ([4 5 ...] 2 3 1 -- 3 [2 ...]) (1 [...] -- [1 ...]) + ([4 5 ...] 2 3 1 -- 3 [2 ...]) (1 [...] -- [1 ...]) Re-label: :: - ([4 5 .1.] 2 3 1 -- 3 [2 .1.]) (6 [.2.] -- [6 .2.]) + ([4 5 .1.] 2 3 1 -- 3 [2 .1.]) (6 [.2.] -- [6 .2.]) Unify: :: - ([4 5 .1.] 2 3 1 -- 3 [2 .1.]) (6 [.2.] -- [6 .2.] ) - w/ { .2.] : 2 .1.] } - ([4 5 .1.] 2 3 1 -- 3 ) (6 -- [6 2 .1.]) - w/ {3: 6} - ([4 5 .1.] 2 6 1 -- ) ( -- [6 2 .1.]) + ([4 5 .1.] 2 3 1 -- 3 [2 .1.]) (6 [.2.] -- [6 .2.] ) + w/ { .2.] : 2 .1.] } + ([4 5 .1.] 2 3 1 -- 3 ) (6 -- [6 2 .1.]) + w/ {3: 6} + ([4 5 .1.] 2 6 1 -- ) ( -- [6 2 .1.]) First or second rule: :: - ([4 5 .1.] 2 6 1 -- [6 2 .1.]) + ([4 5 .1.] 2 6 1 -- [6 2 .1.]) Clean up the labels: :: - ([4 5 ...] 2 3 1 -- [3 2 ...]) + ([4 5 ...] 2 3 1 -- [3 2 ...]) And there you have it, the stack effect for ``pop∘swap∘roll<∘rest∘rest∘cons∘cons``. :: - ([4 5 ...] 2 3 1 -- [3 2 ...]) + ([4 5 ...] 2 3 1 -- [3 2 ...]) From this stack effect comment it should be possible to construct the following Python code: @@ -405,7 +405,7 @@ Part II: Implementation Representing Stack Effect Comments in Python ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -I'm going to use pairs of tuples of type descriptors, which will be +I’m going to use pairs of tuples of type descriptors, which will be integers or tuples of type descriptors: .. code:: ipython2 @@ -549,7 +549,7 @@ integers or tuples of type descriptors: At last we put it all together in a function ``C()`` that accepts two stack effect comments and returns their composition (or raises and -exception if they can't be composed due to type conflicts.) +exception if they can’t be composed due to type conflicts.) .. code:: ipython2 @@ -558,7 +558,7 @@ exception if they can't be composed due to type conflicts.) fg = compose(f, g) return delabel(fg) -Let's try it out. +Let’s try it out. .. code:: ipython2 @@ -629,7 +629,7 @@ Let's try it out. Stack Functions ~~~~~~~~~~~~~~~ -Here's that trick to represent functions like ``rest`` and ``cons`` that +Here’s that trick to represent functions like ``rest`` and ``cons`` that manipulate stacks. We use a cons-list of tuples and give the tails their own numbers. Then everything above already works. @@ -656,20 +656,20 @@ Compare this to the stack effect comment we wrote above: :: - (( (3, 4), 1, 2, 0 ), ( 2, 1, 4 )) - ( [4 ...] 2 3 0 -- 3 2 [...]) + (( (3, 4), 1, 2, 0 ), ( 2, 1, 4 )) + ( [4 ...] 2 3 0 -- 3 2 [...]) The translation table, if you will, would be: :: - { - 3: 4, - 4: ...], - 1: 2, - 2: 3, - 0: 0, - } + { + 3: 4, + 4: ...], + 1: 2, + 2: 3, + 0: 0, + } .. code:: ipython2 @@ -690,13 +690,13 @@ Compare with the stack effect comment and you can see it works fine: :: - ([4 5 ...] 2 3 1 -- [3 2 ...]) - 3 4 5 1 2 0 2 1 5 + ([4 5 ...] 2 3 1 -- [3 2 ...]) + 3 4 5 1 2 0 2 1 5 Dealing with ``cons`` and ``uncons`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -However, if we try to compose e.g. ``cons`` and ``uncons`` it won't +However, if we try to compose e.g. ``cons`` and ``uncons`` it won’t work: .. code:: ipython2 @@ -719,7 +719,7 @@ work: ``unify()`` version 2 ^^^^^^^^^^^^^^^^^^^^^ -The problem is that the ``unify()`` function as written doesn't handle +The problem is that the ``unify()`` function as written doesn’t handle the case when both terms are tuples. We just have to add a clause to deal with this recursively: @@ -797,20 +797,20 @@ stack effect comment tuple, just in the reverse order: :: - (_, (d, (c, ((a, (b, S0)), stack)))) + (_, (d, (c, ((a, (b, S0)), stack)))) Remove the punctuation: :: - _ d c (a, (b, S0)) + _ d c (a, (b, S0)) Reverse the order and compare: :: - (a, (b, S0)) c d _ - ((3, (4, 5 )), 1, 2, 0) + (a, (b, S0)) c d _ + ((3, (4, 5 )), 1, 2, 0) Eh? @@ -833,8 +833,8 @@ is similar to the output stack effect comment tuple: :: - ((d, (c, S0)), stack) - ((2, (1, 5 )), ) + ((d, (c, S0)), stack) + ((2, (1, 5 )), ) This should make it pretty easy to write a Python function that accepts the stack effect comment tuples and returns a new Python function @@ -845,7 +845,7 @@ effect.) Python Identifiers ~~~~~~~~~~~~~~~~~~ -We want to substitute Python identifiers for the integers. I'm going to +We want to substitute Python identifiers for the integers. I’m going to repurpose ``joy.parser.Symbol`` class for this: .. code:: ipython2 @@ -869,10 +869,10 @@ repurpose ``joy.parser.Symbol`` class for this: ``doc_from_stack_effect()`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -As a convenience I've implemented a function to convert the Python stack +As a convenience I’ve implemented a function to convert the Python stack effect comment tuples to reasonable text format. There are some details in how this code works that related to stuff later in the notebook, so -you should skip it for now and read it later if you're interested. +you should skip it for now and read it later if you’re interested. .. code:: ipython2 @@ -974,7 +974,7 @@ Next steps: -Let's try it out: +Let’s try it out: .. code:: ipython2 @@ -996,10 +996,10 @@ Let's try it out: With this, we have a partial Joy compiler that works on the subset of -Joy functions that manipulate stacks (both what I call "stack chatter" +Joy functions that manipulate stacks (both what I call “stack chatter” and the ones that manipulate stacks on the stack.) -I'm probably going to modify the definition wrapper code to detect +I’m probably going to modify the definition wrapper code to detect definitions that can be compiled by this partial compiler and do it automatically. It might be a reasonable idea to detect sequences of compilable functions in definitions that have uncompilable functions in @@ -1106,57 +1106,57 @@ Part IV: Types and Subtypes of Arguments ---------------------------------------- So far we have dealt with types of functions, those dealing with simple -stack manipulation. Let's extend our machinery to deal with types of +stack manipulation. Let’s extend our machinery to deal with types of arguments. -"Number" Type +“Number” Type ~~~~~~~~~~~~~ Consider the definition of ``sqr``: :: - sqr == dup mul + sqr == dup mul The ``dup`` function accepts one *anything* and returns two of that: :: - dup (1 -- 1 1) + dup (1 -- 1 1) -And ``mul`` accepts two "numbers" (we're ignoring ints vs. floats vs. -complex, etc., for now) and returns just one: +And ``mul`` accepts two “numbers” (we’re ignoring ints vs. floats +vs. complex, etc., for now) and returns just one: :: - mul (n n -- n) + mul (n n -- n) -So we're composing: +So we’re composing: :: - (1 -- 1 1)∘(n n -- n) + (1 -- 1 1)∘(n n -- n) The rules say we unify 1 with ``n``: :: - (1 -- 1 1)∘(n n -- n) - --------------------------- w/ {1: n} - (1 -- 1 )∘(n -- n) + (1 -- 1 1)∘(n n -- n) + --------------------------- w/ {1: n} + (1 -- 1 )∘(n -- n) -This involves detecting that "Any type" arguments can accept "numbers". +This involves detecting that “Any type” arguments can accept “numbers”. If we were composing these functions the other way round this is still the case: :: - (n n -- n)∘(1 -- 1 1) - --------------------------- w/ {1: n} - (n n -- )∘( -- n n) + (n n -- n)∘(1 -- 1 1) + --------------------------- w/ {1: n} + (n n -- )∘( -- n n) The important thing here is that the mapping is going the same way in -both cases, from the "any" integer to the number +both cases, from the “any” integer to the number Distinguishing Numbers ~~~~~~~~~~~~~~~~~~~~~~ @@ -1167,40 +1167,40 @@ We should also mind that the number that ``mul`` produces is not :: - mul (n2 n1 -- n3) + mul (n2 n1 -- n3) - (1 -- 1 1)∘(n2 n1 -- n3) - -------------------------------- w/ {1: n2} - (n2 -- n2 )∘(n2 -- n3) + (1 -- 1 1)∘(n2 n1 -- n3) + -------------------------------- w/ {1: n2} + (n2 -- n2 )∘(n2 -- n3) - (n2 n1 -- n3)∘(1 -- 1 1 ) - -------------------------------- w/ {1: n3} - (n2 n1 -- )∘( -- n3 n3) + (n2 n1 -- n3)∘(1 -- 1 1 ) + -------------------------------- w/ {1: n3} + (n2 n1 -- )∘( -- n3 n3) Distinguishing Types ~~~~~~~~~~~~~~~~~~~~ -So we need separate domains of "any" numbers and "number" numbers, and +So we need separate domains of “any” numbers and “number” numbers, and we need to be able to ask the order of these domains. Now the notes on the right side of rule three make more sense, eh? :: - (a -- b t[i])∘(c u[j] -- d) t <= u (t is subtype of u) - ------------------------------- - (a -- b )∘(c -- d) t[i] == t[k] == u[j] - ^ + (a -- b t[i])∘(c u[j] -- d) t <= u (t is subtype of u) + ------------------------------- + (a -- b )∘(c -- d) t[i] == t[k] == u[j] + ^ - (a -- b t[i])∘(c u[j] -- d) u <= t (u is subtype of t) - ------------------------------- - (a -- b )∘(c -- d) t[i] == u[k] == u[j] + (a -- b t[i])∘(c u[j] -- d) u <= t (u is subtype of t) + ------------------------------- + (a -- b )∘(c -- d) t[i] == u[k] == u[j] The indices ``i``, ``k``, and ``j`` are the number part of our labels and ``t`` and ``u`` are the domains. -By creative use of Python's "double underscore" methods we can define a +By creative use of Python’s “double underscore” methods we can define a Python class hierarchy of Joy types and use the ``issubclass()`` method to establish domain ordering, as well as other handy behaviour that will make it fairly easy to reuse most of the code above. @@ -1255,7 +1255,7 @@ Mess with it a little: from itertools import permutations -"Any" types can be specialized to numbers and stacks, but not vice +“Any” types can be specialized to numbers and stacks, but not vice versa: .. code:: ipython2 @@ -1276,7 +1276,7 @@ versa: Our crude `Numerical Tower `__ of *numbers* > -*floats* > *integers* works as well (but we're not going to use it yet): +*floats* > *integers* works as well (but we’re not going to use it yet): .. code:: ipython2 @@ -1359,7 +1359,7 @@ Re-labeling still works fine: ^^^^^^^^^^^^^^^^^^^^^^^ The ``delabel()`` function needs an overhaul. It now has to keep track -of how many labels of each domain it has "seen". +of how many labels of each domain it has “seen”. .. code:: ipython2 @@ -1634,7 +1634,7 @@ also get the effect of combinators in some limited cases. ^^^^^^^^^^^^^^^^^^^^^^^^ Because the type labels represent themselves as valid Python identifiers -the ``compile_()`` function doesn't need to generate them anymore: +the ``compile_()`` function doesn’t need to generate them anymore: .. code:: ipython2 @@ -1665,7 +1665,7 @@ the ``compile_()`` function doesn't need to generate them anymore: return ((a4, (a3, s1)), stack) -But it cannot magically create new functions that involve e.g. math and +But it cannot magically create new functions that involve e.g. math and such. Note that this is *not* a ``sqr`` function implementation: .. code:: ipython2 @@ -1681,7 +1681,7 @@ such. Note that this is *not* a ``sqr`` function implementation: return (n2, stack) -(Eventually I should come back around to this becuase it's not tooo +(Eventually I should come back around to this becuase it’s not tooo difficult to exend this code to be able to compile e.g. ``n2 = mul(n1, n1)`` for ``mul`` with the right variable names and insert it in the right place. It requires a little more support from the @@ -1743,18 +1743,18 @@ and puts it on itself: :: - stack (... -- ... [...] ) - stack (... a -- ... a [a ...] ) - stack (... b a -- ... b a [a b ...]) + stack (... -- ... [...] ) + stack (... a -- ... a [a ...] ) + stack (... b a -- ... b a [a b ...]) We would like to represent this in Python somehow. To do this we use a simple, elegant trick. :: - stack S -- ( S, S) - stack (a, S) -- ( (a, S), (a, S)) - stack (a, (b, S)) -- ( (a, (b, S)), (a, (b, S))) + stack S -- ( S, S) + stack (a, S) -- ( (a, S), (a, S)) + stack (a, (b, S)) -- ( (a, (b, S)), (a, (b, S))) Instead of representing the stack effect comments as a single tuple (with N items in it) we use the same cons-list structure to hold the @@ -1763,54 +1763,54 @@ sequence and ``unify()`` the whole comments. ``stack∘uncons`` ~~~~~~~~~~~~~~~~ -Let's try composing ``stack`` and ``uncons``. We want this result: +Let’s try composing ``stack`` and ``uncons``. We want this result: :: - stack∘uncons (... a -- ... a a [...]) + stack∘uncons (... a -- ... a a [...]) The stack effects are: :: - stack = S -- (S, S) + stack = S -- (S, S) - uncons = ((a, Z), S) -- (Z, (a, S)) + uncons = ((a, Z), S) -- (Z, (a, S)) Unifying: :: - S -- (S, S) ∘ ((a, Z), S) -- (Z, (a, S )) - w/ { S: (a, Z) } - (a, Z) -- ∘ -- (Z, (a, (a, Z))) + S -- (S, S) ∘ ((a, Z), S) -- (Z, (a, S )) + w/ { S: (a, Z) } + (a, Z) -- ∘ -- (Z, (a, (a, Z))) So: :: - stack∘uncons == (a, Z) -- (Z, (a, (a, Z))) + stack∘uncons == (a, Z) -- (Z, (a, (a, Z))) It works. ``stack∘uncons∘uncons`` ~~~~~~~~~~~~~~~~~~~~~~~ -Let's try ``stack∘uncons∘uncons``: +Let’s try ``stack∘uncons∘uncons``: :: - (a, S ) -- (S, (a, (a, S ))) ∘ ((b, Z), S` ) -- (Z, (b, S` )) + (a, S ) -- (S, (a, (a, S ))) ∘ ((b, Z), S` ) -- (Z, (b, S` )) - w/ { S: (b, Z) } - - (a, (b, Z)) -- ((b, Z), (a, (a, (b, Z)))) ∘ ((b, Z), S` ) -- (Z, (b, S` )) + w/ { S: (b, Z) } + + (a, (b, Z)) -- ((b, Z), (a, (a, (b, Z)))) ∘ ((b, Z), S` ) -- (Z, (b, S` )) - w/ { S`: (a, (a, (b, Z))) } - - (a, (b, Z)) -- ((b, Z), (a, (a, (b, Z)))) ∘ ((b, Z), (a, (a, (b, Z)))) -- (Z, (b, (a, (a, (b, Z))))) + w/ { S`: (a, (a, (b, Z))) } + + (a, (b, Z)) -- ((b, Z), (a, (a, (b, Z)))) ∘ ((b, Z), (a, (a, (b, Z)))) -- (Z, (b, (a, (a, (b, Z))))) - (a, (b, Z)) -- (Z, (b, (a, (a, (b, Z))))) + (a, (b, Z)) -- (Z, (b, (a, (a, (b, Z))))) It works. @@ -1819,12 +1819,12 @@ It works. This function has to be modified to use the new datastructures and it is no longer recursive, instead recursion happens as part of unification. -Further, the first and second of Pöial's rules are now handled +Further, the first and second of Pöial’s rules are now handled automatically by the unification algorithm. (One easy way to see this is that now an empty stack effect comment is represented by a -``StackJoyType`` instance which is not "falsey" and so neither of the -first two rules' ``if`` clauses will ever be ``True``. Later on I change -the "truthiness" of ``StackJoyType`` to false to let e.g. +``StackJoyType`` instance which is not “falsey” and so neither of the +first two rules’ ``if`` clauses will ever be ``True``. Later on I change +the “truthiness” of ``StackJoyType`` to false to let e.g. ``joy.utils.stack.concat`` work with our stack effect comment cons-list tuples.) @@ -1837,8 +1837,8 @@ tuples.) raise TypeError('Cannot unify %r and %r.' % (f_out, g_in)) return update(s, (f_in, g_out)) -I don't want to rewrite all the defs myself, so I'll write a little -conversion function instead. This is programmer's laziness. +I don’t want to rewrite all the defs myself, so I’ll write a little +conversion function instead. This is programmer’s laziness. .. code:: ipython2 @@ -2115,7 +2115,7 @@ comments are now already in the form needed for the Python code: Part VI: Multiple Stack Effects ------------------------------- -... +… .. code:: ipython2 @@ -2206,63 +2206,63 @@ Part VI: Multiple Stack Effects Representing an Unbounded Sequence of Types ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -We can borrow a trick from `Brzozowski's Derivatives of Regular +We can borrow a trick from `Brzozowski’s Derivatives of Regular Expressions `__ to -invent a new type of type variable, a "sequence type" (I think this is -what they mean in the literature by that term...) or "`Kleene -Star `__" type. I'm going to +invent a new type of type variable, a “sequence type” (I think this is +what they mean in the literature by that term…) or “`Kleene +Star `__” type. I’m going to represent it as a type letter and the asterix, so a sequence of zero or more ``AnyJoyType`` variables would be: :: - A* + A* The ``A*`` works by splitting the universe into two alternate histories: :: - A* -> 0 | A A* + A* -> 0 | A A* The Kleene star variable disappears in one universe, and in the other it turns into an ``AnyJoyType`` variable followed by itself again. We have to return all universes (represented by their substitution dicts, the -"unifiers") that don't lead to type conflicts. +“unifiers”) that don’t lead to type conflicts. Consider unifying two stacks (the lowercase letters are any type variables of the kinds we have defined so far): :: - [a A* b .0.] U [c d .1.] - w/ {c: a} - [ A* b .0.] U [ d .1.] + [a A* b .0.] U [c d .1.] + w/ {c: a} + [ A* b .0.] U [ d .1.] Now we have to split universes to unify ``A*``. In the first universe it disappears: :: - [b .0.] U [d .1.] - w/ {d: b, .1.: .0.} - [] U [] + [b .0.] U [d .1.] + w/ {d: b, .1.: .0.} + [] U [] While in the second it spawns an ``A``, which we will label ``e``: :: - [e A* b .0.] U [d .1.] - w/ {d: e} - [ A* b .0.] U [ .1.] - w/ {.1.: A* b .0.} - [ A* b .0.] U [ A* b .0.] + [e A* b .0.] U [d .1.] + w/ {d: e} + [ A* b .0.] U [ .1.] + w/ {.1.: A* b .0.} + [ A* b .0.] U [ A* b .0.] Giving us two unifiers: :: - {c: a, d: b, .1.: .0.} - {c: a, d: e, .1.: A* b .0.} + {c: a, d: b, .1.: .0.} + {c: a, d: e, .1.: A* b .0.} .. code:: ipython2 @@ -2312,7 +2312,7 @@ Giving us two unifiers: ``unify()`` version 4 ^^^^^^^^^^^^^^^^^^^^^ -Can now return multiple results... +Can now return multiple results… .. code:: ipython2 @@ -2440,11 +2440,11 @@ Can now return multiple results... :: - (a1*, s1) [a1*] (a1, s2) [a1] + (a1*, s1) [a1*] (a1, s2) [a1] - (a1*, (a1, s2)) [a1* a1] (a1, s2) [a1] + (a1*, (a1, s2)) [a1* a1] (a1, s2) [a1] - (a1*, s1) [a1*] (a2, (a1*, s1)) [a2 a1*] + (a1*, s1) [a1*] (a2, (a1*, s1)) [a2 a1*] .. code:: ipython2 @@ -2628,51 +2628,51 @@ Part VII: Typing Combinators In order to compute the stack effect of combinators you kinda have to have the quoted programs they expect available. In the most general -case, the ``i`` combinator, you can't say anything about its stack +case, the ``i`` combinator, you can’t say anything about its stack effect other than it expects one quote: :: - i (... [.1.] -- ... .1.) + i (... [.1.] -- ... .1.) Or :: - i (... [A* .1.] -- ... A*) + i (... [A* .1.] -- ... A*) Consider the type of: :: - [cons] dip + [cons] dip Obviously it would be: :: - (a1 [..1] a2 -- [a1 ..1] a2) + (a1 [..1] a2 -- [a1 ..1] a2) ``dip`` itself could have: :: - (a1 [..1] -- ... then what? + (a1 [..1] -- ... then what? -Without any information about the contents of the quote we can't say +Without any information about the contents of the quote we can’t say much about the result. Hybrid Inferencer/Interpreter ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -I think there's a way forward. If we convert our list (of terms we are +I think there’s a way forward. If we convert our list (of terms we are composing) into a stack structure we can use it as a *Joy expression*, -then we can treat the *output half* of a function's stack effect comment +then we can treat the *output half* of a function’s stack effect comment as a Joy interpreter stack, and just execute combinators directly. We can hybridize the compostition function with an interpreter to evaluate combinators, compose non-combinator functions, and put type variables on the stack. For combinators like ``branch`` that can have more than one -stack effect we have to "split universes" again and return both. +stack effect we have to “split universes” again and return both. Joy Types for Functions ^^^^^^^^^^^^^^^^^^^^^^^ @@ -2755,7 +2755,7 @@ effects. You can also provide an optional stack effect, input-side only, that will then be used as an identity function (that accepts and returns -stacks that match the "guard" stack effect) which will be used to guard +stacks that match the “guard” stack effect) which will be used to guard against type mismatches going into the evaluation of the combinator. ``infer()`` @@ -2828,7 +2828,7 @@ Work in Progress And that brings us to current Work-In-Progress. The mixed-mode inferencer/interpreter ``infer()`` function seems to work well. There are details I should document, and the rest of the code in the ``types`` -module (FIXME link to its docs here!) should be explained... There is +module (FIXME link to its docs here!) should be explained… There is cruft to convert the definitions in ``DEFS`` to the new ``SymbolJoyType`` objects, and some combinators. Here is an example of output from the current code : @@ -2868,7 +2868,7 @@ output from the current code : The numbers at the start of the lines are the current depth of the -Python call stack. They're followed by the current computed stack effect +Python call stack. They’re followed by the current computed stack effect (initialized to ``ID``) then the pending expression (the inference of the stack effect of which is the whole object of the current example.) @@ -2878,47 +2878,47 @@ implementation in action. :: - 7 (--) ∘ [pred] [mul] [div] [nullary bool] dipd branch - 8 (-- [pred ...2]) ∘ [mul] [div] [nullary bool] dipd branch - 9 (-- [pred ...2] [mul ...3]) ∘ [div] [nullary bool] dipd branch - 10 (-- [pred ...2] [mul ...3] [div ...4]) ∘ [nullary bool] dipd branch - 11 (-- [pred ...2] [mul ...3] [div ...4] [nullary bool ...5]) ∘ dipd branch - 15 (-- [pred ...5]) ∘ nullary bool [mul] [div] branch - 19 (-- [pred ...2]) ∘ [stack] dinfrirst bool [mul] [div] branch - 20 (-- [pred ...2] [stack ]) ∘ dinfrirst bool [mul] [div] branch - 22 (-- [pred ...2] [stack ]) ∘ dip infra first bool [mul] [div] branch - 26 (--) ∘ stack [pred] infra first bool [mul] [div] branch - 29 (... -- ... [...]) ∘ [pred] infra first bool [mul] [div] branch - 30 (... -- ... [...] [pred ...1]) ∘ infra first bool [mul] [div] branch - 34 (--) ∘ pred s1 swaack first bool [mul] [div] branch - 37 (n1 -- n2) ∘ [n1] swaack first bool [mul] [div] branch - 38 (... n1 -- ... n2 [n1 ...]) ∘ swaack first bool [mul] [div] branch - 41 (... n1 -- ... n1 [n2 ...]) ∘ first bool [mul] [div] branch - 44 (n1 -- n1 n2) ∘ bool [mul] [div] branch - 47 (n1 -- n1 b1) ∘ [mul] [div] branch - 48 (n1 -- n1 b1 [mul ...1]) ∘ [div] branch - 49 (n1 -- n1 b1 [mul ...1] [div ...2]) ∘ branch - 53 (n1 -- n1) ∘ div - 56 (f2 f1 -- f3) ∘ - 56 (i1 f1 -- f2) ∘ - 56 (f1 i1 -- f2) ∘ - 56 (i2 i1 -- f1) ∘ - 53 (n1 -- n1) ∘ mul - 56 (f2 f1 -- f3) ∘ - 56 (i1 f1 -- f2) ∘ - 56 (f1 i1 -- f2) ∘ - 56 (i2 i1 -- i3) ∘ - ---------------------------------------- - (f2 f1 -- f3) - (i1 f1 -- f2) - (f1 i1 -- f2) - (i2 i1 -- f1) - (i2 i1 -- i3) + 7 (--) ∘ [pred] [mul] [div] [nullary bool] dipd branch + 8 (-- [pred ...2]) ∘ [mul] [div] [nullary bool] dipd branch + 9 (-- [pred ...2] [mul ...3]) ∘ [div] [nullary bool] dipd branch + 10 (-- [pred ...2] [mul ...3] [div ...4]) ∘ [nullary bool] dipd branch + 11 (-- [pred ...2] [mul ...3] [div ...4] [nullary bool ...5]) ∘ dipd branch + 15 (-- [pred ...5]) ∘ nullary bool [mul] [div] branch + 19 (-- [pred ...2]) ∘ [stack] dinfrirst bool [mul] [div] branch + 20 (-- [pred ...2] [stack ]) ∘ dinfrirst bool [mul] [div] branch + 22 (-- [pred ...2] [stack ]) ∘ dip infra first bool [mul] [div] branch + 26 (--) ∘ stack [pred] infra first bool [mul] [div] branch + 29 (... -- ... [...]) ∘ [pred] infra first bool [mul] [div] branch + 30 (... -- ... [...] [pred ...1]) ∘ infra first bool [mul] [div] branch + 34 (--) ∘ pred s1 swaack first bool [mul] [div] branch + 37 (n1 -- n2) ∘ [n1] swaack first bool [mul] [div] branch + 38 (... n1 -- ... n2 [n1 ...]) ∘ swaack first bool [mul] [div] branch + 41 (... n1 -- ... n1 [n2 ...]) ∘ first bool [mul] [div] branch + 44 (n1 -- n1 n2) ∘ bool [mul] [div] branch + 47 (n1 -- n1 b1) ∘ [mul] [div] branch + 48 (n1 -- n1 b1 [mul ...1]) ∘ [div] branch + 49 (n1 -- n1 b1 [mul ...1] [div ...2]) ∘ branch + 53 (n1 -- n1) ∘ div + 56 (f2 f1 -- f3) ∘ + 56 (i1 f1 -- f2) ∘ + 56 (f1 i1 -- f2) ∘ + 56 (i2 i1 -- f1) ∘ + 53 (n1 -- n1) ∘ mul + 56 (f2 f1 -- f3) ∘ + 56 (i1 f1 -- f2) ∘ + 56 (f1 i1 -- f2) ∘ + 56 (i2 i1 -- i3) ∘ + ---------------------------------------- + (f2 f1 -- f3) + (i1 f1 -- f2) + (f1 i1 -- f2) + (i2 i1 -- f1) + (i2 i1 -- i3) Conclusion ---------- -We built a simple type inferencer, and a kind of crude "compiler" for a +We built a simple type inferencer, and a kind of crude “compiler” for a subset of Joy functions. Then we built a more powerful inferencer that actually does some evaluation and explores branching code paths @@ -2927,18 +2927,22 @@ Work remains to be done: - the rest of the library has to be covered - figure out how to deal with ``loop`` and ``genrec``, etc.. - extend the types to check values (see the appendix) -- other kinds of "higher order" type variables, OR, AND, etc.. +- other kinds of “higher order” type variables, OR, AND, etc.. - maybe rewrite in Prolog for great good? - definitions -- don't permit composition of functions that don't compose -- auto-compile compilable functions + + - don’t permit composition of functions that don’t compose + - auto-compile compilable functions + - Compiling more than just the Yin functions. - getting better visibility (than Python debugger.) - DOOOOCS!!!! Lots of docs! -- docstrings all around -- improve this notebook (it kinda falls apart at the end narratively. I - went off and just started writing code to see if it would work. It - does, but now I have to come back and describe here what I did. + + - docstrings all around + - improve this notebook (it kinda falls apart at the end + narratively. I went off and just started writing code to see if it + would work. It does, but now I have to come back and describe here + what I did. Appendix: Joy in the Logical Paradigm ------------------------------------- @@ -2946,9 +2950,9 @@ Appendix: Joy in the Logical Paradigm For *type checking* to work the type label classes have to be modified to let ``T >= t`` succeed, where e.g. ``T`` is ``IntJoyType`` and ``t`` is ``int``. If you do that you can take advantage of the *logical -relational* nature of the stack effect comments to "compute in reverse" -as it were. There's a working demo of this at the end of the ``types`` -module. But if you're interested in all that you should just use Prolog! +relational* nature of the stack effect comments to “compute in reverse” +as it were. There’s a working demo of this at the end of the ``types`` +module. But if you’re interested in all that you should just use Prolog! Anyhow, type *checking* is a few easy steps away. diff --git a/docs/Zipper.rst b/docs/Zipper.rst index 02ace8e..dc4f996 100644 --- a/docs/Zipper.rst +++ b/docs/Zipper.rst @@ -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 `__ or -the original paper: `"FUNCTIONAL PEARL The Zipper" by Gérard +the original paper: `“FUNCTIONAL PEARL The Zipper” by Gérard Huet `__ 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 aren’t 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 `__ 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 +Let’s 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 you’ll 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 isn’t 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" +It’s 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! diff --git a/docs/fun_with_scan.rst b/docs/fun_with_scan.rst index 30024f5..bafb792 100644 --- a/docs/fun_with_scan.rst +++ b/docs/fun_with_scan.rst @@ -2,7 +2,7 @@ from notebook_preamble import D, DefinitionWrapper, J, V, define -On "Two Exercises Found in a Book on Algorithmics" +On “Two Exercises Found in a Book on Algorithmics” ================================================== Bird & Meertens @@ -13,25 +13,27 @@ here `__ Define ``scan`` in terms of a reduction. ---------------------------------------- - Problem I. The reduction operator ``/`` of APL takes some binary - operator ``⨁`` on its left and a vector ``x`` of values on its - right. The meaning of ``⨁/x`` for ``x = [a b ... z]`` is the value - ``a⨁b⨁...⨁z``. For this to be well-defined in the absence of - brackets, the operation ``⨁`` has to be associative. Now there is - another operator ``\`` of APL called ``scan``. Its effect is closely - related to reduction in that we have: + Problem I. The reduction operator ``/`` of APL takes some binary + operator ``⨁`` on its left and a vector ``x`` of values on its right. + The meaning of ``⨁/x`` for ``x = [a b ... z]`` is the value + ``a⨁b⨁...⨁z``. For this to be well-defined in the absence of + brackets, the operation ``⨁`` has to be associative. Now there is + another operator ``\`` of APL called ``scan``. Its effect is closely + related to reduction in that we have: :: - ⨁\x = [a a⨁b a⨁b⨁c ... a⨁b⨁...⨁z] + ⨁\x = [a a⨁b a⨁b⨁c ... a⨁b⨁...⨁z] - The problem is to find some definition of ``scan`` as a reduction. - In other words, we have to find some function ``f`` and an operator - ``⨂`` so that +.. + + The problem is to find some definition of ``scan`` as a reduction. In + other words, we have to find some function ``f`` and an operator + ``⨂`` so that :: - ⨁\x = f(a)⨂f(b)⨂...⨂f(z) + ⨁\x = f(a)⨂f(b)⨂...⨂f(z) Designing the Recursive Function -------------------------------- @@ -50,48 +52,48 @@ 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″ Initial Definition ~~~~~~~~~~~~~~~~~~ -We're building a list of values so this is an "anamorphism". (An +We’re building a list of values so this is an “anamorphism”. (An anamorphism uses ``[]`` for ``c`` and ``swons`` for ``F``.) :: - scan == [P] [pop []] [[G] dupdip] [dip swons] genrec + scan == [P] [pop []] [[G] dupdip] [dip swons] genrec Convert to ``ifte``: :: - scan == [P] [pop []] [[G] dupdip [scan] dip swons] ifte + scan == [P] [pop []] [[G] dupdip [scan] dip swons] ifte -On the recursive branch ``[G] dupdip`` doesn't cut it: +On the recursive branch ``[G] dupdip`` doesn’t cut it: :: - [1 2 3] [G] dupdip [scan] dip swons - [1 2 3] G [1 2 3] [scan] dip swons + [1 2 3] [G] dupdip [scan] dip swons + [1 2 3] G [1 2 3] [scan] dip swons Use ``first`` ~~~~~~~~~~~~~ @@ -101,11 +103,11 @@ use ``first``. :: - scan == [P] [pop []] [[G] dupdip first] [dip swons] genrec + scan == [P] [pop []] [[G] dupdip first] [dip swons] genrec - [1 2 3] [G] dupdip first [scan] dip swons - [1 2 3] G [1 2 3] first [scan] dip swons - [1 2 3] G 1 [scan] dip swons + [1 2 3] [G] dupdip first [scan] dip swons + [1 2 3] G [1 2 3] first [scan] dip swons + [1 2 3] G 1 [scan] dip swons ``G`` applies ``⨁`` ~~~~~~~~~~~~~~~~~~~ @@ -115,10 +117,10 @@ in the list. :: - [1 2 3] G - [1 2 3] [⨁] infra - [1 2 3] [+] infra - [3 3] + [1 2 3] G + [1 2 3] [⨁] infra + [1 2 3] [+] infra + [3 3] Predicate ``P`` ~~~~~~~~~~~~~~~ @@ -128,14 +130,14 @@ less that two items in them: :: - P == size 1 <= + P == size 1 <= -Let's see what we've got so far: +Let’s see what we’ve got so far: :: - scan == [P ] [pop []] [[G] dupdip first] [dip swons] genrec - scan == [size 1 <=] [pop []] [[[F] infra] dupdip first] [dip swons] genrec + scan == [P ] [pop []] [[G] dupdip first] [dip swons] genrec + scan == [size 1 <=] [pop []] [[[F] infra] dupdip first] [dip swons] genrec Handling the Last Term ~~~~~~~~~~~~~~~~~~~~~~ @@ -152,7 +154,7 @@ This works to a point, but it throws away the last term: [1 3] -Hmm... Let's take out the ``pop`` for a sec... +Hmm… Let’s take out the ``pop`` for a sec… .. code:: ipython2 @@ -165,9 +167,9 @@ Hmm... Let's take out the ``pop`` for a sec... That leaves the last item in our list, then it puts an empty list on the -stack and ``swons``'s the new terms onto that. If we leave out that -empty list, they will be ``swons``'d onto that list that already has the -last item. +stack and ``swons``\ ’s the new terms onto that. If we leave out that +empty list, they will be ``swons``\ ’d onto that list that already has +the last item. .. code:: ipython2 @@ -186,22 +188,22 @@ So we have: :: - [⨁] scan == [size 1 <=] [] [[[⨁] infra] dupdip first] [dip swons] genrec + [⨁] scan == [size 1 <=] [] [[[⨁] infra] dupdip first] [dip swons] genrec Trivially: :: - == [size 1 <=] [] [[[⨁] infra] dupdip first] [dip swons] genrec - == [[[⨁] infra] dupdip first] [size 1 <=] [] roll< [dip swons] genrec - == [[⨁] infra] [dupdip first] cons [size 1 <=] [] roll< [dip swons] genrec - == [⨁] [infra] cons [dupdip first] cons [size 1 <=] [] roll< [dip swons] genrec + == [size 1 <=] [] [[[⨁] infra] dupdip first] [dip swons] genrec + == [[[⨁] infra] dupdip first] [size 1 <=] [] roll< [dip swons] genrec + == [[⨁] infra] [dupdip first] cons [size 1 <=] [] roll< [dip swons] genrec + == [⨁] [infra] cons [dupdip first] cons [size 1 <=] [] roll< [dip swons] genrec And so: :: - scan == [infra] cons [dupdip first] cons [size 1 <=] [] roll< [dip swons] genrec + scan == [infra] cons [dupdip first] cons [size 1 <=] [] roll< [dip swons] genrec .. code:: ipython2 @@ -240,22 +242,22 @@ And so: Problem 2. ---------- - Define a line to be a sequence of characters not containing the - newline character. It is easy to define a function ``Unlines`` that - converts a non-empty sequence of lines into a sequence of characters - by inserting newline characters between every two lines. + Define a line to be a sequence of characters not containing the + newline character. It is easy to define a function ``Unlines`` that + converts a non-empty sequence of lines into a sequence of characters + by inserting newline characters between every two lines. - Since ``Unlines`` is injective, the function ``Lines``, which - converts a sequence of characters into a sequence of lines by - splitting on newline characters, can be specified as the inverse of - ``Unlines``. + Since ``Unlines`` is injective, the function ``Lines``, which + converts a sequence of characters into a sequence of lines by + splitting on newline characters, can be specified as the inverse of + ``Unlines``. - The problem, just as in Problem 1. is to find a definition by - reduction of the function ``Lines``. + The problem, just as in Problem 1. is to find a definition by + reduction of the function ``Lines``. :: - Unlines = uncons ['\n' swap + +] step + Unlines = uncons ['\n' swap + +] step .. code:: ipython2 @@ -267,41 +269,41 @@ Problem 2. 'hello\nworld' -Again ignoring the actual task let's just derive ``Lines``: +Again ignoring the actual task let’s just derive ``Lines``: :: - "abc\nefg\nhij" Lines - --------------------------- - ["abc" "efg" "hij"] + "abc\nefg\nhij" Lines + --------------------------- + ["abc" "efg" "hij"] Instead of ``P == [size 1 <=]`` we want ``["\n" in]``, and for the base-case of a string with no newlines in it we want to use ``unit``: :: - Lines == ["\n" in] [unit] [R0] [dip swons] genrec - Lines == ["\n" in] [unit] [R0 [Lines] dip swons] ifte + Lines == ["\n" in] [unit] [R0] [dip swons] genrec + Lines == ["\n" in] [unit] [R0 [Lines] dip swons] ifte Derive ``R0``: :: - "a \n b" R0 [Lines] dip swons - "a \n b" split-at-newline swap [Lines] dip swons - "a " " b" swap [Lines] dip swons - " b" "a " [Lines] dip swons - " b" Lines "a " swons - [" b"] "a " swons - ["a " " b"] + "a \n b" R0 [Lines] dip swons + "a \n b" split-at-newline swap [Lines] dip swons + "a " " b" swap [Lines] dip swons + " b" "a " [Lines] dip swons + " b" Lines "a " swons + [" b"] "a " swons + ["a " " b"] So: :: - R0 == split-at-newline swap + R0 == split-at-newline swap - Lines == ["\n" in] [unit] [split-at-newline swap] [dip swons] genrec + Lines == ["\n" in] [unit] [split-at-newline swap] [dip swons] genrec Missing the Point? ------------------ @@ -311,27 +313,27 @@ properties are discussed. Am I missing the point? :: - 0 [a b c d] [F] step == 0 [a b] [F] step 0 [c d] [F] step concat + 0 [a b c d] [F] step == 0 [a b] [F] step 0 [c d] [F] step concat -For associative function ``F`` and a "unit" element for that function, +For associative function ``F`` and a “unit” element for that function, here represented by ``0``. -For functions that don't have a "unit" we can fake it (the example is +For functions that don’t have a “unit” we can fake it (the example is given of infinity for the ``min(a, b)`` function.) We can also use: :: - safe_step == [size 1 <=] [] [uncons [F] step] ifte + safe_step == [size 1 <=] [] [uncons [F] step] ifte Or: :: - safe_step == [pop size 1 <=] [pop] [[uncons] dip step] ifte + safe_step == [pop size 1 <=] [pop] [[uncons] dip step] ifte - [a b c] [F] safe_step - --------------------------- - a [b c] [F] step + [a b c] [F] safe_step + --------------------------- + a [b c] [F] step To limit ``F`` to working on pairs of terms from its domain. diff --git a/docs/sphinx_docs/_build/html/_modules/index.html b/docs/sphinx_docs/_build/html/_modules/index.html index 25ccd09..5d40d44 100644 --- a/docs/sphinx_docs/_build/html/_modules/index.html +++ b/docs/sphinx_docs/_build/html/_modules/index.html @@ -1,18 +1,19 @@ - + - + - + + Overview: module code — Thun 0.3.0 documentation - - - - - - + + + + + @@ -27,12 +28,10 @@