From b05f13fc90aaa04c516e54d5031cacacedf28a64 Mon Sep 17 00:00:00 2001 From: Simon Forman Date: Sat, 15 Jan 2022 15:22:17 -0800 Subject: [PATCH] WIP docs update --- docs/Recursion_Combinators.ipynb | 206 +++++++++++++++++++++++++++++++ docs/Trees.ipynb | 10 +- docs/Treestep.ipynb | 11 +- 3 files changed, 216 insertions(+), 11 deletions(-) diff --git a/docs/Recursion_Combinators.ipynb b/docs/Recursion_Combinators.ipynb index 214f8a0..a55f79e 100644 --- a/docs/Recursion_Combinators.ipynb +++ b/docs/Recursion_Combinators.ipynb @@ -609,6 +609,212 @@ "However, this creates (and destroys) an intermediate list, which is a waste." ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Variations\n", + "\n", + "Let's review the operation of the hylomorphism:\n", + "\n", + " [P] c [G] [Q] H₁ == [P] [pop c] [G] [dip Q] genrec\n", + "\n", + " ... a G [H₁] dip Q\n", + " ... a′ b [H₁] dip Q\n", + " ... a′ H₁ b Q\n", + " \n", + " ... a′ G [H₁] dip Q b Q\n", + " ... a″ b′ [H₁] dip Q b Q\n", + " ... a″ H₁ b′ Q b Q\n", + " \n", + " ... a″ G [H₁] dip Q b′ Q b Q\n", + " ... a‴ b″ [H₁] dip Q b′ Q b Q\n", + " ... a‴ H₁ b″ Q b′ Q b Q\n", + " \n", + " ... a‴ pop c b″ Q b′ Q b Q\n", + " ... c b″ Q b′ Q b Q\n", + " ... c′ b′ Q b Q\n", + " ... c″ b Q\n", + " ... c‴\n", + "\n", + "Notice that `b` values and the `Q` combiner function get pushed into the pending expression (\"continuation\")." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### A tail-recursive version\n", + "\n", + "Now let's try a version that doesn't do that. When you can start with the identity value `c` on the stack and the combiner `Q` can operate as you go using the intermediate results immediately rather than queuing them up, we can do this:\n", + "\n", + " [P] c [G] [Q] H₂ == c [pop P] [popd] [[G] dip Q] tailrec\n", + "\n", + "An important difference is that the combiner function `Q` must accept its arguments in the reverse order and take into account the presence of the `a` value:\n", + "\n", + " ... a c [G] dip Q H₂\n", + " ... a G c Q H₂\n", + " ... a′ b c Q H₂\n", + " ... a′ c′ H₂\n", + " \n", + " ... a′ c′ [G] dip Q H₂\n", + " ... a′ G c′ Q H₂\n", + " ... a″ b c′ Q H₂\n", + " ... a″ c″ H₂\n", + " \n", + " ... a″ c″ [G] dip Q H₂\n", + " ... a″ G c″ Q H₂\n", + " ... a‴ b c″ Q H₂\n", + " ... a‴ c‴ H₂\n", + " \n", + " ... a‴ c‴ popd\n", + " ... c‴" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that the `c` values are processed by the `Q` combiner function in reverse order as compared to the original version.\n", + "\n", + "#### `range` with `H₂`\n", + "\n", + "For example, if we reimplement the `range` function in the tail-recursive style it generates the list with $0$ at the head rather than $n - 1$:\n", + "\n", + " range_reverse == [] [pop 0 <=] [popd] [[-- dup] dip cons] tailrec\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [] + } + ], + "source": [ + "clear " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [] + } + ], + "source": [ + "[range_reverse [] [pop 0 <=] [popd] [[-- dup] dip cons] tailrec] inscribe" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[4 3 2 1 0] [0 1 2 3 4]" + ] + } + ], + "source": [ + "5 [range] [range_reverse] cleave" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Instead of making `Q` take account of the `c` value we could do this:\n", + "\n", + " ... a′ b c [Q] ccons dip swap H₂\n", + " ... a′ [b c Q] dip swap H₂\n", + " ... b c Q a′ swap H₂\n", + " ... c′ a′ swap H₂\n", + " ... a′ c′ H₂\n", + "\n", + "This shows that any function `Q` can be converted to `[Q] ccons dip swap` to deal with that `a` value on the stack." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### the old version\n", + "\n", + "Now let's try a version that doesn't do that. When you can start with the identity value `c` on the stack and the combiner `Q` can operate as you go using the intermediate results immediately rather than queuing them up, we can do this:\n", + "\n", + " [P] c [G] [Q] H₂ == c swap [P] [pop] [G [Q] dip] tailrec\n", + "\n", + "An important difference is that the generator function must return its results in the reverse order, and both `Q` and `G` have to take into account the presence of the `c` value:\n", + "\n", + " ... c a G [Q] dip H₂\n", + " ... c b a′ [Q] dip H₂\n", + " ... c b Q a′ H₂\n", + " ... c′ a′ H₂\n", + " \n", + " ... c′ a′ G [Q] dip H₂\n", + " ... c′ b′ a″ [Q] dip H₂\n", + " ... c′ b′ Q a″ H₂\n", + " ... c″ a″ H₂\n", + " \n", + " ... c″ a″ G [Q] dip H₂\n", + " ... c″ b″ a‴ [Q] dip H₂\n", + " ... c″ b″ Q a‴ H₂\n", + " ... c‴ a‴ H₂\n", + " \n", + " ... c‴ a‴ pop\n", + " ... c‴\n" + ] + }, { "cell_type": "markdown", "metadata": {}, diff --git a/docs/Trees.ipynb b/docs/Trees.ipynb index 8baf723..7acf67d 100644 --- a/docs/Trees.ipynb +++ b/docs/Trees.ipynb @@ -2654,21 +2654,21 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 2", + "display_name": "Python 3 (ipykernel)", "language": "python", - "name": "python2" + "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", - "version": 2 + "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.12" + "pygments_lexer": "ipython3", + "version": "3.7.10" } }, "nbformat": 4, diff --git a/docs/Treestep.ipynb b/docs/Treestep.ipynb index 0929064..d7fef34 100644 --- a/docs/Treestep.ipynb +++ b/docs/Treestep.ipynb @@ -470,7 +470,6 @@ "source": [ "### Traversal\n", " [key value] first [left right] [K] map i\n", - " key [value] [left right] [K] map i\n", " key [left right] [K] map i\n", " key [lkey rkey ] i\n", " key lkey rkey" @@ -873,21 +872,21 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 2", + "display_name": "Python 3 (ipykernel)", "language": "python", - "name": "python2" + "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", - "version": 2 + "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.12" + "pygments_lexer": "ipython3", + "version": "3.7.10" } }, "nbformat": 4,