More update to 4.3.0

This commit is contained in:
Simon Forman 2021-11-19 12:49:05 -08:00
parent eeda5044ad
commit 9bf58ff953
50 changed files with 24206 additions and 0 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,4 @@
# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: d581c6009ec2b560e43b1edcc2912d38
tags: 645f666f9bcd5a90fca523b33c5a78b7

View File

@ -0,0 +1,185 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.38.0 (20140413.2041)
-->
<!-- Title: finite_state_machine Pages: 1 -->
<svg width="534pt" height="270pt"
viewBox="0.00 0.00 534.00 270.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 266)">
<title>finite_state_machine</title>
<polygon fill="white" stroke="none" points="-4,4 -4,-266 530,-266 530,4 -4,4"/>
<!-- i -->
<g id="node1" class="node"><title>i</title>
<ellipse fill="none" stroke="black" cx="338" cy="-146" rx="18" ry="18"/>
<ellipse fill="none" stroke="black" cx="338" cy="-146" rx="22" ry="22"/>
<text text-anchor="middle" x="338" y="-142.3" font-family="Times,serif" font-size="14.00">i</text>
</g>
<!-- i&#45;&gt;i -->
<g id="edge17" class="edge"><title>i&#45;&gt;i</title>
<path fill="none" stroke="black" d="M330.317,-166.991C329.369,-177.087 331.93,-186 338,-186 341.889,-186 344.337,-182.342 345.346,-177.059"/>
<polygon fill="black" stroke="black" points="348.846,-177.102 345.683,-166.991 341.85,-176.868 348.846,-177.102"/>
<text text-anchor="middle" x="338" y="-189.8" font-family="Times,serif" font-size="14.00">0</text>
</g>
<!-- j -->
<g id="node10" class="node"><title>j</title>
<ellipse fill="none" stroke="black" cx="421" cy="-136" rx="18" ry="18"/>
<text text-anchor="middle" x="421" y="-132.3" font-family="Times,serif" font-size="14.00">j</text>
</g>
<!-- i&#45;&gt;j -->
<g id="edge18" class="edge"><title>i&#45;&gt;j</title>
<path fill="none" stroke="black" d="M357.466,-135.495C363.775,-132.451 371.008,-129.536 378,-128 383.213,-126.855 388.811,-126.984 394.167,-127.763"/>
<polygon fill="black" stroke="black" points="393.487,-131.197 404.002,-129.894 394.97,-124.355 393.487,-131.197"/>
<text text-anchor="middle" x="381.5" y="-131.8" font-family="Times,serif" font-size="14.00">1</text>
</g>
<!-- h -->
<g id="node2" class="node"><title>h</title>
<ellipse fill="none" stroke="black" cx="504" cy="-85" rx="18" ry="18"/>
<ellipse fill="none" stroke="black" cx="504" cy="-85" rx="22" ry="22"/>
<text text-anchor="middle" x="504" y="-81.3" font-family="Times,serif" font-size="14.00">h</text>
</g>
<!-- h&#45;&gt;i -->
<g id="edge15" class="edge"><title>h&#45;&gt;i</title>
<path fill="none" stroke="black" d="M481.868,-83.4025C461.033,-82.62 428.676,-83.5645 403,-94 387.267,-100.394 372.373,-112.028 360.918,-122.673"/>
<polygon fill="black" stroke="black" points="358.306,-120.33 353.569,-129.807 363.182,-125.353 358.306,-120.33"/>
<text text-anchor="middle" x="421" y="-97.8" font-family="Times,serif" font-size="14.00">0</text>
</g>
<!-- h&#45;&gt;h -->
<g id="edge16" class="edge"><title>h&#45;&gt;h</title>
<path fill="none" stroke="black" d="M496.317,-105.991C495.369,-116.087 497.93,-125 504,-125 507.889,-125 510.337,-121.342 511.346,-116.059"/>
<polygon fill="black" stroke="black" points="514.846,-116.102 511.683,-105.991 507.85,-115.868 514.846,-116.102"/>
<text text-anchor="middle" x="504" y="-128.8" font-family="Times,serif" font-size="14.00">1</text>
</g>
<!-- a -->
<g id="node3" class="node"><title>a</title>
<ellipse fill="none" stroke="black" cx="18" cy="-128" rx="18" ry="18"/>
<text text-anchor="middle" x="18" y="-124.3" font-family="Times,serif" font-size="14.00">a</text>
</g>
<!-- b -->
<g id="node4" class="node"><title>b</title>
<ellipse fill="none" stroke="black" cx="255" cy="-113" rx="18" ry="18"/>
<text text-anchor="middle" x="255" y="-109.3" font-family="Times,serif" font-size="14.00">b</text>
</g>
<!-- a&#45;&gt;b -->
<g id="edge1" class="edge"><title>a&#45;&gt;b</title>
<path fill="none" stroke="black" d="M36.2801,-126.897C76.7816,-124.312 178.091,-117.845 226.89,-114.73"/>
<polygon fill="black" stroke="black" points="227.255,-118.214 237.011,-114.084 226.809,-111.229 227.255,-118.214"/>
<text text-anchor="middle" x="136.5" y="-123.8" font-family="Times,serif" font-size="14.00">0</text>
</g>
<!-- c -->
<g id="node5" class="node"><title>c</title>
<ellipse fill="none" stroke="black" cx="97" cy="-155" rx="18" ry="18"/>
<text text-anchor="middle" x="97" y="-151.3" font-family="Times,serif" font-size="14.00">c</text>
</g>
<!-- a&#45;&gt;c -->
<g id="edge2" class="edge"><title>a&#45;&gt;c</title>
<path fill="none" stroke="black" d="M35.3297,-133.726C45.4364,-137.27 58.635,-141.898 70.1398,-145.932"/>
<polygon fill="black" stroke="black" points="69.099,-149.276 79.6938,-149.282 71.4153,-142.67 69.099,-149.276"/>
<text text-anchor="middle" x="57.5" y="-145.8" font-family="Times,serif" font-size="14.00">1</text>
</g>
<!-- b&#45;&gt;b -->
<g id="edge3" class="edge"><title>b&#45;&gt;b</title>
<path fill="none" stroke="black" d="M248.266,-130.037C246.892,-139.858 249.137,-149 255,-149 258.665,-149 260.916,-145.429 261.753,-140.353"/>
<polygon fill="black" stroke="black" points="265.252,-140.031 261.734,-130.037 258.252,-140.044 265.252,-140.031"/>
<text text-anchor="middle" x="255" y="-152.8" font-family="Times,serif" font-size="14.00">0</text>
</g>
<!-- d -->
<g id="node6" class="node"><title>d</title>
<ellipse fill="none" stroke="black" cx="338" cy="-79" rx="18" ry="18"/>
<text text-anchor="middle" x="338" y="-75.3" font-family="Times,serif" font-size="14.00">d</text>
</g>
<!-- b&#45;&gt;d -->
<g id="edge4" class="edge"><title>b&#45;&gt;d</title>
<path fill="none" stroke="black" d="M272.003,-106.283C283.319,-101.533 298.722,-95.0674 311.693,-89.6227"/>
<polygon fill="black" stroke="black" points="313.164,-92.801 321.03,-85.7034 310.455,-86.3466 313.164,-92.801"/>
<text text-anchor="middle" x="294.5" y="-101.8" font-family="Times,serif" font-size="14.00">1</text>
</g>
<!-- c&#45;&gt;b -->
<g id="edge5" class="edge"><title>c&#45;&gt;b</title>
<path fill="none" stroke="black" d="M114.862,-150.653C138.269,-144.593 181.917,-133.2 219,-123 221.799,-122.23 224.721,-121.414 227.631,-120.594"/>
<polygon fill="black" stroke="black" points="228.623,-123.951 237.284,-117.849 226.708,-117.218 228.623,-123.951"/>
<text text-anchor="middle" x="176" y="-142.8" font-family="Times,serif" font-size="14.00">0</text>
</g>
<!-- e -->
<g id="node7" class="node"><title>e</title>
<ellipse fill="none" stroke="black" cx="176" cy="-206" rx="18" ry="18"/>
<text text-anchor="middle" x="176" y="-202.3" font-family="Times,serif" font-size="14.00">e</text>
</g>
<!-- c&#45;&gt;e -->
<g id="edge6" class="edge"><title>c&#45;&gt;e</title>
<path fill="none" stroke="black" d="M112.483,-164.593C123.668,-172.001 139.356,-182.392 152.219,-190.911"/>
<polygon fill="black" stroke="black" points="150.312,-193.846 160.582,-196.45 154.177,-188.01 150.312,-193.846"/>
<text text-anchor="middle" x="136.5" y="-185.8" font-family="Times,serif" font-size="14.00">1</text>
</g>
<!-- d&#45;&gt;b -->
<g id="edge7" class="edge"><title>d&#45;&gt;b</title>
<path fill="none" stroke="black" d="M320.205,-74.8763C311.208,-73.4911 300.131,-73.1424 291,-77 284.094,-79.9175 277.879,-84.9376 272.669,-90.3183"/>
<polygon fill="black" stroke="black" points="269.694,-88.4067 265.791,-98.2568 274.985,-92.9902 269.694,-88.4067"/>
<text text-anchor="middle" x="294.5" y="-80.8" font-family="Times,serif" font-size="14.00">0</text>
</g>
<!-- f -->
<g id="node8" class="node"><title>f</title>
<ellipse fill="none" stroke="black" cx="176" cy="-46" rx="18" ry="18"/>
<text text-anchor="middle" x="176" y="-42.3" font-family="Times,serif" font-size="14.00">f</text>
</g>
<!-- d&#45;&gt;f -->
<g id="edge8" class="edge"><title>d&#45;&gt;f</title>
<path fill="none" stroke="black" d="M319.923,-75.478C292.098,-69.7389 236.768,-58.3271 203.708,-51.5086"/>
<polygon fill="black" stroke="black" points="204.321,-48.0614 193.82,-49.4692 202.907,-54.9171 204.321,-48.0614"/>
<text text-anchor="middle" x="255" y="-69.8" font-family="Times,serif" font-size="14.00">1</text>
</g>
<!-- e&#45;&gt;b -->
<g id="edge9" class="edge"><title>e&#45;&gt;b</title>
<path fill="none" stroke="black" d="M190.241,-194.796C198.908,-187.136 210.212,-176.503 219,-166 226.507,-157.028 233.803,-146.389 239.774,-137.007"/>
<polygon fill="black" stroke="black" points="242.759,-138.834 245.056,-128.491 236.81,-135.144 242.759,-138.834"/>
<text text-anchor="middle" x="215.5" y="-176.8" font-family="Times,serif" font-size="14.00">0</text>
</g>
<!-- g -->
<g id="node9" class="node"><title>g</title>
<ellipse fill="none" stroke="black" cx="255" cy="-211" rx="18" ry="18"/>
<text text-anchor="middle" x="255" y="-207.3" font-family="Times,serif" font-size="14.00">g</text>
</g>
<!-- e&#45;&gt;g -->
<g id="edge10" class="edge"><title>e&#45;&gt;g</title>
<path fill="none" stroke="black" d="M194.089,-207.11C203.659,-207.731 215.817,-208.521 226.677,-209.226"/>
<polygon fill="black" stroke="black" points="226.753,-212.738 236.959,-209.893 227.207,-205.753 226.753,-212.738"/>
<text text-anchor="middle" x="215.5" y="-211.8" font-family="Times,serif" font-size="14.00">1</text>
</g>
<!-- f&#45;&gt;h -->
<g id="edge12" class="edge"><title>f&#45;&gt;h</title>
<path fill="none" stroke="black" d="M189.02,-33.1864C203.151,-19.5754 227.995,-0 254,-0 254,-0 254,-0 422,-0 453.632,-0 476.677,-31.2311 489.924,-55.8314"/>
<polygon fill="black" stroke="black" points="486.862,-57.5325 494.518,-64.8562 493.1,-54.3566 486.862,-57.5325"/>
<text text-anchor="middle" x="338" y="-3.8" font-family="Times,serif" font-size="14.00">1</text>
</g>
<!-- f&#45;&gt;b -->
<g id="edge11" class="edge"><title>f&#45;&gt;b</title>
<path fill="none" stroke="black" d="M190.834,-56.7689C199.13,-63.3319 209.817,-71.9742 219,-80 224.034,-84.4001 229.343,-89.2757 234.262,-93.899"/>
<polygon fill="black" stroke="black" points="231.917,-96.4985 241.576,-100.852 236.74,-91.4252 231.917,-96.4985"/>
<text text-anchor="middle" x="215.5" y="-83.8" font-family="Times,serif" font-size="14.00">0</text>
</g>
<!-- g&#45;&gt;i -->
<g id="edge13" class="edge"><title>g&#45;&gt;i</title>
<path fill="none" stroke="black" d="M269.741,-199.974C281.437,-190.587 298.524,-176.876 312.548,-165.622"/>
<polygon fill="black" stroke="black" points="314.778,-168.32 320.387,-159.331 310.397,-162.86 314.778,-168.32"/>
<text text-anchor="middle" x="294.5" y="-185.8" font-family="Times,serif" font-size="14.00">0</text>
</g>
<!-- g&#45;&gt;g -->
<g id="edge14" class="edge"><title>g&#45;&gt;g</title>
<path fill="none" stroke="black" d="M248.266,-228.037C246.892,-237.858 249.137,-247 255,-247 258.665,-247 260.916,-243.429 261.753,-238.353"/>
<polygon fill="black" stroke="black" points="265.252,-238.031 261.734,-228.037 258.252,-238.044 265.252,-238.031"/>
<text text-anchor="middle" x="255" y="-250.8" font-family="Times,serif" font-size="14.00">1</text>
</g>
<!-- j&#45;&gt;i -->
<g id="edge19" class="edge"><title>j&#45;&gt;i</title>
<path fill="none" stroke="black" d="M403.34,-139.8C397.561,-140.993 391.021,-142.205 385,-143 380.321,-143.618 375.357,-144.11 370.488,-144.502"/>
<polygon fill="black" stroke="black" points="369.864,-141.036 360.126,-145.209 370.341,-148.02 369.864,-141.036"/>
<text text-anchor="middle" x="381.5" y="-146.8" font-family="Times,serif" font-size="14.00">0</text>
</g>
<!-- j&#45;&gt;h -->
<g id="edge20" class="edge"><title>j&#45;&gt;h</title>
<path fill="none" stroke="black" d="M436.857,-126.646C447.841,-119.73 463.1,-110.122 476.194,-101.878"/>
<polygon fill="black" stroke="black" points="478.237,-104.727 484.835,-96.4375 474.507,-98.8038 478.237,-104.727"/>
<text text-anchor="middle" x="460.5" y="-116.8" font-family="Times,serif" font-size="14.00">1</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1,519 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>joy.utils.generated_library &#8212; Thun 0.4.1 documentation</title>
<link rel="stylesheet" type="text/css" href="../../../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../../../_static/alabaster.css" />
<script data-url_root="../../../" id="documentation_options" src="../../../_static/documentation_options.js"></script>
<script src="../../../_static/jquery.js"></script>
<script src="../../../_static/underscore.js"></script>
<script src="../../../_static/doctools.js"></script>
<link rel="index" title="Index" href="../../../genindex.html" />
<link rel="search" title="Search" href="../../../search.html" />
<link rel="stylesheet" href="../../../_static/custom.css" type="text/css" />
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
</head><body>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<h1>Source code for joy.utils.generated_library</h1><div class="highlight"><pre>
<span></span><span class="c1"># GENERATED FILE. DO NOT EDIT.</span>
<span class="c1"># The code that generated these functions is in the repo history</span>
<span class="c1"># at the v0.4.0 tag.</span>
<span class="kn">from</span> <span class="nn">.errors</span> <span class="kn">import</span> <span class="n">NotAListError</span><span class="p">,</span> <span class="n">StackUnderflowError</span>
<span class="k">def</span> <span class="nf">_Tree_add_Ee</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> ([a4 a5 ...1] a3 a2 a1 -- [a2 a3 ...1])</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="p">(</span><span class="n">a3</span><span class="p">,</span> <span class="p">((</span><span class="n">a4</span><span class="p">,</span> <span class="p">(</span><span class="n">a5</span><span class="p">,</span> <span class="n">s1</span><span class="p">)),</span> <span class="n">s2</span><span class="p">))))</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">return</span> <span class="p">((</span><span class="n">a2</span><span class="p">,</span> <span class="p">(</span><span class="n">a3</span><span class="p">,</span> <span class="n">s1</span><span class="p">)),</span> <span class="n">s2</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">_Tree_delete_R0</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> ([a2 ...1] a1 -- [a2 ...1] a2 a1 a1)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">((</span><span class="n">a2</span><span class="p">,</span> <span class="n">s1</span><span class="p">),</span> <span class="n">s2</span><span class="p">))</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">return</span> <span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="p">((</span><span class="n">a2</span><span class="p">,</span> <span class="n">s1</span><span class="p">),</span> <span class="n">s2</span><span class="p">))))</span>
<span class="k">def</span> <span class="nf">_Tree_delete_clear_stuff</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> (a3 a2 [a1 ...1] -- [...1])</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">((</span><span class="n">a1</span><span class="p">,</span> <span class="n">s1</span><span class="p">),</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="p">(</span><span class="n">a3</span><span class="p">,</span> <span class="n">s2</span><span class="p">)))</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">return</span> <span class="p">(</span><span class="n">s1</span><span class="p">,</span> <span class="n">s2</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">_Tree_get_E</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> ([a3 a4 ...1] a2 a1 -- a4)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="p">((</span><span class="n">a3</span><span class="p">,</span> <span class="p">(</span><span class="n">a4</span><span class="p">,</span> <span class="n">s1</span><span class="p">)),</span> <span class="n">s2</span><span class="p">)))</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">return</span> <span class="p">(</span><span class="n">a4</span><span class="p">,</span> <span class="n">s2</span><span class="p">)</span>
<div class="viewcode-block" id="ccons"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.ccons">[docs]</a><span class="k">def</span> <span class="nf">ccons</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> (a2 a1 [...1] -- [a2 a1 ...1])</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">(</span><span class="n">s1</span><span class="p">,</span> <span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="n">s2</span><span class="p">)))</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">return</span> <span class="p">((</span><span class="n">a2</span><span class="p">,</span> <span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="n">s1</span><span class="p">)),</span> <span class="n">s2</span><span class="p">)</span></div>
<div class="viewcode-block" id="cons"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.cons">[docs]</a><span class="k">def</span> <span class="nf">cons</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> (a1 [...0] -- [a1 ...0])</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span> <span class="n">s0</span><span class="p">,</span> <span class="n">stack</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span> <span class="k">raise</span> <span class="n">StackUnderflowError</span><span class="p">(</span><span class="s1">&#39;Not enough values on stack.&#39;</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">s0</span><span class="p">,</span> <span class="nb">tuple</span><span class="p">):</span> <span class="k">raise</span> <span class="n">NotAListError</span><span class="p">(</span><span class="s1">&#39;Not a list.&#39;</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span> <span class="n">a1</span><span class="p">,</span> <span class="n">s23</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span> <span class="k">raise</span> <span class="n">StackUnderflowError</span><span class="p">(</span><span class="s1">&#39;Not enough values on stack.&#39;</span><span class="p">)</span>
<span class="k">return</span> <span class="p">((</span><span class="n">a1</span><span class="p">,</span> <span class="n">s0</span><span class="p">),</span> <span class="n">s23</span><span class="p">)</span></div>
<div class="viewcode-block" id="dup"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.dup">[docs]</a><span class="k">def</span> <span class="nf">dup</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> (a1 -- a1 a1)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="n">s23</span><span class="p">)</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">return</span> <span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="n">s23</span><span class="p">))</span></div>
<div class="viewcode-block" id="dupd"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.dupd">[docs]</a><span class="k">def</span> <span class="nf">dupd</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> (a2 a1 -- a2 a2 a1)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="n">s23</span><span class="p">))</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">return</span> <span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="n">s23</span><span class="p">)))</span></div>
<div class="viewcode-block" id="dupdd"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.dupdd">[docs]</a><span class="k">def</span> <span class="nf">dupdd</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> (a3 a2 a1 -- a3 a3 a2 a1)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="p">(</span><span class="n">a3</span><span class="p">,</span> <span class="n">s23</span><span class="p">)))</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">return</span> <span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="p">(</span><span class="n">a3</span><span class="p">,</span> <span class="p">(</span><span class="n">a3</span><span class="p">,</span> <span class="n">s23</span><span class="p">))))</span></div>
<div class="viewcode-block" id="first"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.first">[docs]</a><span class="k">def</span> <span class="nf">first</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> ([a1 ...1] -- a1)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">((</span><span class="n">a1</span><span class="p">,</span> <span class="n">s1</span><span class="p">),</span> <span class="n">s23</span><span class="p">)</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">return</span> <span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="n">s23</span><span class="p">)</span></div>
<div class="viewcode-block" id="first_two"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.first_two">[docs]</a><span class="k">def</span> <span class="nf">first_two</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> ([a1 a2 ...1] -- a1 a2)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">((</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="n">s1</span><span class="p">)),</span> <span class="n">s2</span><span class="p">)</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">return</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="n">s2</span><span class="p">))</span></div>
<div class="viewcode-block" id="fourth"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.fourth">[docs]</a><span class="k">def</span> <span class="nf">fourth</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> ([a1 a2 a3 a4 ...1] -- a4)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">((</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="p">(</span><span class="n">a3</span><span class="p">,</span> <span class="p">(</span><span class="n">a4</span><span class="p">,</span> <span class="n">s1</span><span class="p">)))),</span> <span class="n">s2</span><span class="p">)</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">return</span> <span class="p">(</span><span class="n">a4</span><span class="p">,</span> <span class="n">s2</span><span class="p">)</span></div>
<div class="viewcode-block" id="over"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.over">[docs]</a><span class="k">def</span> <span class="nf">over</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> (a2 a1 -- a2 a1 a2)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="n">s23</span><span class="p">))</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">return</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="n">s23</span><span class="p">)))</span></div>
<div class="viewcode-block" id="pop"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.pop">[docs]</a><span class="k">def</span> <span class="nf">pop</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> (a1 --)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="n">s23</span><span class="p">)</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">StackUnderflowError</span><span class="p">(</span><span class="s1">&#39;Cannot pop empty stack.&#39;</span><span class="p">)</span>
<span class="k">return</span> <span class="n">s23</span></div>
<div class="viewcode-block" id="popd"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.popd">[docs]</a><span class="k">def</span> <span class="nf">popd</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> (a2 a1 -- a1)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="n">s23</span><span class="p">))</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">return</span> <span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="n">s23</span><span class="p">)</span></div>
<div class="viewcode-block" id="popdd"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.popdd">[docs]</a><span class="k">def</span> <span class="nf">popdd</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> (a3 a2 a1 -- a2 a1)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="p">(</span><span class="n">a3</span><span class="p">,</span> <span class="n">s23</span><span class="p">)))</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">return</span> <span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="n">s23</span><span class="p">))</span></div>
<div class="viewcode-block" id="popop"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.popop">[docs]</a><span class="k">def</span> <span class="nf">popop</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> (a2 a1 --)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="n">s23</span><span class="p">))</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">return</span> <span class="n">s23</span></div>
<div class="viewcode-block" id="popopd"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.popopd">[docs]</a><span class="k">def</span> <span class="nf">popopd</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> (a3 a2 a1 -- a1)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="p">(</span><span class="n">a3</span><span class="p">,</span> <span class="n">s23</span><span class="p">)))</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">return</span> <span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="n">s23</span><span class="p">)</span></div>
<div class="viewcode-block" id="popopdd"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.popopdd">[docs]</a><span class="k">def</span> <span class="nf">popopdd</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> (a4 a3 a2 a1 -- a2 a1)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="p">(</span><span class="n">a3</span><span class="p">,</span> <span class="p">(</span><span class="n">a4</span><span class="p">,</span> <span class="n">s23</span><span class="p">))))</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">return</span> <span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="n">s23</span><span class="p">))</span></div>
<div class="viewcode-block" id="rest"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.rest">[docs]</a><span class="k">def</span> <span class="nf">rest</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> ([a1 ...0] -- [...0])</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">s0</span><span class="p">,</span> <span class="n">stack</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">StackUnderflowError</span>
<span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">s0</span><span class="p">,</span> <span class="nb">tuple</span><span class="p">):</span>
<span class="k">raise</span> <span class="n">NotAListError</span><span class="p">(</span><span class="s1">&#39;Not a list.&#39;</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">_</span><span class="p">,</span> <span class="n">s1</span> <span class="o">=</span> <span class="n">s0</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">StackUnderflowError</span><span class="p">(</span><span class="s1">&#39;Cannot take rest of empty list.&#39;</span><span class="p">)</span>
<span class="k">return</span> <span class="p">(</span><span class="n">s1</span><span class="p">,</span> <span class="n">stack</span><span class="p">)</span></div>
<div class="viewcode-block" id="rolldown"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.rolldown">[docs]</a><span class="k">def</span> <span class="nf">rolldown</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> (a1 a2 a3 -- a2 a3 a1)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">(</span><span class="n">a3</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="n">s23</span><span class="p">)))</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">return</span> <span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a3</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="n">s23</span><span class="p">)))</span></div>
<div class="viewcode-block" id="rollup"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.rollup">[docs]</a><span class="k">def</span> <span class="nf">rollup</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> (a1 a2 a3 -- a3 a1 a2)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">(</span><span class="n">a3</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="n">s23</span><span class="p">)))</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">return</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a3</span><span class="p">,</span> <span class="n">s23</span><span class="p">)))</span></div>
<div class="viewcode-block" id="rrest"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.rrest">[docs]</a><span class="k">def</span> <span class="nf">rrest</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> ([a1 a2 ...1] -- [...1])</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">((</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="n">s1</span><span class="p">)),</span> <span class="n">s2</span><span class="p">)</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">return</span> <span class="p">(</span><span class="n">s1</span><span class="p">,</span> <span class="n">s2</span><span class="p">)</span></div>
<div class="viewcode-block" id="second"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.second">[docs]</a><span class="k">def</span> <span class="nf">second</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> ([a1 a2 ...1] -- a2)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">((</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="n">s1</span><span class="p">)),</span> <span class="n">s2</span><span class="p">)</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">return</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="n">s2</span><span class="p">)</span></div>
<div class="viewcode-block" id="stack"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.stack">[docs]</a><span class="k">def</span> <span class="nf">stack</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> (... -- ... [...])</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">s0</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">return</span> <span class="p">(</span><span class="n">s0</span><span class="p">,</span> <span class="n">s0</span><span class="p">)</span></div>
<div class="viewcode-block" id="stuncons"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.stuncons">[docs]</a><span class="k">def</span> <span class="nf">stuncons</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> (... a1 -- ... a1 a1 [...])</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="n">s1</span><span class="p">)</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">return</span> <span class="p">(</span><span class="n">s1</span><span class="p">,</span> <span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="n">s1</span><span class="p">)))</span></div>
<div class="viewcode-block" id="stununcons"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.stununcons">[docs]</a><span class="k">def</span> <span class="nf">stununcons</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> (... a2 a1 -- ... a2 a1 a1 a2 [...])</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="n">s1</span><span class="p">))</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">return</span> <span class="p">(</span><span class="n">s1</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="n">s1</span><span class="p">)))))</span></div>
<div class="viewcode-block" id="swaack"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.swaack">[docs]</a><span class="k">def</span> <span class="nf">swaack</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> ([...1] -- [...0])</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="p">(</span><span class="n">s1</span><span class="p">,</span> <span class="n">s0</span><span class="p">)</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">StackUnderflowError</span><span class="p">(</span><span class="s1">&#39;Not enough values on stack.&#39;</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">s1</span><span class="p">,</span> <span class="nb">tuple</span><span class="p">):</span>
<span class="k">raise</span> <span class="n">NotAListError</span><span class="p">(</span><span class="s1">&#39;Not a list.&#39;</span><span class="p">)</span>
<span class="k">return</span> <span class="p">(</span><span class="n">s0</span><span class="p">,</span> <span class="n">s1</span><span class="p">)</span></div>
<div class="viewcode-block" id="swap"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.swap">[docs]</a><span class="k">def</span> <span class="nf">swap</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> (a1 a2 -- a2 a1)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="n">s23</span><span class="p">))</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">StackUnderflowError</span><span class="p">(</span><span class="s1">&#39;Not enough values on stack.&#39;</span><span class="p">)</span>
<span class="k">return</span> <span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="n">s23</span><span class="p">))</span></div>
<div class="viewcode-block" id="swons"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.swons">[docs]</a><span class="k">def</span> <span class="nf">swons</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> ([...1] a1 -- [a1 ...1])</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">s1</span><span class="p">,</span> <span class="n">s2</span><span class="p">))</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">return</span> <span class="p">((</span><span class="n">a1</span><span class="p">,</span> <span class="n">s1</span><span class="p">),</span> <span class="n">s2</span><span class="p">)</span></div>
<div class="viewcode-block" id="third"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.third">[docs]</a><span class="k">def</span> <span class="nf">third</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> ([a1 a2 a3 ...1] -- a3)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">((</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="p">(</span><span class="n">a3</span><span class="p">,</span> <span class="n">s1</span><span class="p">))),</span> <span class="n">s2</span><span class="p">)</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">return</span> <span class="p">(</span><span class="n">a3</span><span class="p">,</span> <span class="n">s2</span><span class="p">)</span></div>
<div class="viewcode-block" id="tuck"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.tuck">[docs]</a><span class="k">def</span> <span class="nf">tuck</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> (a2 a1 -- a1 a2 a1)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="n">s23</span><span class="p">))</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">return</span> <span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">a2</span><span class="p">,</span> <span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="n">s23</span><span class="p">)))</span></div>
<div class="viewcode-block" id="uncons"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.uncons">[docs]</a><span class="k">def</span> <span class="nf">uncons</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> ([a1 ...0] -- a1 [...0])</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">((</span><span class="n">a1</span><span class="p">,</span> <span class="n">s0</span><span class="p">),</span> <span class="n">s23</span><span class="p">)</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">return</span> <span class="p">(</span><span class="n">s0</span><span class="p">,</span> <span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="n">s23</span><span class="p">))</span></div>
<div class="viewcode-block" id="unit"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.unit">[docs]</a><span class="k">def</span> <span class="nf">unit</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> (a1 -- [a1 ])</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="n">s23</span><span class="p">)</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">return</span> <span class="p">((</span><span class="n">a1</span><span class="p">,</span> <span class="p">()),</span> <span class="n">s23</span><span class="p">)</span></div>
<div class="viewcode-block" id="unswons"><a class="viewcode-back" href="../../../library.html#joy.utils.generated_library.unswons">[docs]</a><span class="k">def</span> <span class="nf">unswons</span><span class="p">(</span><span class="n">stack</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> ::</span>
<span class="sd"> ([a1 ...1] -- [...1] a1)</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="p">((</span><span class="n">a1</span><span class="p">,</span> <span class="n">s1</span><span class="p">),</span> <span class="n">s2</span><span class="p">)</span> <span class="o">=</span> <span class="n">stack</span>
<span class="k">return</span> <span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="p">(</span><span class="n">s1</span><span class="p">,</span> <span class="n">s2</span><span class="p">))</span></div>
</pre></div>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h1 class="logo"><a href="../../../index.html">Thun</a></h1>
<h3>Navigation</h3>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../../../notebooks/Intro.html">Thun: Joy in Python</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../joy.html">Joy Interpreter</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../stack.html">Stack or Quote or Sequence or List…</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../parser.html">Parsing Text into Joy Expressions</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../pretty.html">Tracing Joy Execution</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../library.html">Function Reference</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../lib.html">Functions Grouped by, er, Function with Examples</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../types.html">Type Inference of Joy Expressions</a></li>
<li class="toctree-l1"><a class="reference internal" href="../../../notebooks/index.html">Essays about Programming in Joy</a></li>
</ul>
<div class="relations">
<h3>Related Topics</h3>
<ul>
<li><a href="../../../index.html">Documentation overview</a><ul>
<li><a href="../../index.html">Module code</a><ul>
</ul></li>
</ul></li>
</ul>
</div>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="../../../search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>$('#searchbox').show(0);</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer" role="contentinfo">
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">
<img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" />
</a>
<br />
<span xmlns:dct="http://purl.org/dc/terms/" property="dct:title">Thun Documentation</span> by <a xmlns:cc="http://creativecommons.org/ns#" href="https://joypy.osdn.io/" property="cc:attributionName" rel="cc:attributionURL">Simon Forman</a> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License</a>.<br />Based on a work at <a xmlns:dct="http://purl.org/dc/terms/" href="https://osdn.net/projects/joypy/" rel="dct:source">https://osdn.net/projects/joypy/</a>.
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 4.3.0.
</div>
</body>
</html>

