A little more bit o' docs.
This commit is contained in:
parent
41b39e5977
commit
54491f0da2
|
|
@ -367,10 +367,6 @@
|
|||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# This is the straightforward version with no \"compaction\".\n",
|
||||
"# It works fine, but does waaaay too much work because the\n",
|
||||
"# expressions grow each derivation.\n",
|
||||
"\n",
|
||||
"def D(symbol):\n",
|
||||
"\n",
|
||||
" def derv(R):\n",
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ $(docs_rst): %.rst : %.ipynb
|
|||
python -m nbconvert --to rst $<
|
||||
|
||||
|
||||
move_us = Generator_Programs.rst Newton-Raphson.rst Ordered_Binary_Trees.rst Quadratic.rst Recursion_Combinators.rst Replacing.rst Treestep.rst Types.rst Zipper.rst
|
||||
move_us = Derivatives_of_Regular_Expressions.rst Generator_Programs.rst Newton-Raphson.rst Ordered_Binary_Trees.rst Quadratic.rst Recursion_Combinators.rst Replacing.rst The_Four_Operations.rst Treestep.rst TypeChecking.rst Types.rst Zipper.rst
|
||||
|
||||
mov: $(move_us)
|
||||
cp -v $? ./sphinx_docs/notebooks/
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@
|
|||
<span class="p">(</span><span class="s1">'pred'</span><span class="p">,</span> <span class="p">[</span><span class="s1">'--'</span><span class="p">]),</span>
|
||||
<span class="p">(</span><span class="s1">'rolldown'</span><span class="p">,</span> <span class="p">[</span><span class="s1">'roll<'</span><span class="p">]),</span>
|
||||
<span class="p">(</span><span class="s1">'rollup'</span><span class="p">,</span> <span class="p">[</span><span class="s1">'roll>'</span><span class="p">]),</span>
|
||||
<span class="p">(</span><span class="s1">'id'</span><span class="p">,</span> <span class="p">[</span><span class="s1">'•'</span><span class="p">]),</span>
|
||||
<span class="p">(</span><span class="s1">'id'</span><span class="p">,</span> <span class="p">[</span><span class="sa">u</span><span class="s1">'•'</span><span class="p">]),</span>
|
||||
<span class="p">)</span>
|
||||
|
||||
|
||||
|
|
@ -141,7 +141,8 @@
|
|||
<span class="s1">run == [] swap infra</span>
|
||||
<span class="s1">sqr == dup mul</span>
|
||||
<span class="s1">size == 0 swap [pop ++] step</span>
|
||||
<span class="s1">cleave == [i] app2 [popd] dip</span>
|
||||
<span class="s1">fork == [i] app2</span>
|
||||
<span class="s1">cleave == fork [popd] dip</span>
|
||||
<span class="s1">average == [sum 1.0 *] [size] cleave /</span>
|
||||
<span class="s1">gcd == 1 [tuck modulus dup 0 >] loop pop</span>
|
||||
<span class="s1">least_fraction == dup [gcd] infra [div] concat map</span>
|
||||
|
|
@ -157,8 +158,9 @@
|
|||
<span class="s1">step_zero == 0 roll> step</span>
|
||||
<span class="s1">codireco == cons dip rest cons</span>
|
||||
<span class="s1">make_generator == [codireco] ccons</span>
|
||||
<span class="s1">ifte == [nullary not] dipd branch</span>
|
||||
<span class="s1">'''</span>
|
||||
<span class="c1"># ifte == [nullary not] dipd branch</span>
|
||||
<span class="c1"># </span>
|
||||
<span class="c1"># ifte == [nullary] dipd swap branch</span>
|
||||
<span class="c1"># genrec == [[genrec] cons cons cons cons] nullary swons concat ifte</span>
|
||||
|
||||
|
|
@ -510,6 +512,8 @@
|
|||
<span class="sd">'''Clear everything from the stack.</span>
|
||||
<span class="sd"> ::</span>
|
||||
|
||||
<span class="sd"> clear == stack [pop stack] loop</span>
|
||||
|
||||
<span class="sd"> ... clear</span>
|
||||
<span class="sd"> ---------------</span>
|
||||
|
||||
|
|
@ -991,32 +995,32 @@
|
|||
<span class="k">return</span> <span class="n">stack</span><span class="p">,</span> <span class="n">concat</span><span class="p">(</span><span class="n">then</span> <span class="k">if</span> <span class="n">flag</span> <span class="k">else</span> <span class="n">else_</span><span class="p">,</span> <span class="n">expression</span><span class="p">),</span> <span class="n">dictionary</span></div>
|
||||
|
||||
|
||||
<div class="viewcode-block" id="ifte"><a class="viewcode-back" href="../../library.html#joy.library.ifte">[docs]</a><span class="nd">@inscribe</span>
|
||||
<span class="nd">@FunctionWrapper</span>
|
||||
<span class="k">def</span> <span class="nf">ifte</span><span class="p">(</span><span class="n">stack</span><span class="p">,</span> <span class="n">expression</span><span class="p">,</span> <span class="n">dictionary</span><span class="p">):</span>
|
||||
<span class="sd">'''</span>
|
||||
<span class="sd"> If-Then-Else Combinator</span>
|
||||
<span class="sd"> ::</span>
|
||||
|
||||
<span class="sd"> ... [if] [then] [else] ifte</span>
|
||||
<span class="sd"> ---------------------------------------------------</span>
|
||||
<span class="sd"> ... [[else] [then]] [...] [if] infra select i</span>
|
||||
|
||||
|
||||
|
||||
|
||||
<span class="sd"> ... [if] [then] [else] ifte</span>
|
||||
<span class="sd"> -------------------------------------------------------</span>
|
||||
<span class="sd"> ... [else] [then] [...] [if] infra first choice i</span>
|
||||
|
||||
|
||||
<span class="sd"> Has the effect of grabbing a copy of the stack on which to run the</span>
|
||||
<span class="sd"> if-part using infra.</span>
|
||||
<span class="sd"> '''</span>
|
||||
<span class="p">(</span><span class="n">else_</span><span class="p">,</span> <span class="p">(</span><span class="n">then</span><span class="p">,</span> <span class="p">(</span><span class="n">if_</span><span class="p">,</span> <span class="n">stack</span><span class="p">)))</span> <span class="o">=</span> <span class="n">stack</span>
|
||||
<span class="n">expression</span> <span class="o">=</span> <span class="p">(</span><span class="n">S_infra</span><span class="p">,</span> <span class="p">(</span><span class="n">S_first</span><span class="p">,</span> <span class="p">(</span><span class="n">S_choice</span><span class="p">,</span> <span class="p">(</span><span class="n">S_i</span><span class="p">,</span> <span class="n">expression</span><span class="p">))))</span>
|
||||
<span class="n">stack</span> <span class="o">=</span> <span class="p">(</span><span class="n">if_</span><span class="p">,</span> <span class="p">(</span><span class="n">stack</span><span class="p">,</span> <span class="p">(</span><span class="n">then</span><span class="p">,</span> <span class="p">(</span><span class="n">else_</span><span class="p">,</span> <span class="n">stack</span><span class="p">))))</span>
|
||||
<span class="k">return</span> <span class="n">stack</span><span class="p">,</span> <span class="n">expression</span><span class="p">,</span> <span class="n">dictionary</span></div>
|
||||
<span class="c1">##@inscribe</span>
|
||||
<span class="c1">##@FunctionWrapper</span>
|
||||
<span class="c1">##def ifte(stack, expression, dictionary):</span>
|
||||
<span class="c1">## '''</span>
|
||||
<span class="c1">## If-Then-Else Combinator</span>
|
||||
<span class="c1">## ::</span>
|
||||
<span class="c1">##</span>
|
||||
<span class="c1">## ... [if] [then] [else] ifte</span>
|
||||
<span class="c1">## ---------------------------------------------------</span>
|
||||
<span class="c1">## ... [[else] [then]] [...] [if] infra select i</span>
|
||||
<span class="c1">##</span>
|
||||
<span class="c1">##</span>
|
||||
<span class="c1">##</span>
|
||||
<span class="c1">##</span>
|
||||
<span class="c1">## ... [if] [then] [else] ifte</span>
|
||||
<span class="c1">## -------------------------------------------------------</span>
|
||||
<span class="c1">## ... [else] [then] [...] [if] infra first choice i</span>
|
||||
<span class="c1">##</span>
|
||||
<span class="c1">##</span>
|
||||
<span class="c1">## Has the effect of grabbing a copy of the stack on which to run the</span>
|
||||
<span class="c1">## if-part using infra.</span>
|
||||
<span class="c1">## '''</span>
|
||||
<span class="c1">## (else_, (then, (if_, stack))) = stack</span>
|
||||
<span class="c1">## expression = (S_infra, (S_first, (S_choice, (S_i, expression))))</span>
|
||||
<span class="c1">## stack = (if_, (stack, (then, (else_, stack))))</span>
|
||||
<span class="c1">## return stack, expression, dictionary</span>
|
||||
|
||||
|
||||
<div class="viewcode-block" id="cond"><a class="viewcode-back" href="../../library.html#joy.library.cond">[docs]</a><span class="nd">@inscribe</span>
|
||||
|
|
|
|||
|
|
@ -75,16 +75,14 @@
|
|||
</li>
|
||||
</ul></li>
|
||||
<li><a href="library.html#joy.library.add_aliases">add_aliases() (in module joy.library)</a>
|
||||
</li>
|
||||
<li><a href="library.html#joy.library.DefinitionWrapper.add_def">add_def() (joy.library.DefinitionWrapper class method)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="library.html#joy.library.DefinitionWrapper.add_def">add_def() (joy.library.DefinitionWrapper class method)</a>
|
||||
</li>
|
||||
<li><a href="library.html#joy.library.DefinitionWrapper.add_definitions">add_definitions() (joy.library.DefinitionWrapper class method)</a>
|
||||
</li>
|
||||
<li><a href="types.html#joy.utils.types.AnyJoyType">AnyJoyType (class in joy.utils.types)</a>
|
||||
</li>
|
||||
<li><a href="types.html#joy.utils.polytypes.AnyStarJoyType">AnyStarJoyType (class in joy.utils.polytypes)</a>
|
||||
</li>
|
||||
<li><a href="library.html#joy.library.app1">app1() (in module joy.library)</a>
|
||||
</li>
|
||||
|
|
@ -245,8 +243,6 @@
|
|||
<li><a href="library.html#joy.library.i">i() (in module joy.library)</a>
|
||||
</li>
|
||||
<li><a href="library.html#joy.library.id_">id_() (in module joy.library)</a>
|
||||
</li>
|
||||
<li><a href="library.html#joy.library.ifte">ifte() (in module joy.library)</a>
|
||||
</li>
|
||||
<li><a href="types.html#joy.utils.polytypes.infer">infer() (in module joy.utils.polytypes)</a>
|
||||
</li>
|
||||
|
|
@ -300,16 +296,8 @@
|
|||
<h2 id="K">K</h2>
|
||||
<table style="width: 100%" class="indextable genindextable"><tr>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="types.html#joy.utils.polytypes.AnyStarJoyType.kind">kind (joy.utils.polytypes.AnyStarJoyType attribute)</a>
|
||||
|
||||
<ul>
|
||||
<li><a href="types.html#joy.utils.polytypes.KleeneStar.kind">(joy.utils.polytypes.KleeneStar attribute)</a>
|
||||
<li><a href="types.html#joy.utils.polytypes.KleeneStar.kind">kind (joy.utils.polytypes.KleeneStar attribute)</a>
|
||||
</li>
|
||||
<li><a href="types.html#joy.utils.polytypes.NumberStarJoyType.kind">(joy.utils.polytypes.NumberStarJoyType attribute)</a>
|
||||
</li>
|
||||
<li><a href="types.html#joy.utils.polytypes.StackStarJoyType.kind">(joy.utils.polytypes.StackStarJoyType attribute)</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="types.html#joy.utils.polytypes.KleeneStar">KleeneStar (class in joy.utils.polytypes)</a>
|
||||
|
|
@ -349,10 +337,6 @@
|
|||
<table style="width: 100%" class="indextable genindextable"><tr>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="types.html#joy.utils.types.NumberJoyType">NumberJoyType (class in joy.utils.types)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="types.html#joy.utils.polytypes.NumberStarJoyType">NumberStarJoyType (class in joy.utils.polytypes)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
</tr></table>
|
||||
|
|
@ -450,8 +434,6 @@
|
|||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="types.html#joy.utils.polytypes.StackStarJoyType">StackStarJoyType (class in joy.utils.polytypes)</a>
|
||||
</li>
|
||||
<li><a href="library.html#joy.library.step">step() (in module joy.library)</a>
|
||||
</li>
|
||||
<li><a href="library.html#joy.utils.generated_library.stuncons">stuncons() (in module joy.utils.generated_library)</a>
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ interesting aspects. It’s quite a treasure trove.</p>
|
|||
<li class="toctree-l2"><a class="reference internal" href="notebooks/Developing.html">Developing a Program in Joy</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="notebooks/Quadratic.html">Quadratic formula</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="notebooks/Replacing.html">Replacing Functions in the Dictionary</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="notebooks/Recursion_Combinators.html">Recursive Combinators</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="notebooks/Recursion_Combinators.html">Recursion Combinators</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="notebooks/Ordered_Binary_Trees.html">Treating Trees I: Ordered Binary Trees</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="notebooks/Treestep.html">Treating Trees II: <code class="docutils literal notranslate"><span class="pre">treestep</span></code></a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="notebooks/Generator_Programs.html">Using <code class="docutils literal notranslate"><span class="pre">x</span></code> to Generate Values</a></li>
|
||||
|
|
|
|||
|
|
@ -187,7 +187,9 @@ Boolean value (so empty string, zero, etc. are counted as false, etc.)</p>
|
|||
<dt id="joy.library.clear">
|
||||
<code class="descclassname">joy.library.</code><code class="descname">clear</code><span class="sig-paren">(</span><em>stack</em>, <em>expression</em>, <em>dictionary</em><span class="sig-paren">)</span><a class="reference internal" href="_modules/joy/library.html#clear"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#joy.library.clear" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Clear everything from the stack.</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span> <span class="o">...</span> <span class="n">clear</span>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">clear</span> <span class="o">==</span> <span class="n">stack</span> <span class="p">[</span><span class="n">pop</span> <span class="n">stack</span><span class="p">]</span> <span class="n">loop</span>
|
||||
|
||||
<span class="o">...</span> <span class="n">clear</span>
|
||||
<span class="o">---------------</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
|
|
@ -403,26 +405,6 @@ onto the pending expression for evaluation.</p>
|
|||
<dd><p>The identity function.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="function">
|
||||
<dt id="joy.library.ifte">
|
||||
<code class="descclassname">joy.library.</code><code class="descname">ifte</code><span class="sig-paren">(</span><em>stack</em>, <em>expression</em>, <em>dictionary</em><span class="sig-paren">)</span><a class="reference internal" href="_modules/joy/library.html#ifte"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#joy.library.ifte" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>If-Then-Else Combinator</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span> <span class="o">...</span> <span class="p">[</span><span class="k">if</span><span class="p">]</span> <span class="p">[</span><span class="n">then</span><span class="p">]</span> <span class="p">[</span><span class="k">else</span><span class="p">]</span> <span class="n">ifte</span>
|
||||
<span class="o">---------------------------------------------------</span>
|
||||
<span class="o">...</span> <span class="p">[[</span><span class="k">else</span><span class="p">]</span> <span class="p">[</span><span class="n">then</span><span class="p">]]</span> <span class="p">[</span><span class="o">...</span><span class="p">]</span> <span class="p">[</span><span class="k">if</span><span class="p">]</span> <span class="n">infra</span> <span class="n">select</span> <span class="n">i</span>
|
||||
|
||||
|
||||
|
||||
|
||||
<span class="o">...</span> <span class="p">[</span><span class="k">if</span><span class="p">]</span> <span class="p">[</span><span class="n">then</span><span class="p">]</span> <span class="p">[</span><span class="k">else</span><span class="p">]</span> <span class="n">ifte</span>
|
||||
<span class="o">-------------------------------------------------------</span>
|
||||
<span class="o">...</span> <span class="p">[</span><span class="k">else</span><span class="p">]</span> <span class="p">[</span><span class="n">then</span><span class="p">]</span> <span class="p">[</span><span class="o">...</span><span class="p">]</span> <span class="p">[</span><span class="k">if</span><span class="p">]</span> <span class="n">infra</span> <span class="n">first</span> <span class="n">choice</span> <span class="n">i</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Has the effect of grabbing a copy of the stack on which to run the
|
||||
if-part using infra.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="function">
|
||||
<dt id="joy.library.infra">
|
||||
<code class="descclassname">joy.library.</code><code class="descname">infra</code><span class="sig-paren">(</span><em>stack</em>, <em>expression</em>, <em>dictionary</em><span class="sig-paren">)</span><a class="reference internal" href="_modules/joy/library.html#infra"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#joy.library.infra" title="Permalink to this definition">¶</a></dt>
|
||||
|
|
|
|||
|
|
@ -366,10 +366,9 @@ numbers sixty-six times and then four more.</p>
|
|||
</div>
|
||||
<div class="section" id="project-euler-problem-two">
|
||||
<h2>Project Euler Problem Two<a class="headerlink" href="#project-euler-problem-two" title="Permalink to this headline">¶</a></h2>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">By</span> <span class="n">considering</span> <span class="n">the</span> <span class="n">terms</span> <span class="ow">in</span> <span class="n">the</span> <span class="n">Fibonacci</span> <span class="n">sequence</span> <span class="n">whose</span> <span class="n">values</span> <span class="n">do</span> <span class="ow">not</span> <span class="n">exceed</span> <span class="n">four</span> <span class="n">million</span><span class="p">,</span>
|
||||
<span class="n">find</span> <span class="n">the</span> <span class="nb">sum</span> <span class="n">of</span> <span class="n">the</span> <span class="n">even</span><span class="o">-</span><span class="n">valued</span> <span class="n">terms</span><span class="o">.</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<blockquote>
|
||||
<div>By considering the terms in the Fibonacci sequence whose values do
|
||||
not exceed four million, find the sum of the even-valued terms.</div></blockquote>
|
||||
<p>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
|
||||
<code class="docutils literal notranslate"><span class="pre">pop</span></code>s it otherwise.</p>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
<link rel="index" title="Index" href="../genindex.html" />
|
||||
<link rel="search" title="Search" href="../search.html" />
|
||||
<link rel="next" title="Treating Trees II: treestep" href="Treestep.html" />
|
||||
<link rel="prev" title="Recursive Combinators" href="Recursion_Combinators.html" />
|
||||
<link rel="prev" title="Recursion Combinators" href="Recursion_Combinators.html" />
|
||||
|
||||
<link rel="stylesheet" href="../_static/custom.css" type="text/css" />
|
||||
|
||||
|
|
@ -1373,7 +1373,7 @@ Tree-delete == [pop not] [pop] [_Tree_delete_R0] [_Tree_delete_R1] genrec
|
|||
<ul>
|
||||
<li><a href="../index.html">Documentation overview</a><ul>
|
||||
<li><a href="index.html">Essays about Programming in Joy</a><ul>
|
||||
<li>Previous: <a href="Recursion_Combinators.html" title="previous chapter">Recursive Combinators</a></li>
|
||||
<li>Previous: <a href="Recursion_Combinators.html" title="previous chapter">Recursion Combinators</a></li>
|
||||
<li>Next: <a href="Treestep.html" title="next chapter">Treating Trees II: <code class="docutils literal notranslate"><span class="pre">treestep</span></code></a></li>
|
||||
</ul></li>
|
||||
</ul></li>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>Recursive Combinators — Thun 0.2.0 documentation</title>
|
||||
<title>Recursion Combinators — Thun 0.2.0 documentation</title>
|
||||
<link rel="stylesheet" href="../_static/alabaster.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
|
||||
<script type="text/javascript" src="../_static/documentation_options.js"></script>
|
||||
|
|
@ -35,8 +35,8 @@
|
|||
<div class="code ipython2 highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">notebook_preamble</span> <span class="k">import</span> <span class="n">D</span><span class="p">,</span> <span class="n">DefinitionWrapper</span><span class="p">,</span> <span class="n">J</span><span class="p">,</span> <span class="n">V</span><span class="p">,</span> <span class="n">define</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<div class="section" id="recursive-combinators">
|
||||
<h1>Recursive Combinators<a class="headerlink" href="#recursive-combinators" title="Permalink to this headline">¶</a></h1>
|
||||
<div class="section" id="recursion-combinators">
|
||||
<h1>Recursion Combinators<a class="headerlink" href="#recursion-combinators" title="Permalink to this headline">¶</a></h1>
|
||||
<p>This article describes the <code class="docutils literal notranslate"><span class="pre">genrec</span></code> combinator, how to use it, and
|
||||
several generic specializations.</p>
|
||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span> <span class="p">[</span><span class="k">if</span><span class="p">]</span> <span class="p">[</span><span class="n">then</span><span class="p">]</span> <span class="p">[</span><span class="n">rec1</span><span class="p">]</span> <span class="p">[</span><span class="n">rec2</span><span class="p">]</span> <span class="n">genrec</span>
|
||||
|
|
@ -598,7 +598,7 @@ Wire”</a></p>
|
|||
<div class="sphinxsidebarwrapper">
|
||||
<h3><a href="../index.html">Table Of Contents</a></h3>
|
||||
<ul>
|
||||
<li><a class="reference internal" href="#">Recursive Combinators</a><ul>
|
||||
<li><a class="reference internal" href="#">Recursion Combinators</a><ul>
|
||||
<li><a class="reference internal" href="#designing-recursive-functions">Designing Recursive Functions</a></li>
|
||||
<li><a class="reference internal" href="#primitive-recursive-functions">Primitive Recursive Functions</a></li>
|
||||
<li><a class="reference internal" href="#hylomorphism">Hylomorphism</a></li>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||||
<link rel="index" title="Index" href="../genindex.html" />
|
||||
<link rel="search" title="Search" href="../search.html" />
|
||||
<link rel="next" title="Recursive Combinators" href="Recursion_Combinators.html" />
|
||||
<link rel="next" title="Recursion Combinators" href="Recursion_Combinators.html" />
|
||||
<link rel="prev" title="Quadratic formula" href="Quadratic.html" />
|
||||
|
||||
<link rel="stylesheet" href="../_static/custom.css" type="text/css" />
|
||||
|
|
@ -185,7 +185,7 @@ and re-evaluate the expression.</p>
|
|||
<li><a href="../index.html">Documentation overview</a><ul>
|
||||
<li><a href="index.html">Essays about Programming in Joy</a><ul>
|
||||
<li>Previous: <a href="Quadratic.html" title="previous chapter">Quadratic formula</a></li>
|
||||
<li>Next: <a href="Recursion_Combinators.html" title="next chapter">Recursive Combinators</a></li>
|
||||
<li>Next: <a href="Recursion_Combinators.html" title="next chapter">Recursion Combinators</a></li>
|
||||
</ul></li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@
|
|||
<li class="toctree-l2"><a class="reference internal" href="Replacing.html#a-shorter-trace">A shorter trace</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Recursion_Combinators.html">Recursive Combinators</a><ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="Recursion_Combinators.html">Recursion Combinators</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Recursion_Combinators.html#designing-recursive-functions">Designing Recursive Functions</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Recursion_Combinators.html#primitive-recursive-functions">Primitive Recursive Functions</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Recursion_Combinators.html#hylomorphism">Hylomorphism</a></li>
|
||||
|
|
|
|||
Binary file not shown.
File diff suppressed because one or more lines are too long
|
|
@ -277,17 +277,6 @@ is used to generate one.</p>
|
|||
we can capture more information about the type signatures of some functions,
|
||||
and we can introduce a kind of Kleene Star or sequence type that can stand for
|
||||
an unbounded sequence of other types.</p>
|
||||
<dl class="class">
|
||||
<dt id="joy.utils.polytypes.AnyStarJoyType">
|
||||
<em class="property">class </em><code class="descclassname">joy.utils.polytypes.</code><code class="descname">AnyStarJoyType</code><span class="sig-paren">(</span><em>number</em><span class="sig-paren">)</span><a class="reference internal" href="_modules/joy/utils/polytypes.html#AnyStarJoyType"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#joy.utils.polytypes.AnyStarJoyType" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><dl class="attribute">
|
||||
<dt id="joy.utils.polytypes.AnyStarJoyType.kind">
|
||||
<code class="descname">kind</code><a class="headerlink" href="#joy.utils.polytypes.AnyStarJoyType.kind" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>alias of <a class="reference internal" href="#joy.utils.types.AnyJoyType" title="joy.utils.types.AnyJoyType"><code class="xref py py-class docutils literal notranslate"><span class="pre">joy.utils.types.AnyJoyType</span></code></a></p>
|
||||
</dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
||||
<dl class="class">
|
||||
<dt id="joy.utils.polytypes.CombinatorJoyType">
|
||||
<em class="property">class </em><code class="descclassname">joy.utils.polytypes.</code><code class="descname">CombinatorJoyType</code><span class="sig-paren">(</span><em>name</em>, <em>sec</em>, <em>number</em>, <em>expect=None</em><span class="sig-paren">)</span><a class="reference internal" href="_modules/joy/utils/polytypes.html#CombinatorJoyType"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#joy.utils.polytypes.CombinatorJoyType" title="Permalink to this definition">¶</a></dt>
|
||||
|
|
@ -324,8 +313,8 @@ guard against being used on invalid types.</p>
|
|||
<div>A*</div></blockquote>
|
||||
<p>The <cite>A*</cite> works by splitting the universe into two alternate histories:</p>
|
||||
<blockquote>
|
||||
<div><p>A* -> 0</p>
|
||||
<p>A* -> A A*</p>
|
||||
<div><p>A* → ∅</p>
|
||||
<p>A* → A A*</p>
|
||||
</div></blockquote>
|
||||
<p>The Kleene star variable disappears in one universe, and in the other
|
||||
it turns into an <cite>AnyJoyType</cite> variable followed by itself again.</p>
|
||||
|
|
@ -339,28 +328,6 @@ dicts, the “unifiers”) that don’t lead to type conflicts.</p>
|
|||
|
||||
</dd></dl>
|
||||
|
||||
<dl class="class">
|
||||
<dt id="joy.utils.polytypes.NumberStarJoyType">
|
||||
<em class="property">class </em><code class="descclassname">joy.utils.polytypes.</code><code class="descname">NumberStarJoyType</code><span class="sig-paren">(</span><em>number</em><span class="sig-paren">)</span><a class="reference internal" href="_modules/joy/utils/polytypes.html#NumberStarJoyType"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#joy.utils.polytypes.NumberStarJoyType" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><dl class="attribute">
|
||||
<dt id="joy.utils.polytypes.NumberStarJoyType.kind">
|
||||
<code class="descname">kind</code><a class="headerlink" href="#joy.utils.polytypes.NumberStarJoyType.kind" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>alias of <a class="reference internal" href="#joy.utils.types.NumberJoyType" title="joy.utils.types.NumberJoyType"><code class="xref py py-class docutils literal notranslate"><span class="pre">joy.utils.types.NumberJoyType</span></code></a></p>
|
||||
</dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
||||
<dl class="class">
|
||||
<dt id="joy.utils.polytypes.StackStarJoyType">
|
||||
<em class="property">class </em><code class="descclassname">joy.utils.polytypes.</code><code class="descname">StackStarJoyType</code><span class="sig-paren">(</span><em>number</em><span class="sig-paren">)</span><a class="reference internal" href="_modules/joy/utils/polytypes.html#StackStarJoyType"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#joy.utils.polytypes.StackStarJoyType" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><dl class="attribute">
|
||||
<dt id="joy.utils.polytypes.StackStarJoyType.kind">
|
||||
<code class="descname">kind</code><a class="headerlink" href="#joy.utils.polytypes.StackStarJoyType.kind" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>alias of <a class="reference internal" href="#joy.utils.types.StackJoyType" title="joy.utils.types.StackJoyType"><code class="xref py py-class docutils literal notranslate"><span class="pre">joy.utils.types.StackJoyType</span></code></a></p>
|
||||
</dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
||||
<dl class="class">
|
||||
<dt id="joy.utils.polytypes.SymbolJoyType">
|
||||
<em class="property">class </em><code class="descclassname">joy.utils.polytypes.</code><code class="descname">SymbolJoyType</code><span class="sig-paren">(</span><em>name</em>, <em>sec</em>, <em>number</em><span class="sig-paren">)</span><a class="reference internal" href="_modules/joy/utils/polytypes.html#SymbolJoyType"><span class="viewcode-link">[source]</span></a><a class="headerlink" href="#joy.utils.polytypes.SymbolJoyType" title="Permalink to this definition">¶</a></dt>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,949 @@
|
|||
|
||||
∂RE
|
||||
===
|
||||
|
||||
Brzozowski's Derivatives of Regular Expressions
|
||||
-----------------------------------------------
|
||||
|
||||
Legend:
|
||||
|
||||
::
|
||||
|
||||
∧ intersection
|
||||
∨ union
|
||||
∘ concatenation (see below)
|
||||
¬ complement
|
||||
ϕ empty set (aka ∅)
|
||||
λ singleton set containing just the empty string
|
||||
I set of all letters in alphabet
|
||||
|
||||
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:: ipython2
|
||||
|
||||
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
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
I'm only going to use two symbols (at first) becaase this is enough to
|
||||
illustrate the algorithm and because you can represent any other
|
||||
alphabet with two symbols (if you had to.)
|
||||
|
||||
I chose the names ``O`` and ``l`` (uppercase "o" and lowercase "L") to
|
||||
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 I'm going to use tagged tuples. A *regular
|
||||
expression* is one of:
|
||||
|
||||
::
|
||||
|
||||
O
|
||||
l
|
||||
(KSTAR, R)
|
||||
(NOT, R)
|
||||
(AND, R, S)
|
||||
(CONS, R, S)
|
||||
(OR, R, S)
|
||||
|
||||
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()``
|
||||
~~~~~~~~~~~
|
||||
|
||||
Let's 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
|
||||
|
||||
# This is the straightforward version with no "compaction".
|
||||
# It works fine, but does waaaay too much work because the
|
||||
# expressions grow each derivation.
|
||||
|
||||
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
|
||||
|
||||
Let's 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 you'll see that we never use the "set-ness" of the symbols ``O``
|
||||
and ``l``. The only time Python set operators (``&`` and ``|``) appear
|
||||
is in the ``nully()`` function, and there they operate on (recursively
|
||||
computed) outputs of that function, never ``O`` and ``l``.
|
||||
|
||||
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 1's and not ending in 01 nor composed of all 1's."
|
||||
|
||||
.. figure:: omg.svg
|
||||
:alt: omg.svg
|
||||
|
||||
omg.svg
|
||||
|
||||
Start at ``a`` and follow the transition arrows according to their
|
||||
labels. Accepting states have a double outline. (Graphic generated with
|
||||
`Dot from Graphviz <http://www.graphviz.org/>`__.) You'll see that only
|
||||
paths that lead to one of the accepting states will match the regular
|
||||
expression. All other paths will terminate at one of the non-accepting
|
||||
states.
|
||||
|
||||
There's a happy path to ``g`` along 111:
|
||||
|
||||
::
|
||||
|
||||
a→c→e→g
|
||||
|
||||
After you reach ``g`` you're stuck there eating 1's until you see a 0,
|
||||
which takes you to the ``i→j→i|i→j→h→i`` "trap". You can't reach any
|
||||
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 1's or as many 0' in a
|
||||
row and still be either still at ``h`` (for 1's) or move to ``i`` (for
|
||||
0's). If you find yourself at ``i`` you can see as many 0's, or
|
||||
repetitions of 10, as there are, but if you see just a 1 you move to
|
||||
``j``.
|
||||
|
||||
RE to FSM
|
||||
~~~~~~~~~
|
||||
|
||||
So how do we get the state machine from the regular expression?
|
||||
|
||||
It turns out that each RE is effectively a state, and each arrow points
|
||||
to the derivative RE in respect to the arrow's symbol.
|
||||
|
||||
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`` isn't.
|
||||
|
||||
.. 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)*
|
||||
|
|
@ -468,10 +468,8 @@ Putting it all together:
|
|||
Project Euler Problem Two
|
||||
-------------------------
|
||||
|
||||
::
|
||||
|
||||
By considering the terms in the Fibonacci sequence whose values do not exceed four million,
|
||||
find the sum of the even-valued terms.
|
||||
By considering the terms in the Fibonacci sequence whose values do
|
||||
not exceed four million, find the sum of the even-valued terms.
|
||||
|
||||
Now that we have a generator for the Fibonacci sequence, we need a
|
||||
function that adds a term in the sequence to a sum if it is even, and
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
from notebook_preamble import D, DefinitionWrapper, J, V, define
|
||||
|
||||
Recursive Combinators
|
||||
Recursion Combinators
|
||||
=====================
|
||||
|
||||
This article describes the ``genrec`` combinator, how to use it, and
|
||||
|
|
|
|||
|
|
@ -0,0 +1,337 @@
|
|||
|
||||
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 doesn't *have*
|
||||
to be Boolean because Python's rules for "truthiness" will be used to
|
||||
evaluate it. I reflect this in the structure of the stack effect comment
|
||||
of ``branch``, it will only accept Boolean values, and in the definition
|
||||
of ``ifte`` above by including ``not`` in the quote, which also has the
|
||||
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 couldn't be
|
||||
reimplemented in terms of e.g. Python threads. I am not concerned with
|
||||
performance of the system just yet, only the elegance of the code it
|
||||
allows us to write.
|
||||
|
||||
``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
|
||||
Elliott's "Compiling to Categories" paper, et. al.)
|
||||
|
||||
Just a thought, if you ``cleave`` two jobs and one requires more time to
|
||||
finish than the other you'd like to be able to assign resources
|
||||
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
|
||||
|
||||
(I'm not sure why ``cleave`` was specified to take that value, I may
|
||||
make a combinator that does the same thing but without expecting a
|
||||
value.)
|
||||
|
||||
::
|
||||
|
||||
clv == [i] app2
|
||||
|
||||
[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`` couldn't 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 I'm not going to worry about
|
||||
it. (And I think the Categories can deal with it anyhow? Incremental
|
||||
evaluation, yeah?)
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
|
||||
Type Checking
|
||||
=============
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
import logging, sys
|
||||
|
||||
logging.basicConfig(
|
||||
format='%(message)s',
|
||||
stream=sys.stdout,
|
||||
level=logging.INFO,
|
||||
)
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
from joy.utils.polytypes 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.
|
||||
|
||||
|
|
@ -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->i -->
|
||||
<g id="edge17" class="edge"><title>i->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->j -->
|
||||
<g id="edge18" class="edge"><title>i->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->i -->
|
||||
<g id="edge15" class="edge"><title>h->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->h -->
|
||||
<g id="edge16" class="edge"><title>h->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->b -->
|
||||
<g id="edge1" class="edge"><title>a->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->c -->
|
||||
<g id="edge2" class="edge"><title>a->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->b -->
|
||||
<g id="edge3" class="edge"><title>b->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->d -->
|
||||
<g id="edge4" class="edge"><title>b->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->b -->
|
||||
<g id="edge5" class="edge"><title>c->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->e -->
|
||||
<g id="edge6" class="edge"><title>c->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->b -->
|
||||
<g id="edge7" class="edge"><title>d->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->f -->
|
||||
<g id="edge8" class="edge"><title>d->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->b -->
|
||||
<g id="edge9" class="edge"><title>e->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->g -->
|
||||
<g id="edge10" class="edge"><title>e->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->h -->
|
||||
<g id="edge12" class="edge"><title>f->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->b -->
|
||||
<g id="edge11" class="edge"><title>f->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->i -->
|
||||
<g id="edge13" class="edge"><title>g->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->g -->
|
||||
<g id="edge14" class="edge"><title>g->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->i -->
|
||||
<g id="edge19" class="edge"><title>j->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->h -->
|
||||
<g id="edge20" class="edge"><title>j->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 |
Loading…
Reference in New Issue