426 lines
14 KiB
HTML
426 lines
14 KiB
HTML
<!doctype html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>Generator Programs</title>
|
|
<link rel="stylesheet" href="/css/font/fonts.css">
|
|
<link rel="stylesheet" href="/css/site.css">
|
|
<script src="/Joy.js"></script>
|
|
</head>
|
|
<body>
|
|
<div id="joy_interpreter"></div>
|
|
<h1>Generator Programs</h1>
|
|
<h2>Using <code>x</code> to Generate Values</h2>
|
|
<p>Cf. <a href="https://www.kevinalbrecht.com/code/joy-mirror/jp-reprod.html">Self-reproducing and reproducing programs</a> by Manfred von Thun</p>
|
|
<p>Consider the <code>x</code> combinator:</p>
|
|
<pre><code>x == dup i
|
|
</code></pre>
|
|
<p>We can apply it to a quoted program consisting of some value <code>a</code> and some function <code>B</code>:</p>
|
|
<pre><code>[a B] x
|
|
[a B] dup i
|
|
[a B] [a B] i
|
|
[a B] a B
|
|
</code></pre>
|
|
<p>Let <code>B</code> function <code>swap</code> the <code>a</code> with the quote and run some function <code>C</code> on it to generate a new value <code>b</code>:</p>
|
|
<pre><code>B == swap [C] dip
|
|
</code></pre>
|
|
<p>Evaluation would go like this:</p>
|
|
<pre><code>[a B] a B
|
|
[a B] a swap [C] dip
|
|
a [a B] [C] dip
|
|
a C [a B]
|
|
b [a B]
|
|
</code></pre>
|
|
<p>Now discard the quoted <code>a</code> with <code>rest</code> then <code>cons</code> <code>b</code>:</p>
|
|
<pre><code>b [a B] rest cons
|
|
b [B] cons
|
|
[b B]
|
|
</code></pre>
|
|
<p>Altogether, this is the definition of <code>B</code>:</p>
|
|
<pre><code>B == swap [C] dip rest cons
|
|
</code></pre>
|
|
<h2>An Example</h2>
|
|
<p>We can make a generator for the Natural numbers (0, 1, 2, ...) by using
|
|
<code>0</code> for the initial state <code>a</code> and <code>[dup ++]</code> for <code>[C]</code>.
|
|
We need the <code>dup</code> to leave the old state value behind on the stack.
|
|
Putting it together:</p>
|
|
<pre><code>[0 swap [dup ++] dip rest cons]
|
|
</code></pre>
|
|
<p>Let's try it:</p>
|
|
<pre><code>joy? [0 swap [dup ++] dip rest cons]
|
|
[0 swap [dup ++] dip rest cons]
|
|
|
|
joy? [x]
|
|
[0 swap [dup ++] dip rest cons] [x]
|
|
|
|
joy? trace
|
|
[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 0 • 1 + [0 swap [dup ++] dip rest cons] rest cons
|
|
0 0 1 • + [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] •
|
|
</code></pre>
|
|
<p>After one application of <code>x</code> the quoted program contains 1 and 0 is below it on the stack.</p>
|
|
<pre><code>0 [1 swap [dup ++] dip rest cons]
|
|
</code></pre>
|
|
<p>We can use <code>x</code> as many times as we like to get as many terms as we like:</p>
|
|
<pre><code>joy? x x x x x pop
|
|
0 1 2 3 4 5
|
|
</code></pre>
|
|
<h3><code>direco</code></h3>
|
|
<p>Let's define a helper function:</p>
|
|
<pre><code>[direco dip rest cons] inscribe
|
|
</code></pre>
|
|
<p>That makes our generator quote into:</p>
|
|
<pre><code>[0 swap [dup ++] direco]
|
|
</code></pre>
|
|
<h2>Making Generators</h2>
|
|
<p>We want to define a function that accepts <code>a</code> and <code>[C]</code> and builds our quoted program:</p>
|
|
<pre><code> a [C] G
|
|
-------------------------
|
|
[a swap [C] direco]
|
|
</code></pre>
|
|
<p>Working in reverse:</p>
|
|
<pre><code>[a swap [C] direco] cons
|
|
a [swap [C] direco] concat
|
|
a [swap] [[C] direco] swap
|
|
a [[C] direco] [swap]
|
|
a [C] [direco] cons [swap]
|
|
</code></pre>
|
|
<p>Reading from the bottom up:</p>
|
|
<pre><code>[direco] cons [swap] swap concat cons
|
|
</code></pre>
|
|
<p>Or:</p>
|
|
<pre><code>[direco] cons [swap] swoncat cons
|
|
</code></pre>
|
|
<h3>make-generator</h3>
|
|
<pre><code>[make-generator [direco] cons [swap] swoncat cons] inscribe
|
|
</code></pre>
|
|
<p>Let's try it out:</p>
|
|
<pre><code>joy? 0 [dup ++] make-generator
|
|
[0 swap [dup ++] direco]
|
|
</code></pre>
|
|
<p>And generate some values:</p>
|
|
<pre><code>joy? x x x pop
|
|
0 1 2
|
|
</code></pre>
|
|
<h3>Powers of Two</h3>
|
|
<p>Let's generate powers of two:</p>
|
|
<pre><code>joy? 1 [dup 1 <<] make-generator
|
|
[1 swap [dup 1 <<] direco]
|
|
</code></pre>
|
|
<p>We can drive it using <code>times</code> with the <code>x</code> combinator.</p>
|
|
<pre><code>joy? 10 [x] times pop
|
|
1 2 4 8 16 32 64 128 256 512
|
|
</code></pre>
|
|
<h2>Generating Multiples of Three and Five</h2>
|
|
<p>Look at the treatment of the Project Euler Problem One in the
|
|
<a href="/notebooks/Developing_a_Program.html">Developing a Program</a>
|
|
notebook and you'll see that we might be interested in generating an endless cycle of:</p>
|
|
<pre><code>3 2 1 3 1 2 3
|
|
</code></pre>
|
|
<p>To do this we want to encode the numbers as pairs of bits in a single integer:</p>
|
|
<pre><code>Decimal: 3 2 1 3 1 2 3
|
|
Binary: 11 10 01 11 01 10 11
|
|
</code></pre>
|
|
<p>The number 11100111011011 in binary is 14811 in decimal notation.
|
|
We can recover the terms from this number by using <code>4 divmod</code>.</p>
|
|
<pre><code>joy? 14811 [4 divmod swap] make-generator
|
|
[14811 swap [4 divmod swap] direco]
|
|
|
|
joy? x
|
|
3 [3702 swap [4 divmod swap] direco]
|
|
|
|
joy? x
|
|
3 2 [925 swap [4 divmod swap] direco]
|
|
|
|
joy? x
|
|
3 2 1 [231 swap [4 divmod swap] direco]
|
|
|
|
joy? x
|
|
3 2 1 3 [57 swap [4 divmod swap] direco]
|
|
|
|
joy? x
|
|
3 2 1 3 1 [14 swap [4 divmod swap] direco]
|
|
|
|
joy? x
|
|
3 2 1 3 1 2 [3 swap [4 divmod swap] direco]
|
|
|
|
joy? x
|
|
3 2 1 3 1 2 3 [0 swap [4 divmod swap] direco]
|
|
|
|
joy? x
|
|
3 2 1 3 1 2 3 0 [0 swap [4 divmod swap] direco]
|
|
|
|
joy? x
|
|
3 2 1 3 1 2 3 0 0 [0 swap [4 divmod swap] direco]
|
|
|
|
joy? x
|
|
3 2 1 3 1 2 3 0 0 0 [0 swap [4 divmod swap] direco]
|
|
</code></pre>
|
|
<p>...we get a generator that works for seven cycles before it reaches zero.</p>
|
|
<h3>Reset at Zero</h3>
|
|
<p>We need a function that checks if the int has reached zero and resets it if so.
|
|
That's easy enough to write:</p>
|
|
<pre><code>? [pop 14811] [] branch
|
|
</code></pre>
|
|
<p>I don't like that we're checking every time even though we know we
|
|
only need to reset the integer every seventh time, but this way we
|
|
can include this function in the generator (rather than wrapping the
|
|
generator in something to do it only every seventh iteration.) So
|
|
the "forcing" function is just <code>x</code>.</p>
|
|
<h3><code>PE1.1.check</code></h3>
|
|
<pre><code>[PE1.1.check ? [pop 14811] [] branch] inscribe
|
|
</code></pre>
|
|
<h3><code>PE1.1</code></h3>
|
|
<pre><code>[PE1.1 4 divmod swap] inscribe
|
|
</code></pre>
|
|
<p>Now we can <code>make-generator</code>:</p>
|
|
<pre><code>joy? 14811 [PE1.1.check PE1.1] make-generator
|
|
[14811 swap [PE1.1.check PE1.1] direco]
|
|
</code></pre>
|
|
<p>We can then "force" the generator with <code>x</code> to get as many terms as we like:</p>
|
|
<pre><code>joy? 21 [x] times pop
|
|
3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3
|
|
</code></pre>
|
|
<h3>Run 466 times</h3>
|
|
<p>In the PE1 problem <a href="/notebooks/Developing_a_Program.html">PE1 problem</a>
|
|
we are asked to sum all the multiples of three and five
|
|
less than 1000. It's worked out that we need to use our cycle of seven numbers
|
|
sixty-six times and then four more.</p>
|
|
<pre><code>joy? 7 66 * 4 +
|
|
466
|
|
</code></pre>
|
|
<p>If we drive our generator 466 times and sum the stack we get 999:</p>
|
|
<pre><code>joy? 14811 [PE1.1.check PE1.1] make-generator
|
|
[14811 swap [PE1.1.check PE1.1] direco]
|
|
|
|
joy? 466 [x] times pop enstacken sum
|
|
999
|
|
</code></pre>
|
|
<p>If you want to see how this is used read the
|
|
<a href="/notebooks/Developing_a_Program.html">Developing a Program</a> notebook.</p>
|
|
<h2>A generator for the Fibonacci Sequence.</h2>
|
|
<p>Consider:</p>
|
|
<pre><code>[b a F] x
|
|
[b a F] b a F
|
|
</code></pre>
|
|
<p>The obvious first thing to do is just add <code>b</code> and <code>a</code>:</p>
|
|
<pre><code>[b a F] b a +
|
|
[b a F] b+a
|
|
</code></pre>
|
|
<p>From here we want to arrive at:</p>
|
|
<pre><code>b [b+a b F]
|
|
</code></pre>
|
|
<p>Let's start with <code>swons</code>:</p>
|
|
<pre><code>[b a F] b+a swons
|
|
[b+a b a F]
|
|
</code></pre>
|
|
<p>Considering this quote as a stack:</p>
|
|
<pre><code>F a b b+a
|
|
</code></pre>
|
|
<p>We want to get it to:</p>
|
|
<pre><code>F b b+a b
|
|
</code></pre>
|
|
<p>So:</p>
|
|
<pre><code>F a b b+a popdd over
|
|
F b b+a b
|
|
</code></pre>
|
|
<p>And therefore:</p>
|
|
<pre><code>[b+a b a F] [popdd over] infra
|
|
[b b+a b F]
|
|
</code></pre>
|
|
<p>But we can just use <code>cons</code> to carry <code>b+a</code> into the quote:</p>
|
|
<pre><code>[b a F] b+a [popdd over] cons infra
|
|
[b a F] [b+a popdd over] infra
|
|
[b b+a b F]
|
|
</code></pre>
|
|
<p>Lastly:</p>
|
|
<pre><code>[b b+a b F] uncons
|
|
b [b+a b F]
|
|
</code></pre>
|
|
<p>Putting it all together:</p>
|
|
<pre><code>F == + [popdd over] cons infra uncons
|
|
</code></pre>
|
|
<h3><code>fib-gen</code></h3>
|
|
<p>Let's call <code>F</code> <code>fib-gen</code>:</p>
|
|
<pre><code>[fib-gen + [popdd over] cons infra uncons] inscribe
|
|
</code></pre>
|
|
<p>We can just write the initial quote and then "force" it with <code>x</code>:</p>
|
|
<pre><code>joy? [1 1 fib-gen] 10 [x] times
|
|
1 2 3 5 8 13 21 34 55 89 [144 89 fib-gen]
|
|
</code></pre>
|
|
<p>It skips the first term (1) but if that bothers you you can just prepend it to the program:</p>
|
|
<pre><code>1 [1 1 fib-gen] 10 [x] times
|
|
</code></pre>
|
|
<h2>Project Euler Problem Two</h2>
|
|
<blockquote>
|
|
<p>By considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms.</p>
|
|
</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>pop</code>s it otherwise.</p>
|
|
<h3><code>even</code></h3>
|
|
<pre><code>[even 2 % bool] inscribe
|
|
</code></pre>
|
|
<h3><code>PE2.1</code></h3>
|
|
<pre><code>[PE2.1 dup even [+] [pop] branch] inscribe
|
|
</code></pre>
|
|
<p>And a predicate function that detects when the terms in the series "exceed four million".</p>
|
|
<h3><code>>4M</code></h3>
|
|
<pre><code>[>4M 4000000 >] inscribe
|
|
</code></pre>
|
|
<p>Now it's straightforward to define <code>PE2</code> as a recursive function that generates terms
|
|
in the Fibonacci sequence until they exceed four million and sums the even ones.</p>
|
|
<pre><code>joy? 0 [1 1 fib-gen] x [pop >4M] [popop] [[PE2.1] dip x] tailrec
|
|
4613732
|
|
</code></pre>
|
|
<h3><code>PE2</code></h3>
|
|
<pre><code>[PE2 0 [1 1 fib-gen] x [pop >4M] [popop] [[PE2.1] dip x] tailrec] inscribe
|
|
</code></pre>
|
|
<p>Here's the collected program definitions (with a little editorializing):</p>
|
|
<pre><code>fib-gen + [popdd over] cons infra uncons
|
|
even 2 % bool
|
|
>4M 4000000 >
|
|
PE2.1 dup even [+] [pop] branch
|
|
PE2.2 [PE2.1] dip x
|
|
PE2.init 0 [1 1 fib-gen] x
|
|
PE2.rec [pop >4M] [popop] [PE2.2] tailrec
|
|
PE2 PE2.init PE2.rec
|
|
</code></pre>
|
|
<h3>Hmm...</h3>
|
|
<pre><code>fib-gen + swons [popdd over] infra uncons
|
|
</code></pre>
|
|
<h2>Even-valued Fibonacci Terms</h2>
|
|
<p>Using <code>o</code> for odd and <code>e</code> for even:</p>
|
|
<pre><code>o + o = e
|
|
e + e = e
|
|
o + e = o
|
|
</code></pre>
|
|
<p>So the Fibonacci sequence considered in terms of just parity would be:</p>
|
|
<pre><code>o o e o o e o o e o o e . . .
|
|
1 1 2 3 5 8 13 21 34 55 89 144 . . .
|
|
</code></pre>
|
|
<p>Every third term is even.</p>
|
|
<p>So what if we drive the generator three times and discard the odd terms?
|
|
We would have to initialize our <code>fib</code> generator with 1 0:</p>
|
|
<pre><code>[1 0 fib-gen]
|
|
</code></pre>
|
|
<h3><code>third-term</code></h3>
|
|
<pre><code>[third-term x x x [popop] dipd] inscribe
|
|
</code></pre>
|
|
<p>So:</p>
|
|
<pre><code>joy? [1 0 fib-gen]
|
|
[1 0 fib-gen]
|
|
|
|
joy? third-term
|
|
2 [3 2 fib-gen]
|
|
|
|
joy? third-term
|
|
2 8 [13 8 fib-gen]
|
|
|
|
joy? third-term
|
|
2 8 34 [55 34 fib-gen]
|
|
|
|
joy? third-term
|
|
2 8 34 144 [233 144 fib-gen]
|
|
</code></pre>
|
|
<p>So now we need a sum:</p>
|
|
<pre><code>joy? 0
|
|
0
|
|
</code></pre>
|
|
<p>And our Fibonacci generator:</p>
|
|
<pre><code>joy? [1 0 fib-gen]
|
|
0 [1 0 fib-gen]
|
|
</code></pre>
|
|
<p>We want to generate the initial term:</p>
|
|
<pre><code>joy? third-term
|
|
0 2 [3 2 fib-gen]
|
|
</code></pre>
|
|
<p>Now we check if the term is less than four million,
|
|
if so we add it and recur,
|
|
otherwise we discard the term and the generator leaving the sum on the stack:</p>
|
|
<pre><code>joy? [pop >4M] [popop] [[PE2.1] dip third-term] tailrec
|
|
4613732
|
|
</code></pre>
|
|
<h2>Let's Use Math</h2>
|
|
<p>Consider the Fib seq with algebraic variables:</p>
|
|
<pre><code>a b
|
|
b a+b
|
|
a+b a+b+b
|
|
a+b+b a+a+b+b+b
|
|
</code></pre>
|
|
<p>So starting with <code>(a b)</code> and assuming <code>a</code> is even then the next even term pair is <code>(a+2b, 2a+3b)</code>.</p>
|
|
<p>Reconsider:</p>
|
|
<pre><code>[b a F] x
|
|
[b a F] b a F
|
|
</code></pre>
|
|
<p>From here we want to arrive at:</p>
|
|
<pre><code>(a+2b) [(2a+3b) (a+2b) F]
|
|
</code></pre>
|
|
<p>Let's derive <code>F</code>. We have two values and we want two new values so that's a <code>clop</code>:</p>
|
|
<pre><code>b a F
|
|
b a [F0] [F1] clop
|
|
</code></pre>
|
|
<p>Where <code>F0</code> computes <code>a+2b</code>:</p>
|
|
<pre><code>F0 == over [+] ii
|
|
|
|
b a over [+] ii
|
|
b a b [+] ii
|
|
b a + b +
|
|
b+a b +
|
|
a+2b
|
|
</code></pre>
|
|
<p>And <code>F1</code> computes <code>2a+3b</code>:</p>
|
|
<pre><code>F1 == over [dup + +] ii
|
|
|
|
b a over [dup + +] ii
|
|
b a b [dup + +] ii
|
|
b a dup + + b dup + +
|
|
b a a + + b b + +
|
|
...
|
|
2a+3b
|
|
</code></pre>
|
|
<p>So after that we have</p>
|
|
<pre><code>[b a F] (a+2b) (2a+3b) F'
|
|
|
|
[b a F] b‴ a‴ roll<
|
|
b‴ a‴ [b a F] rrest
|
|
b‴ a‴ [F] [tuck] dip ccons
|
|
b‴ a‴ tuck [F] ccons
|
|
a‴ b‴ a‴ [F] ccons
|
|
a‴ [b‴ a‴ F]
|
|
</code></pre>
|
|
<p>Putting it all together (and deferring factoring) we have:</p>
|
|
<pre><code>F == [over [dup + +] ii] [over [+] ii] clop roll< rrest [tuck] dip ccons
|
|
</code></pre>
|
|
<p>Let's try it out:</p>
|
|
<pre><code>joy? [1 0 [over [dup + +] ii] [over [+] ii] clop roll< rrest [tuck] dip ccons]
|
|
[1 0 [over [dup + +] ii] [over [+] ii] clop roll< rrest [tuck] dip ccons]
|
|
|
|
joy? x
|
|
2 [3 2 [over [dup + +] ii] [over [+] ii] clop roll< rrest [tuck] dip ccons]
|
|
|
|
joy? x
|
|
2 8 [13 8 [over [dup + +] ii] [over [+] ii] clop roll< rrest [tuck] dip ccons]
|
|
|
|
joy? x
|
|
2 8 34 [55 34 [over [dup + +] ii] [over [+] ii] clop roll< rrest [tuck] dip ccons]
|
|
|
|
joy? x
|
|
2 8 34 144 [233 144 [over [dup + +] ii] [over [+] ii] clop roll< rrest [tuck] dip ccons]
|
|
</code></pre>
|
|
<p>And so it goes...</p>
|
|
<h2>Conclusion</h2>
|
|
<p>Generator programs like these are fun and interesting.</p>
|
|
<script>var joy_interpreter = Elm.Main.init({node: document.getElementById('joy_interpreter')});</script>
|
|
</body>
|
|
</html>
|