View File

@ -0,0 +1,17 @@
***********************
Categorical Programming
***********************
DRAFT
`Categorical <https://en.wikipedia.org/wiki/Category_theory>`__
In Manfred von Thun's article `Joy compared with other functional languages <http://www.kevinalbrecht.com/code/joy-mirror/j08cnt.html>`__ he asks, "Could the language of categories be used for writing programs? Any lambda expression can be translated into a categorical expression, so the language of categories is expressively complete. But this does not make it a suitable language for writing programs. As it stands it is a very low-level language."
In `Compiling to categories <http://conal.net/papers/compiling-to-categories/>`__ Conal Elliott give a taste of what this might mean.
It is well-known that the simply typed lambda-calculus is modeled by any cartesian closed category (CCC). This correspondence suggests giving typed functional programs a variety of interpretations, each corresponding to a different category. A convenient way to realize this idea is as a collection of meaning-preserving transformations added to an existing compiler, such as GHC for Haskell. This paper describes such an implementation and demonstrates its use for a variety of interpretations including hardware circuits, automatic differentiation, incremental computation, and interval analysis. Each such interpretation is a category easily defined in Haskell (outside of the compiler). The general technique appears to provide a compelling alternative to deeply embedded domain-specific languages.
What he's doing is translating lambda forms into a kind of "point-free" style that is very close to Joy code (although more verbose) and then showing how to instantiate that code over different categories to get several different kinds of program out of the same code.

View File

@ -0,0 +1,946 @@
∂RE
===
Brzozowskis Derivatives of Regular Expressions
-----------------------------------------------
Legend:
::
∧ intersection
union
∘ concatenation (see below)
¬ complement
ϕ empty set (aka ∅)
λ singleton set containing just the empty string
I set of all letters in alphabet
Derivative of a set ``R`` of strings and a string ``a``:
::
∂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)
∂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)
Some rules we will use later for “compaction”:
::
R ∧ ϕ = ϕ ∧ R = ϕ
R ∧ I = I ∧ R = R
R ϕ = ϕ R = R
R I = I R = I
R∘ϕ = ϕ∘R = ϕ
R∘λ = λ∘R = R
Concatination of sets: for two sets A and B the set A∘B is defined as:
{a∘b for a in A for b in B}
E.g.:
{a, b}∘{c, d} → {ac, ad, bc, bd}
Implementation
--------------
.. code:: python
from functools import partial as curry
from itertools import product
``ϕ`` and ``λ``
~~~~~~~~~~~~~~~
The empty set and the set of just the empty string.
.. code:: ipython2
phi = frozenset() # ϕ
y = frozenset({''}) # λ
Two-letter Alphabet
~~~~~~~~~~~~~~~~~~~
Im only going to use two symbols (at first) becaase this is enough to
illustrate the algorithm and because you can represent any other
alphabet with two symbols (if you had to.)
I chose the names ``O`` and ``l`` (uppercase “o” and lowercase “L”) to
look like ``0`` and ``1`` (zero and one) respectively.
.. code:: ipython2
syms = O, l = frozenset({'0'}), frozenset({'1'})
Representing Regular Expressions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To represent REs in Python Im going to use tagged tuples. A *regular
expression* is one of:
::
O
l
(KSTAR, R)
(NOT, R)
(AND, R, S)
(CONS, R, S)
(OR, R, S)
Where ``R`` and ``S`` stand for *regular expressions*.
.. code:: ipython2
AND, CONS, KSTAR, NOT, OR = 'and cons * not or'.split() # Tags are just strings.
Because they are formed of ``frozenset``, ``tuple`` and ``str`` objects
only, these datastructures are immutable.
String Representation of RE Datastructures
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code:: ipython2
def stringy(re):
'''
Return a nice string repr for a regular expression datastructure.
'''
if re == I: return '.'
if re in syms: return next(iter(re))
if re == y: return '^'
if re == phi: return 'X'
assert isinstance(re, tuple), repr(re)
tag = re[0]
if tag == KSTAR:
body = stringy(re[1])
if not body: return body
if len(body) > 1: return '(' + body + ")*"
return body + '*'
if tag == NOT:
body = stringy(re[1])
if not body: return body
if len(body) > 1: return '(' + body + ")'"
return body + "'"
r, s = stringy(re[1]), stringy(re[2])
if tag == CONS: return r + s
if tag == OR: return '%s | %s' % (r, s)
if tag == AND: return '(%s) & (%s)' % (r, s)
raise ValueError
``I``
~~~~~
Match anything. Often spelled “.”
::
I = (0|1)*
.. code:: ipython2
I = (KSTAR, (OR, O, l))
.. code:: ipython2
print stringy(I)
.. parsed-literal::
.
``(.111.) & (.01 + 11*)'``
~~~~~~~~~~~~~~~~~~~~~~~~~~
The example expression from Brzozowski:
::
(.111.) & (.01 + 11*)'
a & (b + c)'
Note that it contains one of everything.
.. code:: ipython2
a = (CONS, I, (CONS, l, (CONS, l, (CONS, l, I))))
b = (CONS, I, (CONS, O, l))
c = (CONS, l, (KSTAR, l))
it = (AND, a, (NOT, (OR, b, c)))
.. code:: ipython2
print stringy(it)
.. parsed-literal::
(.111.) & ((.01 | 11*)')
``nully()``
~~~~~~~~~~~
Lets get that auxiliary predicate function ``δ`` out of the way.
.. code:: ipython2
def nully(R):
'''
δ - Return λ if λ ⊆ R otherwise ϕ.
'''
# δ(a) → ϕ
# δ(ϕ) → ϕ
if R in syms or R == phi:
return phi
# δ(λ) → λ
if R == y:
return y
tag = R[0]
# δ(R*) → λ
if tag == KSTAR:
return y
# δ(¬R) δ(R)≟ϕ → λ
# δ(¬R) δ(R)≟λ → ϕ
if tag == NOT:
return phi if nully(R[1]) else y
# δ(R∘S) → δ(R) ∧ δ(S)
# δ(R ∧ S) → δ(R) ∧ δ(S)
# δ(R S) → δ(R) δ(S)
r, s = nully(R[1]), nully(R[2])
return r & s if tag in {AND, CONS} else r | s
No “Compaction”
~~~~~~~~~~~~~~~
This is the straightforward version with no “compaction”. It works fine,
but does waaaay too much work because the expressions grow each
derivation.
.. code:: ipython2
def D(symbol):
def derv(R):
# ∂a(a) → λ
if R == {symbol}:
return y
# ∂a(λ) → ϕ
# ∂a(ϕ) → ϕ
# ∂a(¬a) → ϕ
if R == y or R == phi or R in syms:
return phi
tag = R[0]
# ∂a(R*) → ∂a(R)∘R*
if tag == KSTAR:
return (CONS, derv(R[1]), R)
# ∂a(¬R) → ¬∂a(R)
if tag == NOT:
return (NOT, derv(R[1]))
r, s = R[1:]
# ∂a(R∘S) → ∂a(R)∘S δ(R)∘∂a(S)
if tag == CONS:
A = (CONS, derv(r), s) # A = ∂a(R)∘S
# A δ(R) ∘ ∂a(S)
# A λ ∘ ∂a(S) → A ∂a(S)
# A ϕ ∘ ∂a(S) → A ϕ → A
return (OR, A, derv(s)) if nully(r) else A
# ∂a(R ∧ S) → ∂a(R) ∧ ∂a(S)
# ∂a(R S) → ∂a(R) ∂a(S)
return (tag, derv(r), derv(s))
return derv
Compaction Rules
~~~~~~~~~~~~~~~~
.. code:: ipython2
def _compaction_rule(relation, one, zero, a, b):
return (
b if a == one else # R*1 = 1*R = R
a if b == one else
zero if a == zero or b == zero else # R*0 = 0*R = 0
(relation, a, b)
)
An elegant symmetry.
.. code:: ipython2
# R ∧ I = I ∧ R = R
# R ∧ ϕ = ϕ ∧ R = ϕ
_and = curry(_compaction_rule, AND, I, phi)
# R ϕ = ϕ R = R
# R I = I R = I
_or = curry(_compaction_rule, OR, phi, I)
# R∘λ = λ∘R = R
# R∘ϕ = ϕ∘R = ϕ
_cons = curry(_compaction_rule, CONS, y, phi)
Memoizing
~~~~~~~~~
We can save re-processing by remembering results we have already
computed. RE datastructures are immutable and the ``derv()`` functions
are *pure* so this is fine.
.. code:: ipython2
class Memo(object):
def __init__(self, f):
self.f = f
self.calls = self.hits = 0
self.mem = {}
def __call__(self, key):
self.calls += 1
try:
result = self.mem[key]
self.hits += 1
except KeyError:
result = self.mem[key] = self.f(key)
return result
With “Compaction”
~~~~~~~~~~~~~~~~~
This version uses the rules above to perform compaction. It keeps the
expressions from growing too large.
.. code:: ipython2
def D_compaction(symbol):
@Memo
def derv(R):
# ∂a(a) → λ
if R == {symbol}:
return y
# ∂a(λ) → ϕ
# ∂a(ϕ) → ϕ
# ∂a(¬a) → ϕ
if R == y or R == phi or R in syms:
return phi
tag = R[0]
# ∂a(R*) → ∂a(R)∘R*
if tag == KSTAR:
return _cons(derv(R[1]), R)
# ∂a(¬R) → ¬∂a(R)
if tag == NOT:
return (NOT, derv(R[1]))
r, s = R[1:]
# ∂a(R∘S) → ∂a(R)∘S δ(R)∘∂a(S)
if tag == CONS:
A = _cons(derv(r), s) # A = ∂a(r)∘s
# A δ(R) ∘ ∂a(S)
# A λ ∘ ∂a(S) → A ∂a(S)
# A ϕ ∘ ∂a(S) → A ϕ → A
return _or(A, derv(s)) if nully(r) else A
# ∂a(R ∧ S) → ∂a(R) ∧ ∂a(S)
# ∂a(R S) → ∂a(R) ∂a(S)
dr, ds = derv(r), derv(s)
return _and(dr, ds) if tag == AND else _or(dr, ds)
return derv
Lets try it out…
-----------------
(FIXME: redo.)
.. code:: ipython2
o, z = D_compaction('0'), D_compaction('1')
REs = set()
N = 5
names = list(product(*(N * [(0, 1)])))
dervs = list(product(*(N * [(o, z)])))
for name, ds in zip(names, dervs):
R = it
ds = list(ds)
while ds:
R = ds.pop()(R)
if R == phi or R == I:
break
REs.add(R)
print stringy(it) ; print
print o.hits, '/', o.calls
print z.hits, '/', z.calls
print
for s in sorted(map(stringy, REs), key=lambda n: (len(n), n)):
print s
.. parsed-literal::
(.111.) & ((.01 | 11*)')
92 / 122
92 / 122
(.01)'
(.01 | 1)'
(.01 | ^)'
(.01 | 1*)'
(.111.) & ((.01 | 1)')
(.111. | 11.) & ((.01 | ^)')
(.111. | 11. | 1.) & ((.01)')
(.111. | 11.) & ((.01 | 1*)')
(.111. | 11. | 1.) & ((.01 | 1*)')
Should match:
::
(.111.) & ((.01 | 11*)')
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*)')
Larger Alphabets
----------------
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 youll see that we never use the “set-ness” of the symbols ``O``
and ``l``. The only time Python set operators (``&`` and ``|``) appear
is in the ``nully()`` function, and there they operate on (recursively
computed) outputs of that function, never ``O`` and ``l``.
What if we try:
::
(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
λ
And compare it to:
::
{'0', '1')
∂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
for now.
State Machine
-------------
We can drive the regular expressions to flesh out the underlying state
machine transition table.
::
.111. & (.01 + 11*)'
Says, “Three or more 1s and not ending in 01 nor composed of all 1s.”
.. figure:: omg.svg
:alt: State Machine Graph
State Machine Graph
Start at ``a`` and follow the transition arrows according to their
labels. Accepting states have a double outline. (Graphic generated with
`Dot from Graphviz <http://www.graphviz.org/>`__.) Youll see that only
paths that lead to one of the accepting states will match the regular
expression. All other paths will terminate at one of the non-accepting
states.
Theres a happy path to ``g`` along 111:
::
a→c→e→g
After you reach ``g`` youre stuck there eating 1s until you see a 0,
which takes you to the ``i→j→i|i→j→h→i`` “trap”. You cant reach any
other states from those two loops.
If you see a 0 before you see 111 you will reach ``b``, which forms
another “trap” with ``d`` and ``f``. The only way out is another happy
path along 111 to ``h``:
::
b→d→f→h
Once you have reached ``h`` you can see as many 1s or as many 0 in a
row and still be either still at ``h`` (for 1s) or move to ``i`` (for
0s). If you find yourself at ``i`` you can see as many 0s, or
repetitions of 10, as there are, but if you see just a 1 you move to
``j``.
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 arrows symbol.
If we label the initial RE ``a``, we can say:
::
a --0--> ∂0(a)
a --1--> ∂1(a)
And so on, each new unique RE is a new state in the FSM table.
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 | ^)'
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.
.. code:: ipython2
from collections import defaultdict
from pprint import pprint
from string import ascii_lowercase
.. code:: ipython2
d0, d1 = D_compaction('0'), D_compaction('1')
``explore()``
~~~~~~~~~~~~~
.. code:: ipython2
def explore(re):
# Don't have more than 26 states...
names = defaultdict(iter(ascii_lowercase).next)
table, accepting = dict(), set()
to_check = {re}
while to_check:
re = to_check.pop()
state_name = names[re]
if (state_name, 0) in table:
continue
if nully(re):
accepting.add(state_name)
o, i = d0(re), d1(re)
table[state_name, 0] = names[o] ; to_check.add(o)
table[state_name, 1] = names[i] ; to_check.add(i)
return table, accepting
.. code:: ipython2
table, accepting = explore(it)
table
.. parsed-literal::
{('a', 0): 'b',
('a', 1): 'c',
('b', 0): 'b',
('b', 1): 'd',
('c', 0): 'b',
('c', 1): 'e',
('d', 0): 'b',
('d', 1): 'f',
('e', 0): 'b',
('e', 1): 'g',
('f', 0): 'b',
('f', 1): 'h',
('g', 0): 'i',
('g', 1): 'g',
('h', 0): 'i',
('h', 1): 'h',
('i', 0): 'i',
('i', 1): 'j',
('j', 0): 'i',
('j', 1): 'h'}
.. code:: ipython2
accepting
.. parsed-literal::
{'h', 'i'}
Generate Diagram
~~~~~~~~~~~~~~~~
Once we have the FSM table and the set of accepting states we can
generate the diagram above.
.. code:: ipython2
_template = '''\
digraph finite_state_machine {
rankdir=LR;
size="8,5"
node [shape = doublecircle]; %s;
node [shape = circle];
%s
}
'''
def link(fr, nm, label):
return ' %s -> %s [ label = "%s" ];' % (fr, nm, label)
def make_graph(table, accepting):
return _template % (
' '.join(accepting),
'\n'.join(
link(from_, to, char)
for (from_, char), (to) in sorted(table.iteritems())
)
)
.. code:: ipython2
print make_graph(table, accepting)
.. parsed-literal::
digraph finite_state_machine {
rankdir=LR;
size="8,5"
node [shape = doublecircle]; i h;
node [shape = circle];
a -> b [ label = "0" ];
a -> c [ label = "1" ];
b -> b [ label = "0" ];
b -> d [ label = "1" ];
c -> b [ label = "0" ];
c -> e [ label = "1" ];
d -> b [ label = "0" ];
d -> f [ label = "1" ];
e -> b [ label = "0" ];
e -> g [ label = "1" ];
f -> b [ label = "0" ];
f -> h [ label = "1" ];
g -> i [ label = "0" ];
g -> g [ label = "1" ];
h -> i [ label = "0" ];
h -> h [ label = "1" ];
i -> i [ label = "0" ];
i -> j [ label = "1" ];
j -> i [ label = "0" ];
j -> h [ label = "1" ];
}
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
datastructure, the code below instead acts like JMP instructions
(“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”
function.
.. code:: ipython2
def trampoline(input_, jump_from, accepting):
I = iter(input_)
while True:
try:
bounce_to = jump_from(I)
except StopIteration:
return jump_from in accepting
jump_from = bounce_to
Stream Functions
^^^^^^^^^^^^^^^^
Little helpers to process the iterator of our data (a “stream” of “1”
and “0” characters, not bits.)
.. code:: ipython2
getch = lambda I: int(next(I))
def _1(I):
'''Loop on ones.'''
while getch(I): pass
def _0(I):
'''Loop on zeros.'''
while not getch(I): pass
A Finite State Machine
^^^^^^^^^^^^^^^^^^^^^^
With those preliminaries out of the way, from the state table of
``.111. & (.01 + 11*)'`` we can immediately write down state machine
code. (You have to imagine that these are GOTO statements in C or
branches in assembly and that the state names are branch destination
labels.)
.. code:: ipython2
a = lambda I: c if getch(I) else b
b = lambda I: _0(I) or d
c = lambda I: e if getch(I) else b
d = lambda I: f if getch(I) else b
e = lambda I: g if getch(I) else b
f = lambda I: h if getch(I) else b
g = lambda I: _1(I) or i
h = lambda I: _1(I) or i
i = lambda I: _0(I) or j
j = lambda I: h if getch(I) else i
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`` isnt.
.. code:: ipython2
def acceptable(input_):
return trampoline(input_, a, {h, i})
.. code:: ipython2
for n in range(2**5):
s = bin(n)[2:]
print '%05s' % s, acceptable(s)
.. parsed-literal::
0 False
1 False
10 False
11 False
100 False
101 False
110 False
111 False
1000 False
1001 False
1010 False
1011 False
1100 False
1101 False
1110 True
1111 False
10000 False
10001 False
10010 False
10011 False
10100 False
10101 False
10110 False
10111 True
11000 False
11001 False
11010 False
11011 False
11100 True
11101 False
11110 True
11111 False
Reversing the Derivatives to Generate Matching Strings
------------------------------------------------------
(UNFINISHED) Brzozowski also shewed how to go from the state machine to
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
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)
Consider:
::
c = d1(a)
b = d0(c)
Substituting:
::
b = d0(d1(a))
Unwrapping:
::
b = d10(a)
::
j = d1(d0(j))
Unwrapping:
::
j = d1(d0(j)) = d01(j)
We have a loop or “fixed point”.
::
j = d01(j) = d0101(j) = d010101(j) = ...
hmm…
::
j = (01)*

View File

@ -0,0 +1,815 @@
***************************
Developing a Program in Joy
***************************
As an example of developing a program in Joy let's take the first problem from the Project Euler website.
`Project Euler, first problem: "Multiples of 3 and 5" <https://projecteuler.net/problem=1>`__
=============================================================================================
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.
.. code:: ipython2
from notebook_preamble import J, V, define
Sum a range filtered by a predicate
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Let's create a predicate that returns ``True`` if a number is a multiple
of 3 or 5 and ``False`` otherwise.
.. code:: ipython2
define('P == [3 % not] dupdip 5 % not or')
.. code:: ipython2
V('80 P')
.. parsed-literal::
. 80 P
80 . P
80 . [3 % not] dupdip 5 % not or
80 [3 % not] . dupdip 5 % not or
80 . 3 % not 80 5 % not or
80 3 . % not 80 5 % not or
2 . not 80 5 % not or
False . 80 5 % not or
False 80 . 5 % not or
False 80 5 . % not or
False 0 . not or
False True . or
True .
Given the predicate function ``P`` a suitable program is:
::
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.
Logically this is fine, but pragmatically we are doing more work than we
should be; we generate one thousand integers but actually use less than
half of them. A better solution would be to generate just the multiples
we want to sum, and to add them as we go rather than storing them and
and summing them at the end.
Generate just the multiples
^^^^^^^^^^^^^^^^^^^^^^^^^^^
At first I had the idea to use two counters and increase them by three
and five, respectively. This way we only generate the terms that we
actually want to sum. We have to proceed by incrementing the counter
that is lower, or if they are equal, the three counter, and we have to
take care not to double add numbers like 15 that are multiples of both
three and five.
This seemed a little clunky, so I tried a different approach.
Consider the first few terms in the series:
::
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 ...
You get this lovely repeating palindromic sequence:
::
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.
To make use of this sequence to increment a counter and sum terms as we
go we need a function that will accept the sum, the counter, and the
next term to add, and that adds the term to the counter and a copy of
the counter to the running sum. This function will do that:
::
PE1.1 == + [+] dupdip
.. code:: ipython2
define('PE1.1 == + [+] dupdip')
.. code:: ipython2
V('0 0 3 PE1.1')
.. parsed-literal::
. 0 0 3 PE1.1
0 . 0 3 PE1.1
0 0 . 3 PE1.1
0 0 3 . PE1.1
0 0 3 . + [+] dupdip
0 3 . [+] dupdip
0 3 [+] . dupdip
0 3 . + 3
3 . 3
3 3 .
.. code:: ipython2
V('0 0 [3 2 1 3 1 2 3] [PE1.1] step')
.. parsed-literal::
. 0 0 [3 2 1 3 1 2 3] [PE1.1] step
0 . 0 [3 2 1 3 1 2 3] [PE1.1] step
0 0 . [3 2 1 3 1 2 3] [PE1.1] step
0 0 [3 2 1 3 1 2 3] . [PE1.1] step
0 0 [3 2 1 3 1 2 3] [PE1.1] . step
0 0 3 [PE1.1] . i [2 1 3 1 2 3] [PE1.1] step
0 0 3 . PE1.1 [2 1 3 1 2 3] [PE1.1] step
0 0 3 . + [+] dupdip [2 1 3 1 2 3] [PE1.1] step
0 3 . [+] dupdip [2 1 3 1 2 3] [PE1.1] step
0 3 [+] . dupdip [2 1 3 1 2 3] [PE1.1] step
0 3 . + 3 [2 1 3 1 2 3] [PE1.1] step
3 . 3 [2 1 3 1 2 3] [PE1.1] step
3 3 . [2 1 3 1 2 3] [PE1.1] step
3 3 [2 1 3 1 2 3] . [PE1.1] step
3 3 [2 1 3 1 2 3] [PE1.1] . step
3 3 2 [PE1.1] . i [1 3 1 2 3] [PE1.1] step
3 3 2 . PE1.1 [1 3 1 2 3] [PE1.1] step
3 3 2 . + [+] dupdip [1 3 1 2 3] [PE1.1] step
3 5 . [+] dupdip [1 3 1 2 3] [PE1.1] step
3 5 [+] . dupdip [1 3 1 2 3] [PE1.1] step
3 5 . + 5 [1 3 1 2 3] [PE1.1] step
8 . 5 [1 3 1 2 3] [PE1.1] step
8 5 . [1 3 1 2 3] [PE1.1] step
8 5 [1 3 1 2 3] . [PE1.1] step
8 5 [1 3 1 2 3] [PE1.1] . step
8 5 1 [PE1.1] . i [3 1 2 3] [PE1.1] step
8 5 1 . PE1.1 [3 1 2 3] [PE1.1] step
8 5 1 . + [+] dupdip [3 1 2 3] [PE1.1] step
8 6 . [+] dupdip [3 1 2 3] [PE1.1] step
8 6 [+] . dupdip [3 1 2 3] [PE1.1] step
8 6 . + 6 [3 1 2 3] [PE1.1] step
14 . 6 [3 1 2 3] [PE1.1] step
14 6 . [3 1 2 3] [PE1.1] step
14 6 [3 1 2 3] . [PE1.1] step
14 6 [3 1 2 3] [PE1.1] . step
14 6 3 [PE1.1] . i [1 2 3] [PE1.1] step
14 6 3 . PE1.1 [1 2 3] [PE1.1] step
14 6 3 . + [+] dupdip [1 2 3] [PE1.1] step
14 9 . [+] dupdip [1 2 3] [PE1.1] step
14 9 [+] . dupdip [1 2 3] [PE1.1] step
14 9 . + 9 [1 2 3] [PE1.1] step
23 . 9 [1 2 3] [PE1.1] step
23 9 . [1 2 3] [PE1.1] step
23 9 [1 2 3] . [PE1.1] step
23 9 [1 2 3] [PE1.1] . step
23 9 1 [PE1.1] . i [2 3] [PE1.1] step
23 9 1 . PE1.1 [2 3] [PE1.1] step
23 9 1 . + [+] dupdip [2 3] [PE1.1] step
23 10 . [+] dupdip [2 3] [PE1.1] step
23 10 [+] . dupdip [2 3] [PE1.1] step
23 10 . + 10 [2 3] [PE1.1] step
33 . 10 [2 3] [PE1.1] step
33 10 . [2 3] [PE1.1] step
33 10 [2 3] . [PE1.1] step
33 10 [2 3] [PE1.1] . step
33 10 2 [PE1.1] . i [3] [PE1.1] step
33 10 2 . PE1.1 [3] [PE1.1] step
33 10 2 . + [+] dupdip [3] [PE1.1] step
33 12 . [+] dupdip [3] [PE1.1] step
33 12 [+] . dupdip [3] [PE1.1] step
33 12 . + 12 [3] [PE1.1] step
45 . 12 [3] [PE1.1] step
45 12 . [3] [PE1.1] step
45 12 [3] . [PE1.1] step
45 12 [3] [PE1.1] . step
45 12 3 [PE1.1] . i
45 12 3 . PE1.1
45 12 3 . + [+] dupdip
45 15 . [+] dupdip
45 15 [+] . dupdip
45 15 . + 15
60 . 15
60 15 .
So one ``step`` through all seven terms brings the counter to 15 and the
total to 60.
How many multiples to sum?
^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code:: ipython2
1000 / 15
.. parsed-literal::
66
.. code:: ipython2
66 * 15
.. parsed-literal::
990
.. code:: ipython2
1000 - 990
.. parsed-literal::
10
We only want the terms *less than* 1000.
.. code:: ipython2
999 - 990
.. parsed-literal::
9
That means we want to run the full list of numbers sixty-six times to
get to 990 and then the first four numbers 3 2 1 3 to get to 999.
.. code:: ipython2
define('PE1 == 0 0 66 [[3 2 1 3 1 2 3] [PE1.1] step] times [3 2 1 3] [PE1.1] step pop')
.. code:: ipython2
J('PE1')
.. parsed-literal::
233168
Packing the terms into an integer
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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
each term as we go. This will use less space and save time loading whole
integer terms from the list.
::
3 2 1 3 1 2 3
0b 11 10 01 11 01 10 11 == 14811
.. code:: ipython2
0b11100111011011
.. parsed-literal::
14811
.. code:: ipython2
define('PE1.2 == [3 & PE1.1] dupdip 2 >>')
.. code:: ipython2
V('0 0 14811 PE1.2')
.. parsed-literal::
. 0 0 14811 PE1.2
0 . 0 14811 PE1.2
0 0 . 14811 PE1.2
0 0 14811 . PE1.2
0 0 14811 . [3 & PE1.1] dupdip 2 >>
0 0 14811 [3 & PE1.1] . dupdip 2 >>
0 0 14811 . 3 & PE1.1 14811 2 >>
0 0 14811 3 . & PE1.1 14811 2 >>
0 0 3 . PE1.1 14811 2 >>
0 0 3 . + [+] dupdip 14811 2 >>
0 3 . [+] dupdip 14811 2 >>
0 3 [+] . dupdip 14811 2 >>
0 3 . + 3 14811 2 >>
3 . 3 14811 2 >>
3 3 . 14811 2 >>
3 3 14811 . 2 >>
3 3 14811 2 . >>
3 3 3702 .
.. code:: ipython2
V('3 3 3702 PE1.2')
.. parsed-literal::
. 3 3 3702 PE1.2
3 . 3 3702 PE1.2
3 3 . 3702 PE1.2
3 3 3702 . PE1.2
3 3 3702 . [3 & PE1.1] dupdip 2 >>
3 3 3702 [3 & PE1.1] . dupdip 2 >>
3 3 3702 . 3 & PE1.1 3702 2 >>
3 3 3702 3 . & PE1.1 3702 2 >>
3 3 2 . PE1.1 3702 2 >>
3 3 2 . + [+] dupdip 3702 2 >>
3 5 . [+] dupdip 3702 2 >>
3 5 [+] . dupdip 3702 2 >>
3 5 . + 5 3702 2 >>
8 . 5 3702 2 >>
8 5 . 3702 2 >>
8 5 3702 . 2 >>
8 5 3702 2 . >>
8 5 925 .
.. code:: ipython2
V('0 0 14811 7 [PE1.2] times pop')
.. parsed-literal::
. 0 0 14811 7 [PE1.2] times pop
0 . 0 14811 7 [PE1.2] times pop
0 0 . 14811 7 [PE1.2] times pop
0 0 14811 . 7 [PE1.2] times pop
0 0 14811 7 . [PE1.2] times pop
0 0 14811 7 [PE1.2] . times pop
0 0 14811 [PE1.2] . i 6 [PE1.2] times pop
0 0 14811 . PE1.2 6 [PE1.2] times pop
0 0 14811 . [3 & PE1.1] dupdip 2 >> 6 [PE1.2] times pop
0 0 14811 [3 & PE1.1] . dupdip 2 >> 6 [PE1.2] times pop
0 0 14811 . 3 & PE1.1 14811 2 >> 6 [PE1.2] times pop
0 0 14811 3 . & PE1.1 14811 2 >> 6 [PE1.2] times pop
0 0 3 . PE1.1 14811 2 >> 6 [PE1.2] times pop
0 0 3 . + [+] dupdip 14811 2 >> 6 [PE1.2] times pop
0 3 . [+] dupdip 14811 2 >> 6 [PE1.2] times pop
0 3 [+] . dupdip 14811 2 >> 6 [PE1.2] times pop
0 3 . + 3 14811 2 >> 6 [PE1.2] times pop
3 . 3 14811 2 >> 6 [PE1.2] times pop
3 3 . 14811 2 >> 6 [PE1.2] times pop
3 3 14811 . 2 >> 6 [PE1.2] times pop
3 3 14811 2 . >> 6 [PE1.2] times pop
3 3 3702 . 6 [PE1.2] times pop
3 3 3702 6 . [PE1.2] times pop
3 3 3702 6 [PE1.2] . times pop
3 3 3702 [PE1.2] . i 5 [PE1.2] times pop
3 3 3702 . PE1.2 5 [PE1.2] times pop
3 3 3702 . [3 & PE1.1] dupdip 2 >> 5 [PE1.2] times pop
3 3 3702 [3 & PE1.1] . dupdip 2 >> 5 [PE1.2] times pop
3 3 3702 . 3 & PE1.1 3702 2 >> 5 [PE1.2] times pop
3 3 3702 3 . & PE1.1 3702 2 >> 5 [PE1.2] times pop
3 3 2 . PE1.1 3702 2 >> 5 [PE1.2] times pop
3 3 2 . + [+] dupdip 3702 2 >> 5 [PE1.2] times pop
3 5 . [+] dupdip 3702 2 >> 5 [PE1.2] times pop
3 5 [+] . dupdip 3702 2 >> 5 [PE1.2] times pop
3 5 . + 5 3702 2 >> 5 [PE1.2] times pop
8 . 5 3702 2 >> 5 [PE1.2] times pop
8 5 . 3702 2 >> 5 [PE1.2] times pop
8 5 3702 . 2 >> 5 [PE1.2] times pop
8 5 3702 2 . >> 5 [PE1.2] times pop
8 5 925 . 5 [PE1.2] times pop
8 5 925 5 . [PE1.2] times pop
8 5 925 5 [PE1.2] . times pop
8 5 925 [PE1.2] . i 4 [PE1.2] times pop
8 5 925 . PE1.2 4 [PE1.2] times pop
8 5 925 . [3 & PE1.1] dupdip 2 >> 4 [PE1.2] times pop
8 5 925 [3 & PE1.1] . dupdip 2 >> 4 [PE1.2] times pop
8 5 925 . 3 & PE1.1 925 2 >> 4 [PE1.2] times pop
8 5 925 3 . & PE1.1 925 2 >> 4 [PE1.2] times pop
8 5 1 . PE1.1 925 2 >> 4 [PE1.2] times pop
8 5 1 . + [+] dupdip 925 2 >> 4 [PE1.2] times pop
8 6 . [+] dupdip 925 2 >> 4 [PE1.2] times pop
8 6 [+] . dupdip 925 2 >> 4 [PE1.2] times pop
8 6 . + 6 925 2 >> 4 [PE1.2] times pop
14 . 6 925 2 >> 4 [PE1.2] times pop
14 6 . 925 2 >> 4 [PE1.2] times pop
14 6 925 . 2 >> 4 [PE1.2] times pop
14 6 925 2 . >> 4 [PE1.2] times pop
14 6 231 . 4 [PE1.2] times pop
14 6 231 4 . [PE1.2] times pop
14 6 231 4 [PE1.2] . times pop
14 6 231 [PE1.2] . i 3 [PE1.2] times pop
14 6 231 . PE1.2 3 [PE1.2] times pop
14 6 231 . [3 & PE1.1] dupdip 2 >> 3 [PE1.2] times pop
14 6 231 [3 & PE1.1] . dupdip 2 >> 3 [PE1.2] times pop
14 6 231 . 3 & PE1.1 231 2 >> 3 [PE1.2] times pop
14 6 231 3 . & PE1.1 231 2 >> 3 [PE1.2] times pop
14 6 3 . PE1.1 231 2 >> 3 [PE1.2] times pop
14 6 3 . + [+] dupdip 231 2 >> 3 [PE1.2] times pop
14 9 . [+] dupdip 231 2 >> 3 [PE1.2] times pop
14 9 [+] . dupdip 231 2 >> 3 [PE1.2] times pop
14 9 . + 9 231 2 >> 3 [PE1.2] times pop
23 . 9 231 2 >> 3 [PE1.2] times pop
23 9 . 231 2 >> 3 [PE1.2] times pop
23 9 231 . 2 >> 3 [PE1.2] times pop
23 9 231 2 . >> 3 [PE1.2] times pop
23 9 57 . 3 [PE1.2] times pop
23 9 57 3 . [PE1.2] times pop
23 9 57 3 [PE1.2] . times pop
23 9 57 [PE1.2] . i 2 [PE1.2] times pop
23 9 57 . PE1.2 2 [PE1.2] times pop
23 9 57 . [3 & PE1.1] dupdip 2 >> 2 [PE1.2] times pop
23 9 57 [3 & PE1.1] . dupdip 2 >> 2 [PE1.2] times pop
23 9 57 . 3 & PE1.1 57 2 >> 2 [PE1.2] times pop
23 9 57 3 . & PE1.1 57 2 >> 2 [PE1.2] times pop
23 9 1 . PE1.1 57 2 >> 2 [PE1.2] times pop
23 9 1 . + [+] dupdip 57 2 >> 2 [PE1.2] times pop
23 10 . [+] dupdip 57 2 >> 2 [PE1.2] times pop
23 10 [+] . dupdip 57 2 >> 2 [PE1.2] times pop
23 10 . + 10 57 2 >> 2 [PE1.2] times pop
33 . 10 57 2 >> 2 [PE1.2] times pop
33 10 . 57 2 >> 2 [PE1.2] times pop
33 10 57 . 2 >> 2 [PE1.2] times pop
33 10 57 2 . >> 2 [PE1.2] times pop
33 10 14 . 2 [PE1.2] times pop
33 10 14 2 . [PE1.2] times pop
33 10 14 2 [PE1.2] . times pop
33 10 14 [PE1.2] . i 1 [PE1.2] times pop
33 10 14 . PE1.2 1 [PE1.2] times pop
33 10 14 . [3 & PE1.1] dupdip 2 >> 1 [PE1.2] times pop
33 10 14 [3 & PE1.1] . dupdip 2 >> 1 [PE1.2] times pop
33 10 14 . 3 & PE1.1 14 2 >> 1 [PE1.2] times pop
33 10 14 3 . & PE1.1 14 2 >> 1 [PE1.2] times pop
33 10 2 . PE1.1 14 2 >> 1 [PE1.2] times pop
33 10 2 . + [+] dupdip 14 2 >> 1 [PE1.2] times pop
33 12 . [+] dupdip 14 2 >> 1 [PE1.2] times pop
33 12 [+] . dupdip 14 2 >> 1 [PE1.2] times pop
33 12 . + 12 14 2 >> 1 [PE1.2] times pop
45 . 12 14 2 >> 1 [PE1.2] times pop
45 12 . 14 2 >> 1 [PE1.2] times pop
45 12 14 . 2 >> 1 [PE1.2] times pop
45 12 14 2 . >> 1 [PE1.2] times pop
45 12 3 . 1 [PE1.2] times pop
45 12 3 1 . [PE1.2] times pop
45 12 3 1 [PE1.2] . times pop
45 12 3 [PE1.2] . i pop
45 12 3 . PE1.2 pop
45 12 3 . [3 & PE1.1] dupdip 2 >> pop
45 12 3 [3 & PE1.1] . dupdip 2 >> pop
45 12 3 . 3 & PE1.1 3 2 >> pop
45 12 3 3 . & PE1.1 3 2 >> pop
45 12 3 . PE1.1 3 2 >> pop
45 12 3 . + [+] dupdip 3 2 >> pop
45 15 . [+] dupdip 3 2 >> pop
45 15 [+] . dupdip 3 2 >> pop
45 15 . + 15 3 2 >> pop
60 . 15 3 2 >> pop
60 15 . 3 2 >> pop
60 15 3 . 2 >> pop
60 15 3 2 . >> pop
60 15 0 . pop
60 15 .
And so we have at last:
.. code:: ipython2
define('PE1 == 0 0 66 [14811 7 [PE1.2] times pop] times 14811 4 [PE1.2] times popop')
.. code:: ipython2
J('PE1')
.. parsed-literal::
233168
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
.. code:: ipython2
define('PE1.3 == 14811 swap [PE1.2] times pop')
Now we can simplify the definition above:
.. code:: ipython2
define('PE1 == 0 0 66 [7 PE1.3] times 4 PE1.3 pop')
.. code:: ipython2
J('PE1')
.. parsed-literal::
233168
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.
::
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
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.
.. code:: ipython2
define('PE1.terms == [0 swap [dup [pop 14811] [] branch [3 &] dupdip 2 >>] dip rest cons]')
.. code:: ipython2
J('PE1.terms 21 [x] times')
.. parsed-literal::
3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 [0 swap [dup [pop 14811] [] branch [3 &] dupdip 2 >>] dip rest cons]
We know from above that we need sixty-six times seven then four more
terms to reach up to but not over one thousand.
.. code:: ipython2
J('7 66 * 4 +')
.. parsed-literal::
466
Here they are...
~~~~~~~~~~~~~~~~
.. code:: ipython2
J('PE1.terms 466 [x] times pop')
.. parsed-literal::
3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3
...and they do sum to 999.
~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code:: ipython2
J('[PE1.terms 466 [x] times pop] run sum')
.. parsed-literal::
999
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,
leaving just the sum.
.. code:: ipython2
J('0 0 PE1.terms 466 [x [PE1.1] dip] times popop')
.. parsed-literal::
233168
A little further analysis renders iteration unnecessary.
========================================================
Consider finding the sum of the positive integers less than or equal to
ten.
.. code:: ipython2
J('[10 9 8 7 6 5 4 3 2 1] sum')
.. parsed-literal::
55
Instead of summing them,
`observe <https://en.wikipedia.org/wiki/File:Animated_proof_for_the_formula_giving_the_sum_of_the_first_integers_1%2B2%2B...%2Bn.gif>`__:
::
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
(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
define('F == dup ++ * 2 floordiv')
.. code:: ipython2
V('10 F')
.. parsed-literal::
. 10 F
10 . F
10 . dup ++ * 2 floordiv
10 10 . ++ * 2 floordiv
10 11 . * 2 floordiv
110 . 2 floordiv
110 2 . floordiv
55 .
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
each, starting with:
::
[3 5 6 9 10 12 15]
And ending with:
::
[978 980 981 984 985 987 990]
If we reverse one of these two blocks and sum pairs...
.. code:: ipython2
J('[3 5 6 9 10 12 15] reverse [978 980 981 984 985 987 990] zip')
.. parsed-literal::
[[978 15] [980 12] [981 10] [984 9] [985 6] [987 5] [990 3]]
.. code:: ipython2
J('[3 5 6 9 10 12 15] reverse [978 980 981 984 985 987 990] zip [sum] map')
.. parsed-literal::
[993 992 991 993 991 992 993]
(Interesting that the sequence of seven numbers appears again in the
rightmost digit of each term.)
.. code:: ipython2
J('[ 3 5 6 9 10 12 15] reverse [978 980 981 984 985 987 990] zip [sum] map sum')
.. parsed-literal::
6945
Since there are sixty-six blocks and we are pairing them up, there must
be thirty-three pairs, each of which sums to 6945. We also have these
additional unpaired terms between 990 and 1000:
::
993 995 996 999
So we can give the "sum of all the multiples of 3 or 5 below 1000" like
so:
.. code:: ipython2
J('6945 33 * [993 995 996 999] cons sum')
.. parsed-literal::
233168
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.
The sequence of differences will always be a palidrome. Consider an
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
around its midpoint. The same reasoning holds for multiples of more than
two numbers.
The Simplest Program
====================
Of course, the simplest joy program for the first Project Euler problem
is just:
::
PE1 == 233168
Fin.

View File

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

View File

@ -0,0 +1,335 @@
*******************
Thun: Joy in Python
*******************
This implementation is meant as a tool for exploring the programming
model and method of Joy. Python seems like a great implementation
language for Joy for several reasons.
* We can lean on the Python immutable types for our basic semantics and types: ints, floats, strings, and tuples, which enforces functional purity.
* We get garbage collection for free.
* Compilation via Cython.
* Python is a "glue language" with loads of libraries which we can wrap in Joy functions.
`Read-Eval-Print Loop (REPL) <https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop>`__
====================================================================================================
The main way to interact with the Joy interpreter is through a simple
`REPL <https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop>`__
that you start by running the package:
::
$ python3 -m joy
Thun - Copyright © 2017 Simon Forman
This program comes with ABSOLUTELY NO WARRANTY; for details type "warranty".
This is free software, and you are welcome to redistribute it
under certain conditions; type "sharing" for details.
Type "words" to see a list of all words, and "[<name>] help" to print the
docs for a word.
<-top
joy? _
The ``<-top`` marker points to the top of the (initially empty) stack.
You can enter Joy notation at the prompt and a :doc:`trace of evaluation <../pretty>` will
be printed followed by the stack and prompt again::
joy? 23 sqr 18 +
547 <-top
joy?
There is a `trace` combinator::
joy? 23 [sqr 18 +] trace
23 . sqr 18 +
23 . dup mul 18 +
23 23 . mul 18 +
529 . 18 +
529 18 . +
547 .
547 <-top
joy?
The Stack
=============
In Joy, in addition to the types Boolean, integer, float, and string,
there is a :doc:`single sequence type <../stack>` represented by enclosing a sequence of
terms in brackets ``[...]``. This sequence type is used to represent
both the stack and the expression. It is a `cons
list <https://en.wikipedia.org/wiki/Cons#Lists>`__ made from Python
tuples.
Purely Functional Datastructures
=================================
Because Joy stacks are made out of Python tuples they are immutable, as are the other Python types we "borrow" for Joy, so all Joy datastructures are `purely functional <https://en.wikipedia.org/wiki/Purely_functional_data_structure>`__.
The ``joy()`` function
=======================
An Interpreter
~~~~~~~~~~~~~~~~~
The ``joy()`` interpreter function is extrememly simple. It accepts a stack, an
expression, and a dictionary, and it iterates through the expression
putting values onto the stack and delegating execution to functions which it
looks up in the dictionary.
`Continuation-Passing Style <https://en.wikipedia.org/wiki/Continuation-passing_style>`__
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
One day I thought, What happens if you rewrite Joy to use
`CPS <https://en.wikipedia.org/wiki/Continuation-passing_style>`__? I
made all the functions accept and return the expression as well as the
stack and found that all the combinators could be rewritten to work by
modifying the expression rather than making recursive calls to the
``joy()`` function.
View function
~~~~~~~~~~~~~
The ``joy()`` function accepts an optional ``viewer`` argument that
is a 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
over the network to resume later. The stack and expression together
contain all the state of the computation at each step.
The ``TracePrinter``.
~~~~~~~~~~~~~~~~~~~~~
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.
Parser
======
The parser is extremely simple. The undocumented ``re.Scanner`` class
does the tokenizing and then the parser builds the tuple
structure out of the tokens. There's no Abstract Syntax Tree or anything
like that.
Symbols
~~~~~~~~~~~~~
TODO: Symbols are just a string subclass; used by the parser to represent function names and by the interpreter to look up functions in the dictionary. N.B.: Symbols are not looked up at parse-time. You *could* define recursive functions, er, recusively, without ``genrec`` or other recursion combinators ``foo == ... foo ...`` but don't do that.
Token Regular Expressions
~~~~~~~~~~~~~~~~~~~~~~~~~
::
123 1.2 'single quotes' "double quotes" function
TBD (look in the :module: joy.parser module.)
Examples
~~~~~~~~~~~
.. code:: ipython2
joy.parser.text_to_expression('1 2 3 4 5') # A simple sequence.
.. parsed-literal::
(1, (2, (3, (4, (5, ())))))
.. code:: ipython2
joy.parser.text_to_expression('[1 2 3] 4 5') # Three items, the first is a list with three items
.. parsed-literal::
((1, (2, (3, ()))), (4, (5, ())))
.. code:: ipython2
joy.parser.text_to_expression('1 23 ["four" [-5.0] cons] 8888') # A mixed bag. cons is
# a Symbol, no lookup at
# parse-time. Haiku docs.
.. parsed-literal::
(1, (23, (('four', ((-5.0, ()), (cons, ()))), (8888, ()))))
.. code:: ipython2
joy.parser.text_to_expression('[][][][][]') # Five empty lists.
.. parsed-literal::
((), ((), ((), ((), ((), ())))))
.. code:: ipython2
joy.parser.text_to_expression('[[[[[]]]]]') # Five nested lists.
.. parsed-literal::
((((((), ()), ()), ()), ()), ())
Library
=======
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
provide control-flow and higher-order operations.
Many of the functions are defined in Python, like ``dip``:
.. code:: ipython2
print inspect.getsource(joy.library.dip)
.. parsed-literal::
def dip(stack, expression, dictionary):
(quote, (x, stack)) = stack
expression = x, expression
return stack, concat(quote, expression), dictionary
Some functions are defined in equations in terms of other functions.
When the interpreter executes a definition function that function just
pushes its body expression onto the pending expression (the
continuation) and returns control to the interpreter.
.. code:: ipython2
print joy.library.definitions
.. parsed-literal::
second == rest first
third == rest rest first
product == 1 swap [*] step
swons == swap cons
swoncat == swap concat
flatten == [] swap [concat] step
unit == [] cons
quoted == [unit] dip
unquoted == [i] dip
enstacken == stack [clear] dip
disenstacken == ? [uncons ?] loop pop
? == dup truthy
dinfrirst == dip infra first
nullary == [stack] dinfrirst
unary == [stack [pop] dip] dinfrirst
binary == [stack [popop] dip] dinfrirst
ternary == [stack [popop pop] dip] dinfrirst
pam == [i] map
run == [] swap infra
sqr == dup mul
size == 0 swap [pop ++] step
cleave == [i] app2 [popd] dip
average == [sum 1.0 *] [size] cleave /
gcd == 1 [tuck modulus dup 0 >] loop pop
least_fraction == dup [gcd] infra [div] concat map
*fraction == [uncons] dip uncons [swap] dip concat [*] infra [*] dip cons
*fraction0 == concat [[swap] dip * [*] dip] infra
down_to_zero == [0 >] [dup --] while
range_to_zero == unit [down_to_zero] infra
anamorphism == [pop []] swap [dip swons] genrec
range == [0 <=] [1 - dup] anamorphism
while == swap [nullary] cons dup dipd concat loop
dudipd == dup dipd
primrec == [i] genrec
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
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."
~~~~~~~~~~~~~~~~~~~~~~~~~~~
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
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
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.
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"
attack <https://en.wikipedia.org/wiki/Thundering_herd_problem>`__ on
human mentality.
Literary Code Library
~~~~~~~~~~~~~~~~~~~~~
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
skeptical, as I was, but it seems to work: deriving code mathematically
seems to lead to fewer errors.
But my point now is that this great ratio of textual explanation to wind
up with code that consists of a few equations and could fit on an index
card is highly desirable. Less code has fewer errors. The structure of
Joy engenders a kind of thinking that seems to be very effective for
developing structured processes.
There seems to be an elegance and power to the notation.

View File

@ -0,0 +1,257 @@
`Newton's method <https://en.wikipedia.org/wiki/Newton%27s_method>`__
=====================================================================
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
Hughes <https://www.cs.kent.ac.uk/people/staff/dat/miranda/whyfp90.pdf>`__
.. code:: ipython3
from notebook_preamble import J, V, define
A Generator for Approximations
------------------------------
To make a generator that generates successive approximations lets start
by assuming an initial approximation and then derive the function that
computes the next approximation:
::
a F
---------
a'
A Function to Compute the Next Approximation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This is the equation for computing the next approximate value of the
square root:
:math:`a_{i+1} = \frac{(a_i+\frac{n}{a_i})}{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 /
Make it into a Generator
~~~~~~~~~~~~~~~~~~~~~~~~
Our generator would be created by:
::
a [dup F] make_generator
With n as part of the function F, but n is the input to the sqrt
function were writing. If we let 1 be the initial approximation:
::
1 n 1 / + 2 /
1 n/1 + 2 /
1 n + 2 /
n+1 2 /
(n+1)/2
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
.. code:: ipython3
define('gsra 1 swap [over / + 2 /] cons [dup] swoncat make_generator')
.. code:: ipython3
J('23 gsra')
.. parsed-literal::
[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...
.. code:: ipython3
J('23 gsra 6 [x popd] times first sqr')
.. parsed-literal::
23.0000000001585
Finding Consecutive Approximations within a Tolerance
-----------------------------------------------------
From `"Why Functional Programming Matters" by John
Hughes <https://www.cs.kent.ac.uk/people/staff/dat/miranda/whyfp90.pdf>`__:
The remainder of a square root finder is a function *within*, which
takes a tolerance and a list of approximations and looks down the
list for two successive approximations that differ by no more than
the given tolerance.
(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...
::
a [b G] ε within
---------------------- a b - abs ε <=
b
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)<=ε)
.. code:: ipython3
define('_within_P [first - abs] dip <=')
Base-Case
~~~~~~~~~
::
a [b G] ε roll< popop first
[b G] ε a popop first
[b G] first
b
.. code:: ipython3
define('_within_B roll< popop first')
Recur
~~~~~
::
a [b G] ε R0 [within] R1
1. Discard a.
2. Use ``x`` combinator to generate next term from ``G``.
3. Run ``within`` with ``i`` (it is a "tail-recursive" function.)
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
b [c G] ε within
.. code:: ipython3
define('_within_R [popd x] dip')
Setting up
~~~~~~~~~~
The recursive function we have defined so far needs a slight preamble:
``x`` to prime the generator and the epsilon value to use:
::
[a G] x ε ...
a [b G] ε ...
.. code:: ipython3
define('within x 0.000000001 [_within_P] [_within_B] [_within_R] tailrec')
define('sqrt gsra within')
Try it out...
.. code:: ipython3
J('36 sqrt')
.. parsed-literal::
6.0
.. code:: ipython3
J('23 sqrt')
.. parsed-literal::
4.795831523312719
Check it.
.. code:: ipython3
4.795831523312719**2
.. parsed-literal::
22.999999999999996
.. code:: ipython3
from math import sqrt
sqrt(23)
.. parsed-literal::
4.795831523312719

View File

@ -0,0 +1,22 @@
**************
No Updates
**************
DRAFT
1. Joy doesn't need to change.
A. The interpreter doesn't need to change, ``viewer`` function can customize mainloop. Or use a sub-interpreter (Joy in Joy.) The base interpreter remains static.
B. Once a function has been named and defined *never change that name*. It's just not allowed. If you need to change a function ``foo`` you have to call it ``foo_II`` or something. Once a function (name mapped to behavior) is released to the public *that's it*, it's done.
C. The language evolves by adding new definitions and refactoring, always choosing new names for new functions.
2. Following `Semantic Versioning`_ there will never be a version 2.0.
A. `Major version must be incremented if any backwards incompatible changes are introduced to the public API. <https://semver.org/#spec-item-8>`__
B. We never implement any backwards incompatible changes, so...
C. We could see e.g. Thun version 1.273.3!
.. _Semantic Versioning: https://semver.org

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,158 @@
.. code:: ipython2
from notebook_preamble import J, V, define
`Quadratic formula <https://en.wikipedia.org/wiki/Quadratic_formula>`__
=======================================================================
Cf.
`jp-quadratic.html <http://www.kevinalbrecht.com/code/joy-mirror/jp-quadratic.html>`__
::
-b ± sqrt(b^2 - 4 * a * c)
--------------------------------
2 * a
:math:`\frac{-b \pm \sqrt{b^2 - 4ac}}{2a}`
Write a straightforward program with variable names.
----------------------------------------------------
This math translates to Joy code in a straightforward manner. We are
going to use named variables to keep track of the arguments, then write
a definition without them.
``-b``
~~~~~~
::
b neg
``sqrt(b^2 - 4 * a * c)``
~~~~~~~~~~~~~~~~~~~~~~~~~
::
b sqr 4 a c * * - sqrt
``/2a``
~~~~~~~
::
a 2 * /
``±``
~~~~~
There is a function ``pm`` that accepts two values on the stack and
replaces them with their sum and difference.
::
pm == [+] [-] cleave popdd
Putting Them Together
~~~~~~~~~~~~~~~~~~~~~
::
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``.
Derive a definition.
--------------------
Working backwards we use ``dip`` and ``dipd`` to extract the code from
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
The three arguments are to the left, so we can “chop off” everything to
the right and say its the definition of the ``quadratic`` function:
.. code:: ipython2
define('quadratic == over [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2')
Lets try it out:
.. code:: ipython2
J('3 1 1 quadratic')
.. parsed-literal::
-0.3819660112501051 -2.618033988749895
If you look at the Joy evaluation trace you can see that the first few
lines are the ``dip`` and ``dipd`` combinators building the main program
by incorporating the values on the stack. Then that program runs and you
get the results. This is pretty typical of Joy code.
.. code:: ipython2
V('-5 1 4 quadratic')
.. parsed-literal::
. -5 1 4 quadratic
-5 . 1 4 quadratic
-5 1 . 4 quadratic
-5 1 4 . quadratic
-5 1 4 . over [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2
-5 1 4 1 . [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2
-5 1 4 1 [[[neg] dupdip sqr 4] dipd * * - sqrt pm] . dip 2 * [/] cons app2
-5 1 4 . [[neg] dupdip sqr 4] dipd * * - sqrt pm 1 2 * [/] cons app2
-5 1 4 [[neg] dupdip sqr 4] . dipd * * - sqrt pm 1 2 * [/] cons app2
-5 . [neg] dupdip sqr 4 1 4 * * - sqrt pm 1 2 * [/] cons app2
-5 [neg] . dupdip sqr 4 1 4 * * - sqrt pm 1 2 * [/] cons app2
-5 . neg -5 sqr 4 1 4 * * - sqrt pm 1 2 * [/] cons app2
5 . -5 sqr 4 1 4 * * - sqrt pm 1 2 * [/] cons app2
5 -5 . sqr 4 1 4 * * - sqrt pm 1 2 * [/] cons app2
5 -5 . dup mul 4 1 4 * * - sqrt pm 1 2 * [/] cons app2
5 -5 -5 . mul 4 1 4 * * - sqrt pm 1 2 * [/] cons app2
5 25 . 4 1 4 * * - sqrt pm 1 2 * [/] cons app2
5 25 4 . 1 4 * * - sqrt pm 1 2 * [/] cons app2
5 25 4 1 . 4 * * - sqrt pm 1 2 * [/] cons app2
5 25 4 1 4 . * * - sqrt pm 1 2 * [/] cons app2
5 25 4 4 . * - sqrt pm 1 2 * [/] cons app2
5 25 16 . - sqrt pm 1 2 * [/] cons app2
5 9 . sqrt pm 1 2 * [/] cons app2
5 3.0 . pm 1 2 * [/] cons app2
8.0 2.0 . 1 2 * [/] cons app2
8.0 2.0 1 . 2 * [/] cons app2
8.0 2.0 1 2 . * [/] cons app2
8.0 2.0 2 . [/] cons app2
8.0 2.0 2 [/] . cons app2
8.0 2.0 [2 /] . app2
[8.0] [2 /] . infra first [2.0] [2 /] infra first
8.0 . 2 / [] swaack first [2.0] [2 /] infra first
8.0 2 . / [] swaack first [2.0] [2 /] infra first
4.0 . [] swaack first [2.0] [2 /] infra first
4.0 [] . swaack first [2.0] [2 /] infra first
[4.0] . first [2.0] [2 /] infra first
4.0 . [2.0] [2 /] infra first
4.0 [2.0] . [2 /] infra first
4.0 [2.0] [2 /] . infra first
2.0 . 2 / [4.0] swaack first
2.0 2 . / [4.0] swaack first
1.0 . [4.0] swaack first
1.0 [4.0] . swaack first
4.0 [1.0] . first
4.0 1.0 .

View File

@ -0,0 +1,690 @@
.. code:: ipython2
from notebook_preamble import D, DefinitionWrapper, J, V, define
Recursion Combinators
=====================
This article describes the ``genrec`` combinator, how to use it, and
several generic specializations.
::
[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:
“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
of the whole function.
For example, given a (general recursive) function ``F``:
::
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
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.
Primitive Recursive Functions
-----------------------------
Primitive recursive functions are those where ``R2 == i``.
::
P == [I] [T] [R] primrec
== [I] [T] [R [P] i] ifte
== [I] [T] [R P] ifte
`Hylomorphism <https://en.wikipedia.org/wiki/Hylomorphism_%28computer_science%29>`__
------------------------------------------------------------------------------------
A
`hylomorphism <https://en.wikipedia.org/wiki/Hylomorphism_%28computer_science%29>`__
is a recursive function ``H :: A -> C`` that converts a value of type
``A`` into a value of type ``C`` by means of:
- A generator ``G :: A -> (B, A)``
- 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”.
It may be helpful to see this function implemented in imperative Python
code.
.. code:: ipython2
def hylomorphism(c, F, P, G):
'''Return a hylomorphism function H.'''
def H(a):
if P(a):
result = c
else:
b, aa = G(a)
result = F(b, H(aa)) # b is stored in the stack frame during recursive call to H().
return result
return H
Cf. `“Bananas, Lenses, & Barbed
Wire” <http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.41.125>`__
Note that during evaluation of ``H()`` the intermediate ``b`` values are
stored in the Python call stack. This is what is meant by “call stack in
the form of a cons list”.
Hylomorphism in Joy
-------------------
We can define a combinator ``hylomorphism`` that will make a
hylomorphism combinator ``H`` from constituent parts.
::
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
``H``. (The then-part just discards the leftover ``a`` and replaces it
with the base case value ``c``.)
::
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.
This gives us a definition for ``J``.
::
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
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
Derivation of ``hylomorphism`` combinator
-----------------------------------------
Now we just need to derive a definition that builds the ``genrec``
arguments out of the pieces given to the ``hylomorphism`` combinator.
::
[P] c [G] [F] hylomorphism
------------------------------------------
[P] [pop c] [G] [dip F] genrec
Working in reverse:
- Use ``swoncat`` twice to decouple ``[c]`` and ``[F]``.
- Use ``unit`` to dequote ``c``.
- Use ``dipd`` to untangle ``[unit [pop] swoncat]`` from the givens.
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
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
.. code:: ipython2
define('hylomorphism == [unit [pop] swoncat] dipd [dip] swoncat genrec')
Example: Finding `Triangular Numbers <https://en.wikipedia.org/wiki/Triangular_number>`__
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Lets write a function that, given a positive integer, returns the sum
of all positive integers less than that one. (In this case the types
``A``, ``B`` and ``C`` are all ``int``.)
To sum a range of integers from 0 to *n* - 1:
- ``[P]`` is ``[1 <=]``
- ``c`` is ``0``
- ``[G]`` is ``[-- dup]``
- ``[F]`` is ``[+]``
.. code:: ipython2
define('triangular_number == [1 <=] 0 [-- dup] [+] hylomorphism')
Lets try it:
.. code:: ipython2
J('5 triangular_number')
.. parsed-literal::
10
.. code:: ipython2
J('[0 1 2 3 4 5 6] [triangular_number] map')
.. parsed-literal::
[0 0 1 3 6 10 15]
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
needs to operate on the current value of the datastructure or the
generators output, in other words, whether ``F`` or ``G`` should run
first in the recursive branch.
::
H1 == [P] [pop c] [G ] [dip F] genrec
H2 == c swap [P] [pop] [G [F] dip ] [i] genrec
H3 == [P] [pop c] [ [G] dupdip ] [dip F] genrec
H4 == c swap [P] [pop] [ [F] dupdip G] [i] genrec
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
... c a G [F] dip H2 a G == b a
... a [G] dupdip [H3] dip F a G == a
... c a [F] dupdip G H4 a G == a
The following four sections illustrate how these work, omitting the
predicate evaluation.
``H1``
~~~~~~
::
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″
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
combiner ``F``.
``H2``
~~~~~~
When you can start with the identity value ``c`` on the stack and the
combiner ``F`` can operate as you go using the intermediate results
immediately rather than queuing them up, use this form. An important
difference is that the generator function must return its results in the
reverse order.
::
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″
``H3``
~~~~~~
If you examine the traces above youll see that the combiner ``F`` only
gets to operate on the results of ``G``, it never “sees” the first value
``a``. If the combiner and the generator both need to work on the
current value then ``dup`` must be used, and the generator must produce
one item instead of two (the b is instead the duplicate of a.)
::
H3 == [P] [pop c] [[G] dupdip] [dip F] genrec
... 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``
~~~~~~
And, last but not least, if you can combine as you go, starting with
``c``, and the combiner ``F`` needs to work on the current item, this is
the form:
::
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″
Anamorphism
-----------
An anamorphism can be defined as a hylomorphism that uses ``[]`` for
``c`` and ``swons`` for ``F``. An anamorphic function builds a list of
values.
::
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*.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Each of the above variations can be used to make four slightly different
``range`` functions.
``range`` with ``H1``
^^^^^^^^^^^^^^^^^^^^^
::
H1 == [P] [pop c] [G] [dip F] genrec
== [0 <=] [pop []] [-- dup] [dip swons] genrec
.. code:: ipython2
define('range == [0 <=] [] [-- dup] [swons] hylomorphism')
.. code:: ipython2
J('5 range')
.. parsed-literal::
[4 3 2 1 0]
``range`` with ``H2``
^^^^^^^^^^^^^^^^^^^^^
::
H2 == c swap [P] [pop] [G [F] dip] primrec
== [] swap [0 <=] [pop] [-- dup [swons] dip] primrec
.. code:: ipython2
define('range_reverse == [] swap [0 <=] [pop] [-- dup [swons] dip] primrec')
.. code:: ipython2
J('5 range_reverse')
.. parsed-literal::
[0 1 2 3 4]
``range`` with ``H3``
^^^^^^^^^^^^^^^^^^^^^
::
H3 == [P] [pop c] [[G] dupdip] [dip F] genrec
== [0 <=] [pop []] [[--] dupdip] [dip swons] genrec
.. code:: ipython2
define('ranger == [0 <=] [pop []] [[--] dupdip] [dip swons] genrec')
.. code:: ipython2
J('5 ranger')
.. parsed-literal::
[5 4 3 2 1]
``range`` with ``H4``
^^^^^^^^^^^^^^^^^^^^^
::
H4 == c swap [P] [pop] [[F] dupdip G ] primrec
== [] swap [0 <=] [pop] [[swons] dupdip --] primrec
.. code:: ipython2
define('ranger_reverse == [] swap [0 <=] [pop] [[swons] dupdip --] primrec')
.. code:: ipython2
J('5 ranger_reverse')
.. parsed-literal::
[1 2 3 4 5]
Hopefully this illustrates the workings of the variations. For more
insight you can run the cells using the ``V()`` function instead of the
``J()`` function to get a trace of the Joy evaluation.
Catamorphism
------------
A catamorphism can be defined as a hylomorphism that uses
``[uncons swap]`` for ``[G]`` and ``[[] =]`` (or just ``[not]``) for the
predicate ``[P]``. A catamorphic function tears down a list term-by-term
and makes some new value.
::
C == [not] c [uncons swap] [F] hylomorphism
.. code:: ipython2
define('swuncons == uncons swap') # Awkward name.
An example of a catamorphism is the sum function.
::
sum == [not] 0 [swuncons] [+] hylomorphism
.. code:: ipython2
define('sum == [not] 0 [swuncons] [+] hylomorphism')
.. code:: ipython2
J('[5 4 3 2 1] sum')
.. parsed-literal::
15
The ``step`` combinator
~~~~~~~~~~~~~~~~~~~~~~~
The ``step`` combinator will usually be better to use than
``catamorphism``.
.. code:: ipython2
J('[step] help')
.. parsed-literal::
Run a quoted program on each item in a sequence.
::
... [] [Q] . step
-----------------------
... .
... [a] [Q] . step
------------------------
... a . Q
... [a b c] [Q] . step
----------------------------------------
... a . Q [b c] [Q] step
The step combinator executes the quotation on each member of the list
on top of the stack.
.. code:: ipython2
define('sum == 0 swap [+] step')
.. code:: ipython2
J('[5 4 3 2 1] sum')
.. parsed-literal::
15
Example: Factorial Function
---------------------------
For the Factorial function:
::
H4 == c swap [P] [pop] [[F] dupdip G] primrec
With:
::
c == 1
F == *
G == --
P == 1 <=
.. code:: ipython2
define('factorial == 1 swap [1 <=] [pop] [[*] dupdip --] primrec')
.. code:: ipython2
J('5 factorial')
.. parsed-literal::
120
Example: ``tails``
------------------
An example of a paramorphism for lists given in the `“Bananas…”
paper <http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.41.125>`__
is ``tails`` which returns the list of “tails” of a list.
::
[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
We would use:
::
c == []
F == swons
G == rest dup
P == not
.. code:: ipython2
define('tails == [] swap [not] [pop] [rest dup [swons] dip] primrec')
.. code:: ipython2
J('[1 2 3] tails')
.. parsed-literal::
[[] [3] [2 3]]
Conclusion: Patterns of Recursion
---------------------------------
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
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
Appendix: Fun with Symbols
--------------------------
::
|[ (c, F), (G, P) ]| == (|c, F|) • [(G, P)]
`“Bananas, Lenses, & Barbed
Wire” <http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.41.125>`__
::
(|...|) [(...)] [<...>]
I think they are having slightly too much fun with the symbols. However,
“Too much is always better than not enough.”

View File

@ -0,0 +1,147 @@
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, theres no function that does that. Adding a new
function to the dictionary is a meta-interpreter action, you have to do
it in Python, not Joy.
.. code:: ipython2
from notebook_preamble import D, J, V
A long trace
------------
.. code:: ipython2
V('[23 18] average')
.. parsed-literal::
. [23 18] average
[23 18] . average
[23 18] . [sum 1.0 *] [size] cleave /
[23 18] [sum 1.0 *] . [size] cleave /
[23 18] [sum 1.0 *] [size] . cleave /
[23 18] [sum 1.0 *] [size] . [i] app2 [popd] dip /
[23 18] [sum 1.0 *] [size] [i] . app2 [popd] dip /
[23 18] [[sum 1.0 *] [23 18]] [i] . infra first [[size] [23 18]] [i] infra first [popd] dip /
[23 18] [sum 1.0 *] . i [[23 18]] swaack first [[size] [23 18]] [i] infra first [popd] dip /
[23 18] . sum 1.0 * [[23 18]] swaack first [[size] [23 18]] [i] infra first [popd] dip /
41 . 1.0 * [[23 18]] swaack first [[size] [23 18]] [i] infra first [popd] dip /
41 1.0 . * [[23 18]] swaack first [[size] [23 18]] [i] infra first [popd] dip /
41.0 . [[23 18]] swaack first [[size] [23 18]] [i] infra first [popd] dip /
41.0 [[23 18]] . swaack first [[size] [23 18]] [i] infra first [popd] dip /
[23 18] [41.0] . first [[size] [23 18]] [i] infra first [popd] dip /
[23 18] 41.0 . [[size] [23 18]] [i] infra first [popd] dip /
[23 18] 41.0 [[size] [23 18]] . [i] infra first [popd] dip /
[23 18] 41.0 [[size] [23 18]] [i] . infra first [popd] dip /
[23 18] [size] . i [41.0 [23 18]] swaack first [popd] dip /
[23 18] . size [41.0 [23 18]] swaack first [popd] dip /
[23 18] . 0 swap [pop ++] step [41.0 [23 18]] swaack first [popd] dip /
[23 18] 0 . swap [pop ++] step [41.0 [23 18]] swaack first [popd] dip /
0 [23 18] . [pop ++] step [41.0 [23 18]] swaack first [popd] dip /
0 [23 18] [pop ++] . step [41.0 [23 18]] swaack first [popd] dip /
0 23 [pop ++] . i [18] [pop ++] step [41.0 [23 18]] swaack first [popd] dip /
0 23 . pop ++ [18] [pop ++] step [41.0 [23 18]] swaack first [popd] dip /
0 . ++ [18] [pop ++] step [41.0 [23 18]] swaack first [popd] dip /
1 . [18] [pop ++] step [41.0 [23 18]] swaack first [popd] dip /
1 [18] . [pop ++] step [41.0 [23 18]] swaack first [popd] dip /
1 [18] [pop ++] . step [41.0 [23 18]] swaack first [popd] dip /
1 18 [pop ++] . i [41.0 [23 18]] swaack first [popd] dip /
1 18 . pop ++ [41.0 [23 18]] swaack first [popd] dip /
1 . ++ [41.0 [23 18]] swaack first [popd] dip /
2 . [41.0 [23 18]] swaack first [popd] dip /
2 [41.0 [23 18]] . swaack first [popd] dip /
[23 18] 41.0 [2] . first [popd] dip /
[23 18] 41.0 2 . [popd] dip /
[23 18] 41.0 2 [popd] . dip /
[23 18] 41.0 . popd 2 /
41.0 . 2 /
41.0 2 . /
20.5 .
Replacing ``size`` with a Python version
----------------------------------------
Both ``sum`` and ``size`` each convert a sequence to a single value.
::
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
up evaluation and make the trace more readable.
.. code:: ipython2
from joy.library import SimpleFunctionWrapper
from joy.utils.stack import iter_stack
@SimpleFunctionWrapper
def size(stack):
'''Return the size of the sequence on the stack.'''
sequence, stack = stack
n = 0
for _ in iter_stack(sequence):
n += 1
return n, stack
Now we replace the old version in the dictionary with the new version,
and re-evaluate the expression.
.. code:: ipython2
D['size'] = size
A shorter trace
---------------
You can see that ``size`` now executes in a single step.
.. code:: ipython2
V('[23 18] average')
.. parsed-literal::
. [23 18] average
[23 18] . average
[23 18] . [sum 1.0 *] [size] cleave /
[23 18] [sum 1.0 *] . [size] cleave /
[23 18] [sum 1.0 *] [size] . cleave /
[23 18] [sum 1.0 *] [size] . [i] app2 [popd] dip /
[23 18] [sum 1.0 *] [size] [i] . app2 [popd] dip /
[23 18] [[sum 1.0 *] [23 18]] [i] . infra first [[size] [23 18]] [i] infra first [popd] dip /
[23 18] [sum 1.0 *] . i [[23 18]] swaack first [[size] [23 18]] [i] infra first [popd] dip /
[23 18] . sum 1.0 * [[23 18]] swaack first [[size] [23 18]] [i] infra first [popd] dip /
41 . 1.0 * [[23 18]] swaack first [[size] [23 18]] [i] infra first [popd] dip /
41 1.0 . * [[23 18]] swaack first [[size] [23 18]] [i] infra first [popd] dip /
41.0 . [[23 18]] swaack first [[size] [23 18]] [i] infra first [popd] dip /
41.0 [[23 18]] . swaack first [[size] [23 18]] [i] infra first [popd] dip /
[23 18] [41.0] . first [[size] [23 18]] [i] infra first [popd] dip /
[23 18] 41.0 . [[size] [23 18]] [i] infra first [popd] dip /
[23 18] 41.0 [[size] [23 18]] . [i] infra first [popd] dip /
[23 18] 41.0 [[size] [23 18]] [i] . infra first [popd] dip /
[23 18] [size] . i [41.0 [23 18]] swaack first [popd] dip /
[23 18] . size [41.0 [23 18]] swaack first [popd] dip /
2 . [41.0 [23 18]] swaack first [popd] dip /
2 [41.0 [23 18]] . swaack first [popd] dip /
[23 18] 41.0 [2] . first [popd] dip /
[23 18] 41.0 2 . [popd] dip /
[23 18] 41.0 2 [popd] . dip /
[23 18] 41.0 . popd 2 /
41.0 . 2 /
41.0 2 . /
20.5 .

View File

@ -0,0 +1,336 @@
The Four Fundamental Operations of Definite Action
==================================================
All definite actions (computer program) can be defined by four
fundamental patterns of combination:
1. Sequence
2. Branch
3. Loop
4. Parallel
Sequence
--------
Do one thing after another. In joy this is represented by putting two
symbols together, juxtaposition:
::
foo bar
Operations have inputs and outputs. The outputs of ``foo`` must be
compatible in “arity”, type, and shape with the inputs of ``bar``.
Branch
------
Do one thing or another.
::
boolean [F] [T] branch
t [F] [T] branch
----------------------
T
f [F] [T] branch
----------------------
F
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
Given some branch function ``G``:
::
G == [F] [T] branch
Used in a sequence like so:
::
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 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”):
::
[P] [T] [E] ifte
Defined in terms of ``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 doesnt *have*
to be Boolean because Pythons rules for “truthiness” will be used to
evaluate it. I reflect this in the structure of the stack effect comment
of ``branch``, it will only accept Boolean values, and in the definition
of ``ifte`` above by including ``not`` in the quote, which also has the
effect that the subject quotes are in the proper order for ``branch``.)
Loop
----
Do one thing zero or more times.
::
boolean [Q] loop
t [Q] loop
----------------
Q [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
equivalent to conventional loops. (There is, however, the ``x``
combinator, defined as ``x == dup i``, which permits recursive
constructs that do not need to be directly self-referential, unlike
``loop`` and ``genrec``.)
::
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
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
Because ``loop`` expects and consumes a Boolean value, the ``Q``
function must be compatible with the previous stack *and itself* with a
boolean flag for the next iteration:
::
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
``while``
~~~~~~~~~
Keep doing ``B`` *while* some predicate ``P`` is true. This is
convenient as the predicate function is made nullary automatically and
the body function can be designed without regard to leaving a Boolean
flag for the next iteration:
::
[P] [B] while
--------------------------------------
[P] nullary [B [P] nullary] 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
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.
The current implementaions and the following definitions *are not
actually parallel* (yet), but there is no reason they couldnt be
reimplemented in terms of e.g. Python threads. I am not concerned with
performance of the system just yet, only the elegance of the code it
allows us to write.
``cleave``
~~~~~~~~~~
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
The ``cleave`` combinator expects a value and two quotes and it executes
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
upward-pointed triangle, that takes two functions ``A :: x -> a`` and
``B :: x -> b`` and returns a function ``F :: x -> (a, b)``, in Conal
Elliotts “Compiling to Categories” paper, et. al.)
Just a thought, if you ``cleave`` two jobs and one requires more time to
finish than the other youd like to be able to assign resources
accordingly so that they both finish at the same time.
“Apply” Functions
~~~~~~~~~~~~~~~~~
There are also ``app2`` and ``app3`` which run a single quote on more
than one value:
::
... 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
Because the quoted program can be ``i`` we can define ``cleave`` in
terms of ``app2``:
::
cleave == [i] app2 [popd] dip
(Im not sure why ``cleave`` was specified to take that value, I may
make a combinator that does the same thing but without expecting a
value.)
::
clv == [i] app2
[A] [B] clv
------------------
a b
``map``
~~~~~~~
The common ``map`` function in Joy should also be though of as a
*parallel* operator:
::
[a b c ...] [Q] map
There is no reason why the implementation of ``map`` couldnt distribute
the ``Q`` function over e.g. a pool of worker CPUs.
``pam``
~~~~~~~
One of my favorite combinators, the ``pam`` combinator is just:
::
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 ...]
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.
first-to-finish
^^^^^^^^^^^^^^^
Thinking about variations of ``pam`` there could be one that only
returns the first result of the first-to-finish sub-program, or the
stack could be replaced by its output stack.
The other sub-programs would be cancelled.
“Fulminators”
^^^^^^^^^^^^^
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
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
the values of the previous thunks.
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?
In any case, until I can find a rigorous theory that shows that this
sort of thing works perfectly in Joy code Im not going to worry about
it. (And I think the Categories can deal with it anyhow? Incremental
evaluation, yeah?)

View File

@ -0,0 +1,620 @@
Treating Trees II: ``treestep``
===============================
Lets consider a tree structure, similar to one described `“Why
functional programming matters” by John
Hughes <https://www.cs.kent.ac.uk/people/staff/dat/miranda/whyfp90.pdf>`__,
that consists of a node value followed by zero or more child trees. (The
asterisk is meant to indicate the `Kleene
star <https://en.wikipedia.org/wiki/Kleene_star>`__.)
::
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
base-case function ``[B]``, and two quoted programs ``[N]`` and ``[C]``.
::
tree [B] [N] [C] treestep
If the current tree node is empty then just execute ``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
combine the result with ``C``.
::
[node tree*] [B] [N] [C] treestep
--------------------------------------- w/ K == [B] [N] [C] treestep
node N [tree*] [K] map C
(Later on well experiment with making ``map`` part of ``C`` so you can
use other combinators.)
Derive the recursive function.
------------------------------
We can begin to derive it by finding the ``ifte`` stage that ``genrec``
will produce.
::
K == [not] [B] [R0] [R1] genrec
== [not] [B] [R0 [K] R1] ifte
So we just have to derive ``J``:
::
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
So ``J`` will have some form like:
::
J == ... [N] ... [K] ... [C] ...
Lets dive in. First, unquote the node and ``dip`` ``N``.
::
[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
So:
::
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
Extract the givens to parameterize the program.
-----------------------------------------------
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
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Extract a couple of auxiliary definitions:
::
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
The givens are all to the left so we have our definition.
(alternate) Extract the givens to parameterize the program.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Define ``treestep``
-------------------
.. code:: ipython2
from notebook_preamble import D, J, V, define, DefinitionWrapper
.. code:: ipython2
DefinitionWrapper.add_definitions('''
_treestep_0 == [[not] swap] dip
_treestep_1 == [dip] cons [uncons] swoncat
treegrind == [_treestep_1 _treestep_0] dip genrec
treestep == [map] swoncat treegrind
''', D)
Examples
--------
Consider trees, the nodes of which are integers. We can find the sum of
all nodes in a tree with this function:
::
sumtree == [pop 0] [] [sum +] treestep
.. code:: ipython2
define('sumtree == [pop 0] [] [sum +] treestep')
Running this function on an empty tree value gives zero:
::
[] [pop 0] [] [sum +] treestep
------------------------------------
0
.. code:: ipython2
J('[] sumtree') # Empty tree.
.. parsed-literal::
0
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
.. code:: ipython2
J('[23] sumtree') # No child trees.
.. parsed-literal::
23
.. code:: ipython2
J('[23 []] sumtree') # Child tree, empty.
.. parsed-literal::
23
.. code:: ipython2
J('[23 [2 [4]] [3]] sumtree') # Non-empty child trees.
.. parsed-literal::
32
.. code:: ipython2
J('[23 [2 [8] [9]] [3] [4 []]] sumtree') # Etc...
.. parsed-literal::
49
.. code:: ipython2
J('[23 [2 [8] [9]] [3] [4 []]] [pop 0] [] [cons sum] treestep') # Alternate "spelling".
.. parsed-literal::
49
.. code:: ipython2
J('[23 [2 [8] [9]] [3] [4 []]] [] [pop 23] [cons] treestep') # Replace each node.
.. parsed-literal::
[23 [23 [23] [23]] [23] [23 []]]
.. code:: ipython2
J('[23 [2 [8] [9]] [3] [4 []]] [] [pop 1] [cons] treestep')
.. parsed-literal::
[1 [1 [1] [1]] [1] [1 []]]
.. code:: ipython2
J('[23 [2 [8] [9]] [3] [4 []]] [] [pop 1] [cons] treestep sumtree')
.. parsed-literal::
6
.. code:: ipython2
J('[23 [2 [8] [9]] [3] [4 []]] [pop 0] [pop 1] [sum +] treestep') # Combine replace and sum into one function.
.. parsed-literal::
6
.. code:: ipython2
J('[4 [3 [] [7]]] [pop 0] [pop 1] [sum +] treestep') # Combine replace and sum into one function.
.. parsed-literal::
3
Redefining the Ordered Binary Tree in terms of ``treestep``.
------------------------------------------------------------
::
Tree = [] | [[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
Plugging in our BTree structure:
::
[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
This doesnt quite work:
.. code:: ipython2
J('[[3 0] [[2 0] [][]] [[9 0] [[5 0] [[4 0] [][]] [[8 0] [[6 0] [] [[7 0] [][]]][]]][]]] ["B"] [first] [i] treestep')
.. parsed-literal::
3 'B' 'B'
Doesnt work because ``map`` extracts the ``first`` item of whatever its
mapped function produces. We have to return a list, rather than
depositing our results directly on the stack.
::
[key value] N [left right] [K] map C
[key value] 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
.. code:: ipython2
J('[[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]] [] [first] [flatten cons] treestep')
.. parsed-literal::
[3 2 9 5 4 8 6 7]
There we go.
In-order traversal
~~~~~~~~~~~~~~~~~~
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]
So:
::
[] [i roll< swons concat] [first] treestep
.. code:: ipython2
J('[[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]] [] [uncons pop] [i roll< swons concat] treestep')
.. parsed-literal::
[2 3 4 5 6 7 8 9]
With ``treegrind``?
-------------------
The ``treegrind`` function doesnt include the ``map`` combinator, so
the ``[C]`` function must arrange to use some combinator on the quoted
recursive copy ``[K]``. With this function, the pattern for processing a
non-empty node is:
::
node N [tree*] [K] C
Plugging in our BTree structure:
::
[key value] N [left right] [K] C
.. code:: ipython2
J('[["key" "value"] ["left"] ["right"] ] ["B"] ["N"] ["C"] treegrind')
.. parsed-literal::
['key' 'value'] 'N' [['left'] ['right']] [[not] ['B'] [uncons ['N'] dip] ['C'] genrec] 'C'
``treegrind`` with ``step``
---------------------------
Iteration through the nodes
.. code:: ipython2
J('[[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]] [pop] ["N"] [step] treegrind')
.. parsed-literal::
[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.
.. code:: ipython2
J('0 [[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]] [pop] [first +] [step] treegrind')
.. parsed-literal::
44
Rebuild the tree using ``map`` (imitating ``treestep``.)
.. code:: ipython2
J('[[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]] [] [[100 +] infra] [map cons] treegrind')
.. parsed-literal::
[[103 0] [[102 0] [] []] [[109 0] [[105 0] [[104 0] [] []] [[108 0] [[106 0] [] [[107 0] [] []]] []]] []]]
Do we have the flexibility to reimplement ``Tree-get``?
-------------------------------------------------------
I think we do:
::
[B] [N] [C] treegrind
Well start by saying that the base-case (the key is not in the tree) is
user defined, and the per-node function is just the query key literal:
::
[B] [query_key] [C] treegrind
This means we just have to define ``C`` from:
::
[key value] query_key [left right] [K] C
Lets try ``cmp``:
::
C == P [T>] [E] [T<] cmp
[key value] query_key [left right] [K] P [T>] [E] [T<] cmp
The predicate ``P``
~~~~~~~~~~~~~~~~~~~
Seems pretty easy (we must preserve the value in case the keys are
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] [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
(Possibly with a swap at the end? Or just swap ``T<`` and ``T>``.)
So now:
::
[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<
``E``
~~~~~
Easy.
::
E == roll> popop first
``T<`` and ``T>``
~~~~~~~~~~~~~~~~~
::
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
Tree-get == [P [T>] [E] [T<] cmp] treegrind
To me, that seems simpler than the ``genrec`` version.
.. code:: ipython2
DefinitionWrapper.add_definitions('''
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
''', D)
.. code:: ipython2
J('''\
[[3 13] [[2 12] [] []] [[9 19] [[5 15] [[4 14] [] []] [[8 18] [[6 16] [] [[7 17] [] []]] []]] []]]
[] [5] Tree-get
''')
.. parsed-literal::
15
.. code:: ipython2
J('''\
[[3 13] [[2 12] [] []] [[9 19] [[5 15] [[4 14] [] []] [[8 18] [[6 16] [] [[7 17] [] []]] []]] []]]
[pop "nope"] [25] Tree-get
''')
.. parsed-literal::
'nope'

View File

@ -0,0 +1,171 @@
Type Checking
=============
.. code:: ipython2
import logging, sys
logging.basicConfig(
format='%(message)s',
stream=sys.stdout,
level=logging.INFO,
)
.. code:: ipython2
from joy.utils.types import (
doc_from_stack_effect,
infer,
reify,
unify,
FUNCTIONS,
JoyTypeError,
)
.. code:: ipython2
D = FUNCTIONS.copy()
del D['product']
globals().update(D)
An Example
----------
.. code:: ipython2
fi, fo = infer(pop, swap, rolldown, rrest, ccons)[0]
.. parsed-literal::
25 (--) ∘ pop swap rolldown rrest ccons
28 (a1 --) ∘ swap rolldown rrest ccons
31 (a3 a2 a1 -- a2 a3) ∘ rolldown rrest ccons
34 (a4 a3 a2 a1 -- a2 a3 a4) ∘ rrest ccons
37 ([a4 a5 ...1] a3 a2 a1 -- a2 a3 [...1]) ∘ ccons
40 ([a4 a5 ...1] a3 a2 a1 -- [a2 a3 ...1]) ∘
.. code:: ipython2
print doc_from_stack_effect(fi, fo)
.. parsed-literal::
([a4 a5 ...1] a3 a2 a1 -- [a2 a3 ...1])
.. code:: ipython2
from joy.parser import text_to_expression
from joy.utils.stack import stack_to_string
.. code:: ipython2
e = text_to_expression('0 1 2 [3 4]') # reverse order
print stack_to_string(e)
.. parsed-literal::
[3 4] 2 1 0
.. code:: ipython2
u = unify(e, fi)[0]
u
.. parsed-literal::
{a1: 0, a2: 1, a3: 2, a4: 3, a5: 4, s2: (), s1: ()}
.. code:: ipython2
g = reify(u, (fi, fo))
print doc_from_stack_effect(*g)
.. parsed-literal::
(... [3 4 ] 2 1 0 -- ... [1 2 ])
Unification Works “in Reverse”
------------------------------
.. code:: ipython2
e = text_to_expression('[2 3]')
.. code:: ipython2
u = unify(e, fo)[0] # output side, not input side
u
.. parsed-literal::
{a2: 2, a3: 3, s2: (), s1: ()}
.. code:: ipython2
g = reify(u, (fi, fo))
print doc_from_stack_effect(*g)
.. parsed-literal::
(... [a4 a5 ] 3 2 a1 -- ... [2 3 ])
Failing a Check
---------------
.. code:: ipython2
fi, fo = infer(dup, mul)[0]
.. parsed-literal::
25 (--) ∘ dup mul
28 (a1 -- a1 a1) ∘ mul
31 (f1 -- f2) ∘
31 (i1 -- i2) ∘
.. code:: ipython2
e = text_to_expression('"two"')
print stack_to_string(e)
.. parsed-literal::
'two'
.. code:: ipython2
try:
unify(e, fi)
except JoyTypeError, err:
print err
.. parsed-literal::
Cannot unify 'two' and f1.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,352 @@
Traversing Datastructures with Zippers
======================================
This notebook is about using the “zipper” with joy datastructures. See
the `Zipper wikipedia
entry <https://en.wikipedia.org/wiki/Zipper_%28data_structure%29>`__ or
the original paper: `“FUNCTIONAL PEARL The Zipper” by Gérard
Huet <https://www.st.cs.uni-saarland.de/edu/seminare/2005/advanced-fp/docs/huet-zipper.pdf>`__
Given a datastructure on the stack we can navigate through it, modify
it, and rebuild it using the “zipper” technique.
.. code:: ipython2
from notebook_preamble import J, V, define
Trees
-----
In Joypy there arent any complex datastructures, just ints, floats,
strings, Symbols (strings that are names of functions) and sequences
(aka lists, aka quoted literals, aka aggregates, etc…), but we can build
`trees <https://en.wikipedia.org/wiki/Tree_%28data_structure%29>`__ out
of sequences.
.. code:: ipython2
J('[1 [2 [3 4 25 6] 7] 8]')
.. parsed-literal::
[1 [2 [3 4 25 6] 7] 8]
Zipper in Joy
-------------
Zippers work by keeping track of the current item, the already-seen
items, and the yet-to-be seen items as you traverse a datastructure (the
datastructure used to keep track of these items is the zipper.)
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
Lets use them to change 25 into 625. The first time a word is used I
show the trace so you can see how it works. If we were going to use
these a lot it would make sense to write Python versions for efficiency,
but see below.
.. code:: ipython2
define('z-down == [] swap uncons swap')
define('z-up == swons swap shunt')
define('z-right == [swons] cons dip uncons swap')
define('z-left == swons [uncons swap] dip swap')
.. code:: ipython2
V('[1 [2 [3 4 25 6] 7] 8] z-down')
.. parsed-literal::
. [1 [2 [3 4 25 6] 7] 8] z-down
[1 [2 [3 4 25 6] 7] 8] . z-down
[1 [2 [3 4 25 6] 7] 8] . [] swap uncons swap
[1 [2 [3 4 25 6] 7] 8] [] . swap uncons swap
[] [1 [2 [3 4 25 6] 7] 8] . uncons swap
[] 1 [[2 [3 4 25 6] 7] 8] . swap
[] [[2 [3 4 25 6] 7] 8] 1 .
.. code:: ipython2
V('[] [[2 [3 4 25 6] 7] 8] 1 z-right')
.. parsed-literal::
. [] [[2 [3 4 25 6] 7] 8] 1 z-right
[] . [[2 [3 4 25 6] 7] 8] 1 z-right
[] [[2 [3 4 25 6] 7] 8] . 1 z-right
[] [[2 [3 4 25 6] 7] 8] 1 . z-right
[] [[2 [3 4 25 6] 7] 8] 1 . [swons] cons dip uncons swap
[] [[2 [3 4 25 6] 7] 8] 1 [swons] . cons dip uncons swap
[] [[2 [3 4 25 6] 7] 8] [1 swons] . dip uncons swap
[] . 1 swons [[2 [3 4 25 6] 7] 8] uncons swap
[] 1 . swons [[2 [3 4 25 6] 7] 8] uncons swap
[] 1 . swap cons [[2 [3 4 25 6] 7] 8] uncons swap
1 [] . cons [[2 [3 4 25 6] 7] 8] uncons swap
[1] . [[2 [3 4 25 6] 7] 8] uncons swap
[1] [[2 [3 4 25 6] 7] 8] . uncons swap
[1] [2 [3 4 25 6] 7] [8] . swap
[1] [8] [2 [3 4 25 6] 7] .
.. code:: ipython2
J('[1] [8] [2 [3 4 25 6] 7] z-down')
.. parsed-literal::
[1] [8] [] [[3 4 25 6] 7] 2
.. code:: ipython2
J('[1] [8] [] [[3 4 25 6] 7] 2 z-right')
.. parsed-literal::
[1] [8] [2] [7] [3 4 25 6]
.. code:: ipython2
J('[1] [8] [2] [7] [3 4 25 6] z-down')
.. parsed-literal::
[1] [8] [2] [7] [] [4 25 6] 3
.. code:: ipython2
J('[1] [8] [2] [7] [] [4 25 6] 3 z-right')
.. parsed-literal::
[1] [8] [2] [7] [3] [25 6] 4
.. code:: ipython2
J('[1] [8] [2] [7] [3] [25 6] 4 z-right')
.. parsed-literal::
[1] [8] [2] [7] [4 3] [6] 25
.. code:: ipython2
J('[1] [8] [2] [7] [4 3] [6] 25 sqr')
.. parsed-literal::
[1] [8] [2] [7] [4 3] [6] 625
.. code:: ipython2
V('[1] [8] [2] [7] [4 3] [6] 625 z-up')
.. parsed-literal::
. [1] [8] [2] [7] [4 3] [6] 625 z-up
[1] . [8] [2] [7] [4 3] [6] 625 z-up
[1] [8] . [2] [7] [4 3] [6] 625 z-up
[1] [8] [2] . [7] [4 3] [6] 625 z-up
[1] [8] [2] [7] . [4 3] [6] 625 z-up
[1] [8] [2] [7] [4 3] . [6] 625 z-up
[1] [8] [2] [7] [4 3] [6] . 625 z-up
[1] [8] [2] [7] [4 3] [6] 625 . z-up
[1] [8] [2] [7] [4 3] [6] 625 . swons swap shunt
[1] [8] [2] [7] [4 3] [6] 625 . swap cons swap shunt
[1] [8] [2] [7] [4 3] 625 [6] . cons swap shunt
[1] [8] [2] [7] [4 3] [625 6] . swap shunt
[1] [8] [2] [7] [625 6] [4 3] . shunt
[1] [8] [2] [7] [3 4 625 6] .
.. code:: ipython2
J('[1] [8] [2] [7] [3 4 625 6] z-up')
.. parsed-literal::
[1] [8] [2 [3 4 625 6] 7]
.. code:: ipython2
J('[1] [8] [2 [3 4 625 6] 7] z-up')
.. parsed-literal::
[1 [2 [3 4 625 6] 7] 8]
``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.
.. code:: ipython2
V('[1 [2 [3 4 25 6] 7] 8] [[[[[[sqr] dipd] infra] dip] infra] dip] infra')
.. parsed-literal::
. [1 [2 [3 4 25 6] 7] 8] [[[[[[sqr] dipd] infra] dip] infra] dip] infra
[1 [2 [3 4 25 6] 7] 8] . [[[[[[sqr] dipd] infra] dip] infra] dip] infra
[1 [2 [3 4 25 6] 7] 8] [[[[[[sqr] dipd] infra] dip] infra] dip] . infra
8 [2 [3 4 25 6] 7] 1 . [[[[[sqr] dipd] infra] dip] infra] dip [] swaack
8 [2 [3 4 25 6] 7] 1 [[[[[sqr] dipd] infra] dip] infra] . dip [] swaack
8 [2 [3 4 25 6] 7] . [[[[sqr] dipd] infra] dip] infra 1 [] swaack
8 [2 [3 4 25 6] 7] [[[[sqr] dipd] infra] dip] . infra 1 [] swaack
7 [3 4 25 6] 2 . [[[sqr] dipd] infra] dip [8] swaack 1 [] swaack
7 [3 4 25 6] 2 [[[sqr] dipd] infra] . dip [8] swaack 1 [] swaack
7 [3 4 25 6] . [[sqr] dipd] infra 2 [8] swaack 1 [] swaack
7 [3 4 25 6] [[sqr] dipd] . infra 2 [8] swaack 1 [] swaack
6 25 4 3 . [sqr] dipd [7] swaack 2 [8] swaack 1 [] swaack
6 25 4 3 [sqr] . dipd [7] swaack 2 [8] swaack 1 [] swaack
6 25 . sqr 4 3 [7] swaack 2 [8] swaack 1 [] swaack
6 25 . dup mul 4 3 [7] swaack 2 [8] swaack 1 [] swaack
6 25 25 . mul 4 3 [7] swaack 2 [8] swaack 1 [] swaack
6 625 . 4 3 [7] swaack 2 [8] swaack 1 [] swaack
6 625 4 . 3 [7] swaack 2 [8] swaack 1 [] swaack
6 625 4 3 . [7] swaack 2 [8] swaack 1 [] swaack
6 625 4 3 [7] . swaack 2 [8] swaack 1 [] swaack
7 [3 4 625 6] . 2 [8] swaack 1 [] swaack
7 [3 4 625 6] 2 . [8] swaack 1 [] swaack
7 [3 4 625 6] 2 [8] . swaack 1 [] swaack
8 [2 [3 4 625 6] 7] . 1 [] swaack
8 [2 [3 4 625 6] 7] 1 . [] swaack
8 [2 [3 4 625 6] 7] 1 [] . swaack
[1 [2 [3 4 625 6] 7] 8] .
If you read the trace carefully youll see that about half of it is the
``dip`` and ``infra`` combinators de-quoting programs and “digging” into
the subject datastructure. Instead of maintaining temporary results on
the stack they are pushed into the pending expression (continuation).
When ``sqr`` has run the rest of the pending expression rebuilds the
datastructure.
``Z``
-----
Imagine a function ``Z`` that accepts a sequence of ``dip`` and
``infra`` combinators, a quoted program ``[Q]``, and a datastructure to
work on. It would effectively execute the quoted program as if it had
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
The ``Z`` function isnt hard to make.
.. code:: ipython2
define('Z == [[] cons cons] step i')
Here it is in action in a simplified scenario.
.. code:: ipython2
V('1 [2 3 4] Z')
.. parsed-literal::
. 1 [2 3 4] Z
1 . [2 3 4] Z
1 [2 3 4] . Z
1 [2 3 4] . [[] cons cons] step i
1 [2 3 4] [[] cons cons] . step i
1 2 [[] cons cons] . i [3 4] [[] cons cons] step i
1 2 . [] cons cons [3 4] [[] cons cons] step i
1 2 [] . cons cons [3 4] [[] cons cons] step i
1 [2] . cons [3 4] [[] cons cons] step i
[1 2] . [3 4] [[] cons cons] step i
[1 2] [3 4] . [[] cons cons] step i
[1 2] [3 4] [[] cons cons] . step i
[1 2] 3 [[] cons cons] . i [4] [[] cons cons] step i
[1 2] 3 . [] cons cons [4] [[] cons cons] step i
[1 2] 3 [] . cons cons [4] [[] cons cons] step i
[1 2] [3] . cons [4] [[] cons cons] step i
[[1 2] 3] . [4] [[] cons cons] step i
[[1 2] 3] [4] . [[] cons cons] step i
[[1 2] 3] [4] [[] cons cons] . step i
[[1 2] 3] 4 [[] cons cons] . i i
[[1 2] 3] 4 . [] cons cons i
[[1 2] 3] 4 [] . cons cons i
[[1 2] 3] [4] . cons i
[[[1 2] 3] 4] . i
. [[1 2] 3] 4
[[1 2] 3] . 4
[[1 2] 3] 4 .
And here it is doing the main thing.
.. code:: ipython2
J('[1 [2 [3 4 25 6] 7] 8] [sqr] [dip dip infra dip infra dip infra] Z')
.. parsed-literal::
[1 [2 [3 4 625 6] 7] 8]
Addressing
----------
Because we are only using two combinators we could replace the list with
a string made from only two characters.
::
[...] [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.
---------------------------------------------------
Its easy to read off (in reverse) the right sequence of “d” and “i”
from the subject datastructure:
::
[ n [ n [ n n x ...
i d i d i d d Bingo!

View File

@ -0,0 +1,26 @@
Essays about Programming in Joy
===============================
These essays are adapted from Jupyter notebooks. I hope to have those hosted somewhere where people can view them "live" and interact with them, possibly on MS Azure. For now, Sphinx does such a great job rendering the HTML that I am copying over some notebooks in ReST format and hand-editing them into these documents.
.. toctree::
:glob:
:maxdepth: 2
Developing
Quadratic
Replacing
Recursion_Combinators
Ordered_Binary_Trees
Treestep
Generator_Programs
Newton-Raphson
Zipper
Types
TypeChecking
NoUpdates
Categorical
The_Four_Operations
Derivatives_of_Regular_Expressions

View File

@ -0,0 +1,153 @@
Type Inference of Joy Expressions
=================================
UPDATE: May 2020 - I removed the type inference code in `joy.utils.types`
but you can find it in the `v0.4.0` tag here:
https://osdn.net/projects/joypy/scm/hg/Joypy/tags
Two kinds of type inference are provided, a simple inferencer that can
handle functions that have a single stack effect (aka "type signature")
and that can generate Python code for a limited subset of those
functions, and a more complex inferencer/interpreter hybrid that can
infer the stack effects of most Joy expressions, including multiple stack
effects, unbounded sequences of values, and combinators (if enough
information is available.)
``joy.utils.types``
-------------------
Curently (asterix after name indicates a function that can be
auto-compiled to Python)::
_Tree_add_Ee = ([a4 a5 ...1] a3 a2 a1 -- [a2 a3 ...1]) *
_Tree_delete_R0 = ([a2 ...1] a1 -- [a2 ...1] a2 a1 a1) *
_Tree_delete_clear_stuff = (a3 a2 [a1 ...1] -- [...1]) *
_Tree_get_E = ([a3 a4 ...1] a2 a1 -- a4) *
add = (n1 n2 -- n3)
and = (b1 b2 -- b3)
bool = (a1 -- b1)
ccons = (a2 a1 [...1] -- [a2 a1 ...1]) *
cons = (a1 [...0] -- [a1 ...0]) *
div = (n1 n2 -- n3)
divmod = (n2 n1 -- n4 n3)
dup = (a1 -- a1 a1) *
dupd = (a2 a1 -- a2 a2 a1) *
dupdd = (a3 a2 a1 -- a3 a3 a2 a1) *
eq = (n1 n2 -- b1)
first = ([a1 ...1] -- a1) *
first_two = ([a1 a2 ...1] -- a1 a2) *
floordiv = (n1 n2 -- n3)
fourth = ([a1 a2 a3 a4 ...1] -- a4) *
ge = (n1 n2 -- b1)
gt = (n1 n2 -- b1)
le = (n1 n2 -- b1)
lshift = (n1 n2 -- n3)
lt = (n1 n2 -- b1)
modulus = (n1 n2 -- n3)
mul = (n1 n2 -- n3)
ne = (n1 n2 -- b1)
neg = (n1 -- n2)
not = (a1 -- b1)
over = (a2 a1 -- a2 a1 a2) *
pm = (n2 n1 -- n4 n3)
pop = (a1 --) *
popd = (a2 a1 -- a1) *
popdd = (a3 a2 a1 -- a2 a1) *
popop = (a2 a1 --) *
popopd = (a3 a2 a1 -- a1) *
popopdd = (a4 a3 a2 a1 -- a2 a1) *
pow = (n1 n2 -- n3)
pred = (n1 -- n2)
rest = ([a1 ...0] -- [...0]) *
rolldown = (a1 a2 a3 -- a2 a3 a1) *
rollup = (a1 a2 a3 -- a3 a1 a2) *
rrest = ([a1 a2 ...1] -- [...1]) *
rshift = (n1 n2 -- n3)
second = ([a1 a2 ...1] -- a2) *
sqrt = (n1 -- n2)
stack = (... -- ... [...]) *
stuncons = (... a1 -- ... a1 a1 [...]) *
stununcons = (... a2 a1 -- ... a2 a1 a1 a2 [...]) *
sub = (n1 n2 -- n3)
succ = (n1 -- n2)
swaack = ([...1] -- [...0]) *
swap = (a1 a2 -- a2 a1) *
swons = ([...1] a1 -- [a1 ...1]) *
third = ([a1 a2 a3 ...1] -- a3) *
truediv = (n1 n2 -- n3)
tuck = (a2 a1 -- a1 a2 a1) *
uncons = ([a1 ...0] -- a1 [...0]) *
unit = (a1 -- [a1 ]) *
unswons = ([a1 ...1] -- [...1] a1) *
Example output of the ``infer()`` function. The first number on each
line is the depth of the Python stack. It goes down when the function
backtracks. The next thing on each line is the currently-computed stack
effect so far. It starts with the empty "identity function" and proceeds
through the expression, which is the rest of each line. The function
acts like an interpreter but instead of executing the terms of the
expression it composes them, but for combinators it *does* execute them,
using the output side of the stack effect as the stack. This seems to
work fine. With proper definitions for the behavior of the combinators
that can have more than one effect (like ``branch`` or ``loop``) the
``infer()`` function seems to be able to handle anything I throw at it so
far.
::
7 (--) ∘ pop swap rolldown rest rest cons cons
10 (a1 --) ∘ swap rolldown rest rest cons cons
13 (a3 a2 a1 -- a2 a3) ∘ rolldown rest rest cons cons
16 (a4 a3 a2 a1 -- a2 a3 a4) ∘ rest rest cons cons
19 ([a4 ...1] a3 a2 a1 -- a2 a3 [...1]) ∘ rest cons cons
22 ([a4 a5 ...1] a3 a2 a1 -- a2 a3 [...1]) ∘ cons cons
25 ([a4 a5 ...1] a3 a2 a1 -- a2 [a3 ...1]) ∘ cons
28 ([a4 a5 ...1] a3 a2 a1 -- [a2 a3 ...1]) ∘
----------------------------------------
([a4 a5 ...1] a3 a2 a1 -- [a2 a3 ...1])
Here's another example (implementing ``ifte``) using some combinators::
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)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,297 @@
/*
* language_data.js
* ~~~~~~~~~~~~~~~~
*
* This script contains the language-specific data used by searchtools.js,
* namely the list of stopwords, stemmer, scorer and splitter.
*
* :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
var stopwords = ["a","and","are","as","at","be","but","by","for","if","in","into","is","it","near","no","not","of","on","or","such","that","the","their","then","there","these","they","this","to","was","will","with"];
/* Non-minified version is copied as a separate JS file, is available */
/**
* Porter Stemmer
*/
var Stemmer = function() {
var step2list = {
ational: 'ate',
tional: 'tion',
enci: 'ence',
anci: 'ance',
izer: 'ize',
bli: 'ble',
alli: 'al',
entli: 'ent',
eli: 'e',
ousli: 'ous',
ization: 'ize',
ation: 'ate',
ator: 'ate',
alism: 'al',
iveness: 'ive',
fulness: 'ful',
ousness: 'ous',
aliti: 'al',
iviti: 'ive',
biliti: 'ble',
logi: 'log'
};
var step3list = {
icate: 'ic',
ative: '',
alize: 'al',
iciti: 'ic',
ical: 'ic',
ful: '',
ness: ''
};
var c = "[^aeiou]"; // consonant
var v = "[aeiouy]"; // vowel
var C = c + "[^aeiouy]*"; // consonant sequence
var V = v + "[aeiou]*"; // vowel sequence
var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0
var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1
var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1
var s_v = "^(" + C + ")?" + v; // vowel in stem
this.stemWord = function (w) {
var stem;
var suffix;
var firstch;
var origword = w;
if (w.length < 3)
return w;
var re;
var re2;
var re3;
var re4;
firstch = w.substr(0,1);
if (firstch == "y")
w = firstch.toUpperCase() + w.substr(1);
// Step 1a
re = /^(.+?)(ss|i)es$/;
re2 = /^(.+?)([^s])s$/;
if (re.test(w))
w = w.replace(re,"$1$2");
else if (re2.test(w))
w = w.replace(re2,"$1$2");
// Step 1b
re = /^(.+?)eed$/;
re2 = /^(.+?)(ed|ing)$/;
if (re.test(w)) {
var fp = re.exec(w);
re = new RegExp(mgr0);
if (re.test(fp[1])) {
re = /.$/;
w = w.replace(re,"");
}
}
else if (re2.test(w)) {
var fp = re2.exec(w);
stem = fp[1];
re2 = new RegExp(s_v);
if (re2.test(stem)) {
w = stem;
re2 = /(at|bl|iz)$/;
re3 = new RegExp("([^aeiouylsz])\\1$");
re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
if (re2.test(w))
w = w + "e";
else if (re3.test(w)) {
re = /.$/;
w = w.replace(re,"");
}
else if (re4.test(w))
w = w + "e";
}
}
// Step 1c
re = /^(.+?)y$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(s_v);
if (re.test(stem))
w = stem + "i";
}
// Step 2
re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
suffix = fp[2];
re = new RegExp(mgr0);
if (re.test(stem))
w = stem + step2list[suffix];
}
// Step 3
re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
suffix = fp[2];
re = new RegExp(mgr0);
if (re.test(stem))
w = stem + step3list[suffix];
}
// Step 4
re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
re2 = /^(.+?)(s|t)(ion)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(mgr1);
if (re.test(stem))
w = stem;
}
else if (re2.test(w)) {
var fp = re2.exec(w);
stem = fp[1] + fp[2];
re2 = new RegExp(mgr1);
if (re2.test(stem))
w = stem;
}
// Step 5
re = /^(.+?)e$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(mgr1);
re2 = new RegExp(meq1);
re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
w = stem;
}
re = /ll$/;
re2 = new RegExp(mgr1);
if (re.test(w) && re2.test(w)) {
re = /.$/;
w = w.replace(re,"");
}
// and turn initial Y back to y
if (firstch == "y")
w = firstch.toLowerCase() + w.substr(1);
return w;
}
}
var splitChars = (function() {
var result = {};
var singles = [96, 180, 187, 191, 215, 247, 749, 885, 903, 907, 909, 930, 1014, 1648,
1748, 1809, 2416, 2473, 2481, 2526, 2601, 2609, 2612, 2615, 2653, 2702,
2706, 2729, 2737, 2740, 2857, 2865, 2868, 2910, 2928, 2948, 2961, 2971,
2973, 3085, 3089, 3113, 3124, 3213, 3217, 3241, 3252, 3295, 3341, 3345,
3369, 3506, 3516, 3633, 3715, 3721, 3736, 3744, 3748, 3750, 3756, 3761,
3781, 3912, 4239, 4347, 4681, 4695, 4697, 4745, 4785, 4799, 4801, 4823,
4881, 5760, 5901, 5997, 6313, 7405, 8024, 8026, 8028, 8030, 8117, 8125,
8133, 8181, 8468, 8485, 8487, 8489, 8494, 8527, 11311, 11359, 11687, 11695,
11703, 11711, 11719, 11727, 11735, 12448, 12539, 43010, 43014, 43019, 43587,
43696, 43713, 64286, 64297, 64311, 64317, 64319, 64322, 64325, 65141];
var i, j, start, end;
for (i = 0; i < singles.length; i++) {
result[singles[i]] = true;
}
var ranges = [[0, 47], [58, 64], [91, 94], [123, 169], [171, 177], [182, 184], [706, 709],
[722, 735], [741, 747], [751, 879], [888, 889], [894, 901], [1154, 1161],
[1318, 1328], [1367, 1368], [1370, 1376], [1416, 1487], [1515, 1519], [1523, 1568],
[1611, 1631], [1642, 1645], [1750, 1764], [1767, 1773], [1789, 1790], [1792, 1807],
[1840, 1868], [1958, 1968], [1970, 1983], [2027, 2035], [2038, 2041], [2043, 2047],
[2070, 2073], [2075, 2083], [2085, 2087], [2089, 2307], [2362, 2364], [2366, 2383],
[2385, 2391], [2402, 2405], [2419, 2424], [2432, 2436], [2445, 2446], [2449, 2450],
[2483, 2485], [2490, 2492], [2494, 2509], [2511, 2523], [2530, 2533], [2546, 2547],
[2554, 2564], [2571, 2574], [2577, 2578], [2618, 2648], [2655, 2661], [2672, 2673],
[2677, 2692], [2746, 2748], [2750, 2767], [2769, 2783], [2786, 2789], [2800, 2820],
[2829, 2830], [2833, 2834], [2874, 2876], [2878, 2907], [2914, 2917], [2930, 2946],
[2955, 2957], [2966, 2968], [2976, 2978], [2981, 2983], [2987, 2989], [3002, 3023],
[3025, 3045], [3059, 3076], [3130, 3132], [3134, 3159], [3162, 3167], [3170, 3173],
[3184, 3191], [3199, 3204], [3258, 3260], [3262, 3293], [3298, 3301], [3312, 3332],
[3386, 3388], [3390, 3423], [3426, 3429], [3446, 3449], [3456, 3460], [3479, 3481],
[3518, 3519], [3527, 3584], [3636, 3647], [3655, 3663], [3674, 3712], [3717, 3718],
[3723, 3724], [3726, 3731], [3752, 3753], [3764, 3772], [3774, 3775], [3783, 3791],
[3802, 3803], [3806, 3839], [3841, 3871], [3892, 3903], [3949, 3975], [3980, 4095],
[4139, 4158], [4170, 4175], [4182, 4185], [4190, 4192], [4194, 4196], [4199, 4205],
[4209, 4212], [4226, 4237], [4250, 4255], [4294, 4303], [4349, 4351], [4686, 4687],
[4702, 4703], [4750, 4751], [4790, 4791], [4806, 4807], [4886, 4887], [4955, 4968],
[4989, 4991], [5008, 5023], [5109, 5120], [5741, 5742], [5787, 5791], [5867, 5869],
[5873, 5887], [5906, 5919], [5938, 5951], [5970, 5983], [6001, 6015], [6068, 6102],
[6104, 6107], [6109, 6111], [6122, 6127], [6138, 6159], [6170, 6175], [6264, 6271],
[6315, 6319], [6390, 6399], [6429, 6469], [6510, 6511], [6517, 6527], [6572, 6592],
[6600, 6607], [6619, 6655], [6679, 6687], [6741, 6783], [6794, 6799], [6810, 6822],
[6824, 6916], [6964, 6980], [6988, 6991], [7002, 7042], [7073, 7085], [7098, 7167],
[7204, 7231], [7242, 7244], [7294, 7400], [7410, 7423], [7616, 7679], [7958, 7959],
[7966, 7967], [8006, 8007], [8014, 8015], [8062, 8063], [8127, 8129], [8141, 8143],
[8148, 8149], [8156, 8159], [8173, 8177], [8189, 8303], [8306, 8307], [8314, 8318],
[8330, 8335], [8341, 8449], [8451, 8454], [8456, 8457], [8470, 8472], [8478, 8483],
[8506, 8507], [8512, 8516], [8522, 8525], [8586, 9311], [9372, 9449], [9472, 10101],
[10132, 11263], [11493, 11498], [11503, 11516], [11518, 11519], [11558, 11567],
[11622, 11630], [11632, 11647], [11671, 11679], [11743, 11822], [11824, 12292],
[12296, 12320], [12330, 12336], [12342, 12343], [12349, 12352], [12439, 12444],
[12544, 12548], [12590, 12592], [12687, 12689], [12694, 12703], [12728, 12783],
[12800, 12831], [12842, 12880], [12896, 12927], [12938, 12976], [12992, 13311],
[19894, 19967], [40908, 40959], [42125, 42191], [42238, 42239], [42509, 42511],
[42540, 42559], [42592, 42593], [42607, 42622], [42648, 42655], [42736, 42774],
[42784, 42785], [42889, 42890], [42893, 43002], [43043, 43055], [43062, 43071],
[43124, 43137], [43188, 43215], [43226, 43249], [43256, 43258], [43260, 43263],
[43302, 43311], [43335, 43359], [43389, 43395], [43443, 43470], [43482, 43519],
[43561, 43583], [43596, 43599], [43610, 43615], [43639, 43641], [43643, 43647],
[43698, 43700], [43703, 43704], [43710, 43711], [43715, 43738], [43742, 43967],
[44003, 44015], [44026, 44031], [55204, 55215], [55239, 55242], [55292, 55295],
[57344, 63743], [64046, 64047], [64110, 64111], [64218, 64255], [64263, 64274],
[64280, 64284], [64434, 64466], [64830, 64847], [64912, 64913], [64968, 65007],
[65020, 65135], [65277, 65295], [65306, 65312], [65339, 65344], [65371, 65381],
[65471, 65473], [65480, 65481], [65488, 65489], [65496, 65497]];
for (i = 0; i < ranges.length; i++) {
start = ranges[i][0];
end = ranges[i][1];
for (j = start; j <= end; j++) {
result[j] = true;
}
}
return result;
})();
function splitQuery(query) {
var result = [];
var start = -1;
for (var i = 0; i < query.length; i++) {
if (splitChars[query.charCodeAt(i)]) {
if (start !== -1) {
result.push(query.slice(start, i));
start = -1;
}
} else if (start === -1) {
start = i;
}
}
if (start !== -1) {
result.push(query.slice(start));
}
return result;
}

File diff suppressed because it is too large Load Diff