<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>Tom Moertel's Blog</title>
    <link href="http://blog.moertel.com/xml/atom/feed.xml" rel="self" />
    <link href="http://blog.moertel.com" />
    <id>http://blog.moertel.com/xml/atom/feed.xml</id>
    <author>
        <name>Tom Moertel</name>
        
        <email>tom@moertel.com</email>
        
    </author>
    <updated>2026-07-30T00:00:00Z</updated>
    <entry>
    <title>How I would do the “Bowling Game Kata” in Haskell today</title>
    <link href="http://blog.moertel.com/posts/2026-07-30-how-i-would-do-the-bowling-game-kata-in-haskell-today.html" />
    <id>http://blog.moertel.com/posts/2026-07-30-how-i-would-do-the-bowling-game-kata-in-haskell-today.html</id>
    <published>2026-07-30T00:00:00Z</published>
    <updated>2026-07-30T00:00:00Z</updated>
    <summary type="html"><![CDATA[<div class="info byline">By
  <span itemprop="author" itemscope itemtype="http://schema.org/Person"><a href="https://blog.moertel.com/about.html" itemprop="url"><span itemprop="name">Tom Moertel</span></a></span></div>
<div class="info">Posted on <time itemprop="datePublished" datetime="2026-07-30">July 30, 2026</time></div>
<div class="tags">Tags: <span itemprop="keywords"><a title="All pages tagged &#39;code&#39;." href="/tags/code.html" rel="tag">code</a>, <a title="All pages tagged &#39;comments&#39;." href="/tags/comments.html" rel="tag">comments</a>, <a title="All pages tagged &#39;clean code&#39;." href="/tags/clean%20code.html" rel="tag">clean code</a>, <a title="All pages tagged &#39;good code&#39;." href="/tags/good%20code.html" rel="tag">good code</a>, <a title="All pages tagged &#39;source code&#39;." href="/tags/source%20code.html" rel="tag">source code</a>, <a title="All pages tagged &#39;haskell&#39;." href="/tags/haskell.html" rel="tag">haskell</a>, <a title="All pages tagged &#39;kata&#39;." href="/tags/kata.html" rel="tag">kata</a>, <a title="All pages tagged &#39;bowling&#39;." href="/tags/bowling.html" rel="tag">bowling</a>, <a title="All pages tagged &#39;tdd&#39;." href="/tags/tdd.html" rel="tag">tdd</a></span></div>

<div itemprop="articleBody">
<p>Recently, Philip Schwarz from <a href="https://fpilluminated.org/">fpilluminated.org</a> wrote to let me know about a new slide deck he had published: <a href="https://fpilluminated.org/deck/276">The Bowling Game - From Imperative to Functional Programming - Part 1</a>. In the deck, he revisits the “<a href="http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata">Bowling Game Kata</a>” and summarizes some of the discussion (controversy?) spurred by this exercise over the last two decades. Philip tells this story in a rather clever fashion that blends code, graphics, and comic-book storytelling techniques, so consider checking out the deck.</p>
<p>Anyway, he wrote in particular to kindly let me know that one of the code examples he examines is from my blog, way back when I was learning Haskell in 2006: <a href="../posts/2006-04-05-the-bowling-game-kata-in-haskell.html">The Bowling Game Kata in Haskell</a>. This got me thinking about how I might write this code today. So I revised the code, and here it is. (Be sure to read my rationale for each change, which follows the code.)</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="co">{-</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="co">   My solution to &quot;The Bowling Game Kata&quot;</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="co">   Tom Moertel &lt;tom@moertel.com&gt;</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="co">   Created 2006-04-05. Revised 2026-07-30 to show &quot;production style.&quot;</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="co">   See http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata.</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="co">-}</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a><span class="kw">module</span> <span class="dt">Bowling</span> (scoreGame) <span class="kw">where</span></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Test.HUnit</span></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a><span class="co">-- | Computes the score for a player&#39;s game having a list of `rolls`.</span></span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a><span class="co">--   Uses US Bowling Congress rules: https://bowl.com/keeping-score.</span></span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a><span class="ot">scoreGame ::</span> [<span class="dt">Int</span>] <span class="ot">-&gt;</span> <span class="dt">Int</span></span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a>scoreGame rolls <span class="ot">=</span> go <span class="dv">0</span> <span class="dv">1</span> rolls</span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a>  <span class="kw">where</span></span>
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a><span class="ot">    go ::</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> [<span class="dt">Int</span>] <span class="ot">-&gt;</span> <span class="dt">Int</span></span>
<span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a>    go <span class="op">!</span>score <span class="op">!</span>frame rs <span class="ot">=</span> <span class="kw">case</span> rs <span class="kw">of</span></span>
<span id="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a>        _       <span class="op">|</span> frame <span class="op">==</span> <span class="dv">11</span> <span class="ot">-&gt;</span> score             <span class="co">-- 10th frame ends game.</span></span>
<span id="cb1-22"><a href="#cb1-22" aria-hidden="true" tabindex="-1"></a>        <span class="dv">10</span><span class="op">:</span>rs&#39;                <span class="ot">-&gt;</span> accumFrame <span class="dv">3</span> rs&#39;  <span class="co">-- Strike scores 3 rolls.</span></span>
<span id="cb1-23"><a href="#cb1-23" aria-hidden="true" tabindex="-1"></a>        x<span class="op">:</span>y<span class="op">:</span>rs&#39; <span class="op">|</span> x <span class="op">+</span> y <span class="op">==</span> <span class="dv">10</span> <span class="ot">-&gt;</span> accumFrame <span class="dv">3</span> rs&#39;  <span class="co">-- Spare scores 3 rolls.</span></span>
<span id="cb1-24"><a href="#cb1-24" aria-hidden="true" tabindex="-1"></a>                <span class="op">|</span> <span class="fu">otherwise</span>   <span class="ot">-&gt;</span> accumFrame <span class="dv">2</span> rs&#39;  <span class="co">-- Others score 2 rolls.</span></span>
<span id="cb1-25"><a href="#cb1-25" aria-hidden="true" tabindex="-1"></a>        _                     <span class="ot">-&gt;</span> <span class="fu">error</span> <span class="st">&quot;ill-formed sequence of rolls&quot;</span></span>
<span id="cb1-26"><a href="#cb1-26" aria-hidden="true" tabindex="-1"></a>      <span class="kw">where</span></span>
<span id="cb1-27"><a href="#cb1-27" aria-hidden="true" tabindex="-1"></a>        accumFrame n rs&#39; <span class="ot">=</span> go (score <span class="op">+</span> <span class="fu">sum</span> (<span class="fu">take</span> n rs)) (frame <span class="op">+</span> <span class="dv">1</span>) rs&#39;</span>
<span id="cb1-28"><a href="#cb1-28" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-29"><a href="#cb1-29" aria-hidden="true" tabindex="-1"></a><span class="co">{-</span></span>
<span id="cb1-30"><a href="#cb1-30" aria-hidden="true" tabindex="-1"></a><span class="co">                      *** Unit tests ***</span></span>
<span id="cb1-31"><a href="#cb1-31" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-32"><a href="#cb1-32" aria-hidden="true" tabindex="-1"></a><span class="co">             *Bowling&gt; runTestTT tests</span></span>
<span id="cb1-33"><a href="#cb1-33" aria-hidden="true" tabindex="-1"></a><span class="co">             Cases: 9  Tried: 9  Errors: 0  Failures: 0</span></span>
<span id="cb1-34"><a href="#cb1-34" aria-hidden="true" tabindex="-1"></a><span class="co">-}</span></span>
<span id="cb1-35"><a href="#cb1-35" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-36"><a href="#cb1-36" aria-hidden="true" tabindex="-1"></a>tests <span class="ot">=</span> test</span>
<span id="cb1-37"><a href="#cb1-37" aria-hidden="true" tabindex="-1"></a>    [ <span class="st">&quot;gutters&quot;</span>       <span class="op">~:</span> score  (rep <span class="dv">20</span>  <span class="dv">0</span>)          <span class="op">~?=</span>   <span class="dv">0</span></span>
<span id="cb1-38"><a href="#cb1-38" aria-hidden="true" tabindex="-1"></a>    , <span class="st">&quot;ones&quot;</span>          <span class="op">~:</span> score  (rep <span class="dv">20</span>  <span class="dv">1</span>)          <span class="op">~?=</span>  <span class="dv">20</span></span>
<span id="cb1-39"><a href="#cb1-39" aria-hidden="true" tabindex="-1"></a>    , <span class="st">&quot;fives&quot;</span>         <span class="op">~:</span> score  (rep <span class="dv">22</span>  <span class="dv">5</span>)          <span class="op">~?=</span> <span class="dv">150</span></span>
<span id="cb1-40"><a href="#cb1-40" aria-hidden="true" tabindex="-1"></a>    , <span class="st">&quot;strikes&quot;</span>       <span class="op">~:</span> score  (rep <span class="dv">12</span> <span class="dv">10</span>)          <span class="op">~?=</span> <span class="dv">300</span></span>
<span id="cb1-41"><a href="#cb1-41" aria-hidden="true" tabindex="-1"></a>    , <span class="st">&quot;1 + gutters&quot;</span>   <span class="op">~:</span> score  (<span class="dv">1</span> <span class="op">:</span> rep <span class="dv">19</span> <span class="dv">0</span>)       <span class="op">~?=</span>   <span class="dv">1</span></span>
<span id="cb1-42"><a href="#cb1-42" aria-hidden="true" tabindex="-1"></a>    , <span class="st">&quot;first spare&quot;</span>   <span class="op">~:</span> score  (<span class="dv">5</span><span class="op">:</span><span class="dv">5</span><span class="op">:</span><span class="dv">5</span> <span class="op">:</span> rep <span class="dv">17</span> <span class="dv">0</span>)   <span class="op">~?=</span>  <span class="dv">20</span></span>
<span id="cb1-43"><a href="#cb1-43" aria-hidden="true" tabindex="-1"></a>    , <span class="st">&quot;first strike&quot;</span>  <span class="op">~:</span> score  (<span class="dv">10</span><span class="op">:</span><span class="dv">5</span><span class="op">:</span><span class="dv">5</span> <span class="op">:</span> rep <span class="dv">17</span> <span class="dv">0</span>)  <span class="op">~?=</span>  <span class="dv">30</span></span>
<span id="cb1-44"><a href="#cb1-44" aria-hidden="true" tabindex="-1"></a>    , <span class="st">&quot;last spare&quot;</span>    <span class="op">~:</span> rscore (<span class="dv">5</span><span class="op">:</span><span class="dv">5</span><span class="op">:</span><span class="dv">5</span> <span class="op">:</span> rep <span class="dv">18</span> <span class="dv">0</span>)   <span class="op">~?=</span>  <span class="dv">15</span></span>
<span id="cb1-45"><a href="#cb1-45" aria-hidden="true" tabindex="-1"></a>    , <span class="st">&quot;last strike&quot;</span>   <span class="op">~:</span> rscore (<span class="dv">5</span><span class="op">:</span><span class="dv">5</span><span class="op">:</span><span class="dv">10</span> <span class="op">:</span> rep <span class="dv">18</span> <span class="dv">0</span>)  <span class="op">~?=</span>  <span class="dv">20</span></span>
<span id="cb1-46"><a href="#cb1-46" aria-hidden="true" tabindex="-1"></a>    ]</span>
<span id="cb1-47"><a href="#cb1-47" aria-hidden="true" tabindex="-1"></a>  <span class="kw">where</span></span>
<span id="cb1-48"><a href="#cb1-48" aria-hidden="true" tabindex="-1"></a>    rep    <span class="ot">=</span> <span class="fu">replicate</span></span>
<span id="cb1-49"><a href="#cb1-49" aria-hidden="true" tabindex="-1"></a>    score  <span class="ot">=</span> scoreGame</span>
<span id="cb1-50"><a href="#cb1-50" aria-hidden="true" tabindex="-1"></a>    rscore <span class="ot">=</span> score <span class="op">.</span> <span class="fu">reverse</span>  <span class="co">-- Scores a reversed list of rolls.</span></span></code></pre></div>
<p>Before I dig into the changes and my rationale for them, let me explain my motivating beliefs about code.</p>
<p>First, I believe <a href="../posts/2026-07-23-on-the-nature-and-purpose-of-code.html"><strong>the purpose of code is to instruct a computing device what to do in a form that is easy for the code’s intended audience to understand and maintain</strong></a>.</p>
<p>So who was the intended audience for my 2006 bowling code? Then, I was writing the logic mainly for myself and was golfing the solution somewhat in a challenge to see how simply and compactly I could represent it. But my code’s commentary was for people new to Haskell, which at the time was something of a curiosity among programming communities.</p>
<p>This time around, I am writing my code for Haskell programmers who are reasonably “skilled in the art”: they are competent in the language, its standard libraries, and its idioms. That is, I’m writing the code the way I probably would in a production shop that used Haskell.</p>
<p>I point this out because some of the criticism of the functional implementations that are featured in Philip’s deck focused on the use of recursion, and this criticism stemmed from an (unsupported) assertion that recursion is inherently trickier than iteration. Most Haskell programmers who are reasonably “skilled in the art,” however, will have no problem reading or writing iterative processes, such as the one at the heart of my code above, in terms of recursion. In fact, this subject is covered within <a href="https://mitp-content-server.mit.edu/books/content/sectbyfn/books_pres_0/6515/sicp.zip/full-text/book/book-Z-H-11.html#%_sec_1.2.1">the very first chapter of Abelson and Sussman’s <em>Structure and Interpretation of Computer Programs</em></a>, which was used to teach <em>introductory</em> programming at MIT and elsewhere going back to the 1980s.</p>
<p>Second, a note on the problem itself. Both now and back in 2006 when I wrote the original version of this code, I did not do the “kata” as prescribed. It called for developing the solution via Test Driven Development. I am not a fan of TDD because it asks you to develop your logic through the lens of one failing test at a time, the so-called “red-green-refactor” loop. This narrow lens encourages you to view only a tiny slice of your full design space at a time, making it harder to see and exploit the space’s overall structure in your solution. That said, I remain a fan of testing, just not the TDD dogma.</p>
<p>With that in mind, here are the salient changes I made to the code and my rationale for each. Most of them follow from my change in intended audience:</p>
<ol type="1">
<li>I added a comment saying that the scoring follows the rules published by the United States Bowling Congress (USBC). Whenever your code follows standards or requirements, it is helpful to identify them. That way, if the people reading or maintaining the code have any questions about its logic, they will know how to establish the ground truth.</li>
<li>I removed some comments that explained what the Haskell logic was doing. They made sense for my original code’s Haskell-curious audience, but they don’t for my new code’s audience of more seasoned Haskell programmers.</li>
<li>I renamed a few variables when I thought there was something useful I ought to communicate. The top-level <em>score</em> function became <em>scoreGame</em>; the loop accumulator variables <em>s</em> and <em>f</em> became <em>score</em> and <em>frame</em>. I did not, however, rename <em>rs</em> and <em>rs’</em> representing the rolls before and after the current frame is consumed. Those names are fairly idiomatic and are used ten times in a small block of logic. Renaming them <em>rolls</em> and <em>rolls’</em> would have communicated little more than the current names but would have made that small block of logic unwieldy. I didn’t think the cost was worth the benefit.</li>
<li>I expressed the ten-frame loop in terms of a <em>go</em> function, an idiom which has become common in the two decades since my original version. People unfamiliar with the idiom may believe that calling this function “<em>go</em>” squanders the opportunity to use a name more expressive of the code’s problem domain, but the reality is that <em>go</em> in Haskell is like the <em>for/while/do</em> keywords in other languages: it’s an established way to declare an iterative or recursive process. Calling it something else would have deprived the intended audience of a valuable signal.</li>
<li>I added a type annotation for the <em>go</em> function. This provides a little extra documentation and prevents the <em>frame</em> argument from defaulting to a needlessly large <em>Integer</em> type.</li>
<li>I have added strictness annotations (<code>!</code>) before the <em>score</em> and <em>frame</em> arguments to force their strict evaluation. This ensures that they are always represented by tiny <em>Int</em> values and do not accumulate as a chain of unevaluated additions. (We don’t actually have to worry about this problem for <em>frame</em> because it is compared to 11 on each iteration, forcing its evaluation, but I added the annotation anyway to make its strictness obvious to readers.)</li>
<li>I moved the loop’s termination test into the <em>case</em> expression for slightly improved readability.</li>
<li>I have revised the comments within the code to summarize the USBC rules as they are applied. For example, the comment “Strike scores 3 rolls,” summarizes the official rules: “<em>A strike is marked when you knock down all the pins with your first roll. A strike gives you extra pins as a bonus. You do not add up the score for this frame until you have rolled the ball two more times.</em>” Knowing this rule, for instance, would help a reader to understand why we are passing the argument <code>3</code> into the <em>accumFrame</em> call at this point.</li>
</ol>
<p>I will note that one thing I did <em>not</em> change was the solution’s basic logic. Even though I was golfing a bit on the original 2006 version, I think the form of the solution as it appeared back then was, and still is, a straightforward expression of the rules for scoring a game of bowling.</p>
<p>So that’s how I would write this code today and why. Let me know if you have any questions or concerns with this version.</p>
<p>I would like to thank Philip Schwarz for spurring this discussion. It caused me to think about why I believe the things I do about code and to put those beliefs in writing. (I can’t trust that I understand something unless I can write about it clearly.) If you are interested in reading these writings, <a href="../tags/source%20code.html">on my blog they are tagged with “source code.”</a></p>
</div>
]]></summary>
</entry>
<entry>
    <title>Beyond “Clean Code”: Why Your Comments Matter</title>
    <link href="http://blog.moertel.com/posts/2026-07-27-beyond-clean-code-why-your-comments-matter.html" />
    <id>http://blog.moertel.com/posts/2026-07-27-beyond-clean-code-why-your-comments-matter.html</id>
    <published>2026-07-27T00:00:00Z</published>
    <updated>2026-07-27T00:00:00Z</updated>
    <summary type="html"><![CDATA[<div class="info byline">By
  <span itemprop="author" itemscope itemtype="http://schema.org/Person"><a href="https://blog.moertel.com/about.html" itemprop="url"><span itemprop="name">Tom Moertel</span></a></span></div>
<div class="info">Posted on <time itemprop="datePublished" datetime="2026-07-27">July 27, 2026</time></div>
<div class="tags">Tags: <span itemprop="keywords"><a title="All pages tagged &#39;code&#39;." href="/tags/code.html" rel="tag">code</a>, <a title="All pages tagged &#39;comments&#39;." href="/tags/comments.html" rel="tag">comments</a>, <a title="All pages tagged &#39;clean code&#39;." href="/tags/clean%20code.html" rel="tag">clean code</a>, <a title="All pages tagged &#39;good code&#39;." href="/tags/good%20code.html" rel="tag">good code</a>, <a title="All pages tagged &#39;nature of code&#39;." href="/tags/nature%20of%20code.html" rel="tag">nature of code</a>, <a title="All pages tagged &#39;purpose of code&#39;." href="/tags/purpose%20of%20code.html" rel="tag">purpose of code</a>, <a title="All pages tagged &#39;source code&#39;." href="/tags/source%20code.html" rel="tag">source code</a>, <a title="All pages tagged &#39;definitions&#39;." href="/tags/definitions.html" rel="tag">definitions</a>, <a title="All pages tagged &#39;theory&#39;." href="/tags/theory.html" rel="tag">theory</a>, <a title="All pages tagged &#39;philosophy&#39;." href="/tags/philosophy.html" rel="tag">philosophy</a>, <a title="All pages tagged &#39;engineering culture&#39;." href="/tags/engineering%20culture.html" rel="tag">engineering culture</a></span></div>

<div itemprop="articleBody">
<p>I have argued that <a href="https://blog.moertel.com/posts/2026-07-24-what-makes-good-code-good.html">comments are necessary in good code</a>. My rationale, in short, is that without comments your code’s logic can only express one thing clearly and faithfully: what you <em>actually</em> instructed the computing device to do. But your code’s audience—the people who will read and maintain your code—must also understand what you <em>intended</em> the device to do and the all-important “why” information behind your coding decisions.</p>
<p>To the extent that your programming language lets you express these things naturally in your logic, you should do so. <strong>But don’t contort your logic to accommodate intent and “why” information that would be easier for your audience to understand in natural language.</strong> Just use a comment! That’s what comments are for! <em>To let you speak directly to your audience in plain language.</em></p>
<p>But not everyone agrees. In his book <em>Clean Code</em>, Robert C. Martin (aka Uncle Bob) writes that “comments are always failures”:</p>
<blockquote>
<p>“The proper use of comments is to compensate for our failure to express ourselves in code. Note that I used the word <em>failure</em>. I meant it. Comments are always failures. We must have them because we cannot always figure out how to express ourselves without them, but their use is not a cause for celebration. So when you find yourself in a position where you need to write a comment, think it through and see whether there isn’t some way to turn the tables and express yourself in code.”</p>
</blockquote>
<p>(Note: what Mr. Martin calls <em>code</em> I call <em>logic</em> to distinguish it from <em>commentary</em>.)</p>
<p>I have a hard time swallowing Mr. Martin’s argument because it goes too far.
If he had claimed that comments must be used with care, lest they become a crutch to prop up logic that ought to be rewritten instead, I would have heartily agreed.
But “comments are always failures”?
That admonishment is harmful.
Why?
Because eschewing comments makes code harder to understand and maintain by forcing you to express in logic any ideas that would be more clearly and directly communicated via commentary.</p>
<p>As I have argued above, logic cannot be trusted to express intent: it represents only what the device was <em>actually</em> told to do. If your logic contains an error, was that your intent?</p>
<p>Further, the syntax of logic does not allow for natural language, only a crude and limited approximation of it, and natural language is the, well, natural way to express the all-important “why” information underlying your logic.
That’s why the original source materials for this information are almost always written in natural language. Consider:</p>
<ul>
<li>“We use scoring rules from the United States Bowling Congress: https://bowl.com/keeping-score.”</li>
<li>“Two Newton–Raphson iterations get us close enough to prevent noticeable visual artifacts.”</li>
<li>“We impose a global lock order to prevent deadlock with competing services.”</li>
<li>“Invariant: From this point on the array’s length must be 3N + 1 for some integer N.”</li>
<li>“We use simple brute force here because instances are guaranteed to be small (see guard above).”</li>
<li>“Limit each request to one day’s data; otherwise, we won’t get 1-minute resolution.”</li>
<li>“A binary search using this method of finding the midpoint still runs in O(lg N) expected time because, on average, we’ll be able to discard a quarter of the active range in at most two iterations, limiting the number of iterations to O(log(n)/log(4/3)) = O(log n), just as in the quickselect algorithm with random pivots.”</li>
<li>“We allow inserted rows to replace existing rows for the same device and timestamp because we want to allow for the possibility that the upstream source will revise data, and we prefer the most-recent version of any data we already have.”</li>
<li>“Repack the weighted pairs into a single array of blocks, each having a weight of exactly the mean and each representing one or two of the original values.”</li>
</ul>
<p>In production code, this kind of information is often the most important. How would you communicate it clearly without using comments? Should you name your function <em>ScoreAGameOfBowlingAccordingToTheUnitedStatesBowlingCongressRulesAtBowlDotComSlashKeepingHyphenScore</em>? To communicate that an array-length invariant holds for the second half of your algorithm’s logic, should you exile that half to a new function you invented solely to let you name its argument <em>array_having_length_3n_plus_1_for_some_integer_n</em>? Does breaking the natural flow of your algorithms to invent naming sites make your code clean? Does using unwieldy names instead of natural language make your code clean?</p>
<p>Or contorted?</p>
<p>To be clear, <strong>I am emphatically not arguing against expressing intent and “why” information in logic</strong>. I believe that you should express them in your code’s logic to the extent you reasonably can <em>without contorting your logic</em>.</p>
<p>If you ignore the names in your logic and just look at its structure, is that structure a simple, natural expression of your solution and the problem domain? If it is anything more than that, that additional complexity is probably contorting your logic. Why did you contort your logic? Was it to create naming sites where you could insert words as a crude, limited approximation of natural language? Maybe don’t do that. Maybe just use a comment instead.</p>
<p>Everything in your code, both logic and commentary, should communicate your intent as clearly <em>as it reasonably allows.</em> So please do structure your logic in a way that’s natural to your problem domain and please do choose informative (but not unwieldy) names. But if there’s more to communicate—and there usually ought to be—don’t contort your logic to shoehorn it in. Accept that logic, as a medium for communicating intent and “why” information, is limited. Don’t try to make it do things it’s not good at. Just use a comment instead.</p>
<h1 id="discussion">Discussion</h1>
<p>Let me address some of the common objections to using comments.</p>
<h4 id="what-about-the-idea-that-comments-can-lie-if-i-never-use-comments-what-my-code-says-it-does-and-what-it-actually-does-will-always-be-the-same-right-isnt-that-better">What about the idea that “comments can lie”? If I never use comments, what my code says it does and what it actually does will always be the same, right? Isn’t that better?</h4>
<p>No, it’s worse. Consider the possibilities when a comment disagrees with its associated logic: (1) the comment is wrong, (2) the logic is wrong, or (3) both are wrong. In case (1), the code at least still does what it should, but in cases (2) and (3) the code will do the wrong thing—oops! launched the missiles—and that’s a problem in the real world. The good news is that you at least know there is a problem and can investigate, thanks to the red flag raised by the comment and logic disagreeing, a red flag that AI agents are pretty good at noticing. Now imagine the same code but without the comments. Now there’s no red flag to draw your attention while coding or during code reviews. In case (1), there’s no real-world impact, but in cases (2) and (3) your code will happily continue to do the wrong thing while you remain unaware.</p>
<h4 id="dont-comments-go-stale-programmers-sometimes-forget-to-update-their-comments-when-changing-their-logic-dont-they-isnt-it-better-then-to-avoid-comments-and-eliminate-this-problem">Don’t comments go stale? Programmers sometimes forget to update their comments when changing their logic, don’t they? Isn’t it better, then, to avoid comments and eliminate this problem?</h4>
<p>No, it’s worse (see the answer above). Further, let’s think this idea through. As I hope I have argued convincingly, comments are essential: they are the most effective means of communicating all-important “why” information to your code’s audience. So when an organization doesn’t value comments enough to keep them up to date, what does this neglect say about its engineering culture? If you’re concerned about comments going stale, improve your engineering culture (for example, through code reviews). But don’t eliminate comments! That will only reinforce a broken culture <em>and</em> make it harder for your audience to understand and maintain your code.</p>
<h4 id="arent-comments-a-crutch-to-prop-up-bad-code-arent-they-a-code-smell">Aren’t comments a crutch to prop up bad code? Aren’t they a “code smell”?</h4>
<p>It is true that if you have confusing logic, you can make it more understandable by adding comments. And, for this reason, confusing logic is often propped up by supporting comments. But that just shows that comments are effective: they’re good at conveying useful information to your audience. If you add good comments to bad code, the bad code gets better. Likewise, if you add good comments to good code, the good code gets better. So write good code <em>and</em> good comments.</p>
<p>But don’t believe for a second that you’ve made your code better if you have contorted its logic to eliminate helpful comments. Use your good sense:</p>
<ul>
<li>Is your code’s logic as simple and straightforward as you can reasonably make it? <em>Then leave it alone.</em></li>
<li>Does your code have comments that communicate useful things to your intended audience? <em>Then leave them alone.</em></li>
<li>Are there additional things that your audience should know? <em>Then add comments to tell them</em>.</li>
</ul>
<h1 id="a-challenge-if-you-disagree">A challenge if you disagree</h1>
<p>If you think I’m mistaken about the value of comments or the limitations of logic as a medium for expressing intent and “why” information, then consider this <a href="https://github.com/tmoertel/practice/blob/master/libraries/discrete_random_variables.py">short Python library that implements discrete random variables</a>. Kindly show me how to eliminate its internal comments without discarding useful information or contorting its logic.</p>
<p>If you don’t like Python but want to try the challenge, just let me know in a comment or email, and I will suggest some code in the language of your choice.</p>
<h1 id="further-discussion">Further discussion</h1>
<p>Check out the further discussion on <a href="https://news.ycombinator.com/item?id=49075915">Hacker News</a>.</p>
</div>
]]></summary>
</entry>
<entry>
    <title>What Makes Good Code Good</title>
    <link href="http://blog.moertel.com/posts/2026-07-24-what-makes-good-code-good.html" />
    <id>http://blog.moertel.com/posts/2026-07-24-what-makes-good-code-good.html</id>
    <published>2026-07-24T00:00:00Z</published>
    <updated>2026-07-24T00:00:00Z</updated>
    <summary type="html"><![CDATA[<div class="info byline">By
  <span itemprop="author" itemscope itemtype="http://schema.org/Person"><a href="https://blog.moertel.com/about.html" itemprop="url"><span itemprop="name">Tom Moertel</span></a></span></div>
<div class="info">Posted on <time itemprop="datePublished" datetime="2026-07-24">July 24, 2026</time></div>
<div class="tags">Tags: <span itemprop="keywords"><a title="All pages tagged &#39;code&#39;." href="/tags/code.html" rel="tag">code</a>, <a title="All pages tagged &#39;comments&#39;." href="/tags/comments.html" rel="tag">comments</a>, <a title="All pages tagged &#39;clean code&#39;." href="/tags/clean%20code.html" rel="tag">clean code</a>, <a title="All pages tagged &#39;good code&#39;." href="/tags/good%20code.html" rel="tag">good code</a>, <a title="All pages tagged &#39;nature of code&#39;." href="/tags/nature%20of%20code.html" rel="tag">nature of code</a>, <a title="All pages tagged &#39;purpose of code&#39;." href="/tags/purpose%20of%20code.html" rel="tag">purpose of code</a>, <a title="All pages tagged &#39;source code&#39;." href="/tags/source%20code.html" rel="tag">source code</a>, <a title="All pages tagged &#39;definitions&#39;." href="/tags/definitions.html" rel="tag">definitions</a>, <a title="All pages tagged &#39;theory&#39;." href="/tags/theory.html" rel="tag">theory</a>, <a title="All pages tagged &#39;philosophy&#39;." href="/tags/philosophy.html" rel="tag">philosophy</a></span></div>

<div itemprop="articleBody">
<p>Recall that <a href="../posts/2026-07-23-on-the-nature-and-purpose-of-code.html"><strong>the purpose of code is to instruct a computing device what to do in a form that is easy for the code’s intended audience to understand and maintain</strong></a>. Therefore, good code not only makes the device do what is wanted but also communicates three things:</p>
<ol type="1">
<li>what the device was instructed to do,</li>
<li>what the code’s authors <em>intended</em> the device to do, and</li>
<li>why the code was written in the way that it was.</li>
</ol>
<p>This last part—the “why”—is crucial. It includes domain knowledge, design and coding choices, and the rationale for those choices. Without this “why” knowledge, code becomes harder for its audience to understand and maintain, especially in organizations with many programmers and large code repositories.</p>
<p>Because logic is an unwieldy and sometimes unreliable vehicle for communicating anything but device instruction, good code usually communicates intent and “why” information at least partially via commentary.</p>
<h1 id="discussion">Discussion</h1>
<p>Let me defend some of the nuances and implications of my claims above.</p>
<h4 id="why-should-good-code-communicate-both-1-what-the-device-was-instructed-to-do-and-2-what-the-codes-authors-intended-the-device-to-do-in-good-code-shouldnt-they-be-the-same">Why should good code communicate both (1) “what the device <em>was</em> instructed to do” and (2) “what the code’s authors <em>intended</em> the device to do”? In good code, shouldn’t they be the same?</h4>
<p>The reason that good code must communicate both (1) and (2) is that its audience must understand the authors’ <em>intent</em> for the code, but the code’s logic can only express what the device was <em>actually</em> instructed to do. Ideally, the logic would align with the intent, but there are many cases when it cannot or does not, for instance when the logic is buggy or when the logic by choice expresses an approximation of the intent (e.g., fast math logic in game engines). For this reason, good code communicates the authors’ intent directly to the intended audience, usually via commentary.</p>
<h4 id="why-are-comments-necessary-in-good-code">Why are comments necessary in good code?</h4>
<p>The reason that comments are necessary in good code is twofold. First, the code’s logic cannot be trusted to express what the code’s authors intended the code to do. Logic can only express what the computing device was <em>actually</em> instructed to do. Without comments to communicate intent to the audience directly, bugs and approximations can be mistaken for the author’s ultimate intent.</p>
<p>Second, natural languages such as English are vastly more expressive than what you can shoehorn into a programming language’s logic. When you need to communicate a requirement, design decision, or other domain knowledge to your code’s audience, what is the most effective means? There is a reason that the very sources of this knowledge—requirements documents, design documents, standards, textbooks, and so on—are written in natural language. For the same reason, you probably ought to use natural language when you communicate these things in your code. And since you can’t use natural language except in comments, good code usually has comments.</p>
</div>
]]></summary>
</entry>
<entry>
    <title>On the Nature and Purpose of Code</title>
    <link href="http://blog.moertel.com/posts/2026-07-23-on-the-nature-and-purpose-of-code.html" />
    <id>http://blog.moertel.com/posts/2026-07-23-on-the-nature-and-purpose-of-code.html</id>
    <published>2026-07-23T00:00:00Z</published>
    <updated>2026-07-23T00:00:00Z</updated>
    <summary type="html"><![CDATA[<div class="info byline">By
  <span itemprop="author" itemscope itemtype="http://schema.org/Person"><a href="https://blog.moertel.com/about.html" itemprop="url"><span itemprop="name">Tom Moertel</span></a></span></div>
<div class="info">Posted on <time itemprop="datePublished" datetime="2026-07-23">July 23, 2026</time></div>
<div class="tags">Tags: <span itemprop="keywords"><a title="All pages tagged &#39;code&#39;." href="/tags/code.html" rel="tag">code</a>, <a title="All pages tagged &#39;good code&#39;." href="/tags/good%20code.html" rel="tag">good code</a>, <a title="All pages tagged &#39;nature of code&#39;." href="/tags/nature%20of%20code.html" rel="tag">nature of code</a>, <a title="All pages tagged &#39;purpose of code&#39;." href="/tags/purpose%20of%20code.html" rel="tag">purpose of code</a>, <a title="All pages tagged &#39;source code&#39;." href="/tags/source%20code.html" rel="tag">source code</a>, <a title="All pages tagged &#39;definitions&#39;." href="/tags/definitions.html" rel="tag">definitions</a>, <a title="All pages tagged &#39;theory&#39;." href="/tags/theory.html" rel="tag">theory</a>, <a title="All pages tagged &#39;philosophy&#39;." href="/tags/philosophy.html" rel="tag">philosophy</a></span></div>

<div itemprop="articleBody">
<p>Recently, I came across some programming advice that I disagreed with. When I thought about why I disagreed with it, I realized that the argument forming in my head followed from my foundational beliefs about code. So I thought it would be a useful exercise to set those thoughts down as clearly as I could. This post is my vehicle for the first part of that exercise.</p>
<p>Please let me know if you think I got anything wrong.</p>
<p>Ready? Let’s go!</p>
<h1 id="what-is-code">What is code?</h1>
<p><em><strong>Code</strong> (or <strong>source code</strong>) is a formal, typically textual document written in a programming language and having two parts: the first part is <strong>logic</strong>, which instructs a computing device what to do in the language’s syntax and according to the language’s semantics; and the second part is <strong>commentary</strong>, which communicates important information directly to the code’s audience; the combined purpose of these being to instruct a computing device what to do in a form that is easy for the code’s intended audience to understand and maintain.</em></p>
<h1 id="what-is-the-purpose-of-code">What is the purpose of code?</h1>
<p>If you disagree with me about what makes for good code (a <a href="../posts/2026-07-24-what-makes-good-code-good.html">subject for a later post</a>), the best place to make your assault on my argument is probably going to be in the <em>purpose</em> part of my definition of <em>code</em>, upon which my conclusions rest. To recap:</p>
<p><strong>The purpose of code is to instruct a computing device what to do in a form that is easy for the code’s intended audience to understand and maintain.</strong></p>
<p>Most people will find the first half of my claim uncontroversial: code has the purpose of instructing a computing device what to do. If we want computing devices to do things, we must tell them what to do <em>somehow</em>. Since the “we” in question is generally humans—usually the humans tasked with the creation and maintenance of the code in question—it follows that however we tell the devices what to do, it must be in a form that is easy for this audience to understand and maintain. This is the rationale for the second half of my claim.</p>
<h1 id="discussion">Discussion</h1>
<p>To clarify my definitions, let’s discuss points that may not be obvious. If you have any questions I haven’t covered, just drop a comment below or send me an email.</p>
<h4 id="what-do-you-mean-by-the-intended-audience-for-a-work-of-code">What do you mean by the “intended audience” for a work of code?</h4>
<p><strong>By “intended audience,” I mean the people (and AI agents) who are expected to read and/or maintain the code over its expected lifecycle.</strong> The intended audience for a work can therefore vary markedly, depending on the situation.</p>
<p><strong>In professional settings, the audience is generally assumed to be programmers who are “skilled in the art”</strong>: they are competent in the programming languages, libraries, and idioms in use, and they have a working knowledge of their organization’s software development practices. That is, they are expected to have already internalized the <em>common knowledge</em> needed to work effectively within their organizations.</p>
<p>For example, we would expect the intended audience for C++ code at Google to have a working understanding of C++, Google’s C++ Style Guide, and Google’s practices for code reviews and source-code management. This assumption allows C++ code to be written across the company efficiently because it eliminates the burden of having to re-explain these common foundations with each new work of code. These common parts have been “factored out” and placed into the organization’s engineering culture, where all projects can share them.</p>
<h4 id="you-say-that-code-instructs-a-computing-device-what-to-do-but-how-does-this-definition-apply-to-code-in-declarative-languages-like-prolog-and-sql-where-you-just-supply-constraints-and-let-the-computer-figure-out-what-to-do">You say that “code instructs a computing device what to do,” but how does this definition apply to code in declarative languages like Prolog and SQL, where you just supply constraints and let the computer figure out what to do?</h4>
<p>I think the definitions work just fine for this case. Recall that code “instructs a computing device what to do in the language’s syntax and <strong>according to the language’s semantics</strong>.” When you define facts and relations in Prolog and then run a query, for example, the language’s semantics say that you are instructing the computing device to search for a resolution refutation of the negated query. You haven’t supplied a literal series of do-this-do-that instructions, but you are nonetheless instructing the computing device to search on your behalf. The same argument generally applies to other declarative programming languages.</p>
</div>
]]></summary>
</entry>
<entry>
    <title>When Google Takeout Fails</title>
    <link href="http://blog.moertel.com/posts/2025-08-27-when-takeout-fails.html" />
    <id>http://blog.moertel.com/posts/2025-08-27-when-takeout-fails.html</id>
    <published>2025-08-27T00:00:00Z</published>
    <updated>2025-08-27T00:00:00Z</updated>
    <summary type="html"><![CDATA[<div class="info byline">By
  <span itemprop="author" itemscope itemtype="http://schema.org/Person"><a href="https://blog.moertel.com/about.html" itemprop="url"><span itemprop="name">Tom Moertel</span></a></span></div>
<div class="info">Posted on <time itemprop="datePublished" datetime="2025-08-27">August 27, 2025</time></div>
<div class="tags">Tags: <span itemprop="keywords"><a title="All pages tagged &#39;google&#39;." href="/tags/google.html" rel="tag">google</a>, <a title="All pages tagged &#39;takeout&#39;." href="/tags/takeout.html" rel="tag">takeout</a>, <a title="All pages tagged &#39;backups&#39;." href="/tags/backups.html" rel="tag">backups</a>, <a title="All pages tagged &#39;data&#39;." href="/tags/data.html" rel="tag">data</a></span></div>

<div itemprop="articleBody">
<p>When Google Takeout fails, you’re basically on your own. Here are some quick tips for getting your data when things go wrong.</p>
<p>Oh, you tried to download your precious data from a bunch of Google products and days later you got an email with the subject “We couldn’t create a copy of your Google data”? Surely, for something as important as this, Google is going to Respect the User and provide meaningful help and options for moving forward.</p>
<p>Joke’s on you. Because what Google actually provides is nothing. Seriously, the email says only that “Something went wrong and we couldn’t create a copy of your Google data. You can try to create a new export.”</p>
<p>Well, that’s helpful.</p>
<p>What else can we try? It turns out that you can glean <em>some</em> information by going to <a href="https://takeout.google.com/manage">takeout.google.com/manage</a> and clicking on the failing export to reveal this screen:</p>
<p><img src="data:image/png;base64,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iKryytVoNBolaxNkrArly2triU2FVUY2potVzFxkMBj0a0fKlikL63JzvZQFBATA18fXXHTXn2LNkb17ua2h36uvYsXKlRBXq1woaskS+cB9Ne7TnxIZDAaIVc7Pz0+yOYLBYICsJy8xJNcV/jk6FlAg/cS9aTDcEjLSXP75q27PdNMMJV/cQcSTrL24r8PxSYAESIAEchJwylnEkpwEWHIvEpgyeTLkn6nq2Lmzdu3uid6Lme/P0JY3e+QhLmXuDbPHO8M5kQAJkEDRE3BYATejd2UkrqiPvCxbRY+SIzoaAdnPJu9bmz9vHuYr9+6yqCWoWLGioy2T6yEBEihOAhybBIqJgMMKOGflezTegcu0mPhyWAcgUKlSJe1iNhiy3JUOsCwugQRIgARIoBQTcFgBV4rvCadOAkVJgGORAAmQAAk4IAEKOAe8qVwSCZAACZAACZCAYxMofgHn2Py4OhIgARIgARIgARIocQIUcCWOnBckARIgARIoDAG2IQESyJsABVzebFhDAiRAAiRAAiRAAnZJgALOLm8LJ2UfBDgLEiABEiABErBPAhRw9nlfOCsSIAESIAESIIHSSqAE5k0BVwKQeQkSIAESIAESIAESKEoCFHBFSZNjkQAJkIB9EOAsSIAEHJwABZyD32AujwRIgARIgARIwPEIUMA53j21jxVxFiRAAiRAAiRAAsVGgAKu2NByYBIgARIgARIggTslwPaFI0ABVzhObEUCJEACJEACJEACdkOAAs5ubgUnQgIkYB8EOAsSIAESsH8CFHD2f484QxIgARIgARIgARKwIUABZ4PDPjKcBQmQAAmQAAmQAAnkR4ACLj86rCMBEiABEiCB0kOAM72HCFDA3UM3m0slARIgARIgARJwDAIUcI5xH7kKErAPApwFCZAACZBAiRCggCsRzLwICZAACZAACZAACRQdAUcTcEVHhiORAAmQAAmQAAmQgJ0SoICz0xvDaZEACZAACZQkAV6LBEoXAQq40nW/OFsSIAESIAESIAESAAUcHwISsBMCnAYJkAAJkAAJFJYABVxhSbEdCZAACZAACZAACdgJASsBZycz4jRIgARIgARIgARIgATyJUABly8eVpIACZAACRRIgA1IgARKnAAFXIkj5wVJgARIgARIgARI4O4IUMDdHT/2tg8CnAUJkAAJkAAJ3FME7lrAGe4pXFwsCZAACZAACZCA4xAo7pXcRHHppLsWcM5GF6RnpBc3AY5PAiRAAiRAAiRAAqWKQGZmJpycnYtlznct4Nzd3HHtWkqxTI6DkgAJkICjE+D6SIAEHJdAWloaTEZTsSzwrgWcm6sbbt68idS01GKZIAclARIgARIgARIggdJG4GrKVTg7u8BoNBbL1O9awMmsfLx9cenSRUkylDoCnDAJkAAJkAAJkEBRE0hKSoSfr19RD2sZr0gEnKenpzYRJpyPtwzMBAmQAAmQAAmQgAMT4NLyJHAu/iw8Pb3g6uqaZ5u7rSgSASeTKFOmLFxcjIg7E8s9cQKEgQRIgARIgARI4J4icOXqFa2DPDw84evjW6xrLzIBZzAYUMa/DAICyiEpOUkvQBRofMI5MJABnwE+A8XwDPD/Lfz/K58BPgN28QyI3hEDVkrKVZQvVwE+3j7FKt5k8CITcDKYBFeTKwIrVNShjH9Z+Pn6M5ABnwE+A3wG+AzwGeAz4LDPgOgd0T4i3orr0IJoLOtQ5ALOPLizszNMJpP2/4oP2CGD8m1zXa68x3wO+AzwGeAzwGfgnn4GRO+I7kEJ/ik2AVeCa+ClSIAESIAESKBUEeBkSeBuCVDA3S1B9icBEiABEiABEiCBEiZAAVfCwHk5ErAPApwFCZAACZBAaSZAAVea7x7nTgIkQAIkQAIkcE8S+D8AAAD//2aIxuMAAAAGSURBVAMAN68igq2+FQcAAAAASUVORK5CYII=" /></p>
<p>Which of the 22 products failed? Who the f— knows. Google isn’t saying.</p>
<p>What? Did you think that clicking on the “22 products” link would tell you something useful about the failure? Nope. It just lists all 22 products that were in the failed attempt. What about the “Learn more” link? It just takes you to the generic help for Takeout failures, which again boils down to “try again.”</p>
<p>If you want to figure out which products you can’t get your data from, you’ll have to do it the old fashioned way: Binary Search.</p>
<p>I know, I know. It’s absolutely, mind bogglingly absurd that you should have to waste days making repeated Takeout attempts to identify what products are failing you. All because Google’s leadership doesn’t prioritize Takeout—or users—enough to make sure that Takeout actually works reliably.</p>
<p>(Note that I am criticizing Google’s <em>leadership</em>, not the heroic folks within Google that do yeoman’s work on Takeout, despite the lack of prioritization.)</p>
<p>Fortunately, there is a trick to make the binary search slightly easier. If you click the red “Start new request” button on this particular error screen, the UI for starting a new request will be limited to just the products that you tried to include in the failed attempt. (As far as I can tell, this semi-helpful feature is completely undocumented.) That means you can run the binary search as follows:</p>
<ol type="1">
<li>When a Takeout request fails, go to <a href="https://takeout.google.com/manage">takeout.google.com/manage</a>, find the failed attempt, and click on it. You will see a screen like the one above.</li>
<li>Now, right click the “Start new request” button and select “Open Link in New Tab” in your web browser. In that new tab, configure a new Takeout request but, crucially, <em>uncheck all but the <strong>first</strong> half of the listed products</em>. Submit the request and close the tab.</li>
<li>That will take you back to the previous tab. Click the “Start new request” button again. This time, <em>uncheck all but the <strong>second</strong> half of the listed products</em>. Submit the request.</li>
<li>Now wait. Hours. Maybe days.</li>
<li>If all the Takeout requests succeed, great, you’re done.</li>
<li>Otherwise, for each failing attempt, go back to Step 1 and split that failed attempt into two half-sized attempts by repeating the procedure above.</li>
<li>Eventually, you will narrow the failures down to the individual products that you can’t get your data from. For these products, you can try changing the Takeout settings (if offered) or try to escalate to someone you know within Google who might help. If you’re famous, you can try the Twitter route.</li>
</ol>
<p>Good luck!</p>
</div>
]]></summary>
</entry>
<entry>
    <title>Sampling with SQL</title>
    <link href="http://blog.moertel.com/posts/2024-08-23-sampling-with-sql.html" />
    <id>http://blog.moertel.com/posts/2024-08-23-sampling-with-sql.html</id>
    <published>2024-08-23T00:00:00Z</published>
    <updated>2024-08-23T00:00:00Z</updated>
    <summary type="html"><![CDATA[<div class="info byline">By
  <span itemprop="author" itemscope itemtype="http://schema.org/Person"><a href="https://blog.moertel.com/about.html" itemprop="url"><span itemprop="name">Tom Moertel</span></a></span></div>
<div class="info">Posted on <time itemprop="datePublished" datetime="2024-08-23">August 23, 2024</time></div>
<div class="tags">Tags: <span itemprop="keywords"><a title="All pages tagged &#39;sampling&#39;." href="/tags/sampling.html" rel="tag">sampling</a>, <a title="All pages tagged &#39;sql&#39;." href="/tags/sql.html" rel="tag">sql</a>, <a title="All pages tagged &#39;probability&#39;." href="/tags/probability.html" rel="tag">probability</a>, <a title="All pages tagged &#39;poisson process&#39;." href="/tags/poisson%20process.html" rel="tag">poisson process</a>, <a title="All pages tagged &#39;exponential distribution&#39;." href="/tags/exponential%20distribution.html" rel="tag">exponential distribution</a>, <a title="All pages tagged &#39;statistics&#39;." href="/tags/statistics.html" rel="tag">statistics</a></span></div>

<div itemprop="articleBody">
<p>Sampling is one of the most powerful tools you can wield to extract meaning from large datasets.
It lets you reduce a massive pile of data into a small yet representative dataset that’s fast and easy to use.</p>
<p>If you know how to take samples using SQL, the ubiquitous query language, you’ll be able to take samples anywhere.
No dataset will be beyond your reach!</p>
<p>In this post, we’ll look at some clever algorithms for taking samples.
These algorithms are fast and easily translated into SQL.</p>
<p>First, however, I’ll note that many database systems have some built-in support for taking samples.
For example, some SQL dialects support a <code>TABLESAMPLE</code> clause.
If your system has built-in support—and it does what you need—using it will usually be your best option.</p>
<p>Often, though, the built-in support is limited to simple cases.
Let’s consider some realistic scenarios that are more challenging:</p>
<ul>
<li>We want to be able to take samples <em>with</em> and <em>without</em> replacement.</li>
<li>We want to take <em>weighted</em> samples in which each item in the input dataset is selected with probability in proportion to its corresponding weight.</li>
<li>We want to support the full range of weights we might expect to see in a FAANG-sized dataset, say between <span class="math inline">\(0\)</span> to <span class="math inline">\(10^{20}\)</span> for frequency distributions (e.g., clicks or impressions or RPC events) and between <span class="math inline">\(0\)</span> to <span class="math inline">\(1\)</span> with values as small as <span class="math inline">\(10^{-20}\)</span> for normalized probability distributions. In other words, weights are non-negative numbers, possibly very large or very small.</li>
<li>We want to take deterministic samples. This property lets us take repeatable samples and, in some cases, helps query planners produce faster queries.</li>
</ul>
<h2 id="sampling-without-replacement-the-a-es-algorithm-in-sql">Sampling without replacement: the A-ES algorithm in SQL</h2>
<p>In 2006, Pavlos S. Efraimidis and Paul G. Spirakis published a one-pass algorithm for drawing a weighted random sample, without replacement, from a population of weighted items.
It’s quite simple:</p>
<p>Given a population <span class="math inline">\(V\)</span> indexed by <span class="math inline">\(i = 1\ldots{}n\)</span> and having weights <span class="math inline">\(w_i\)</span>:</p>
<ol type="1">
<li>For each <span class="math inline">\(v_i\)</span> in <span class="math inline">\(V,\)</span> let <span class="math inline">\(u_i = \mathrm{random}(0, 1)\)</span> and <span class="math inline">\(k_i = u_i^{1/w_i}\)</span>.</li>
<li>Select the <span class="math inline">\(m\)</span> items with the largest keys <span class="math inline">\(k_i\)</span>.</li>
</ol>
<p>That algorithm has a straightforward implementation in SQL:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode sql"><code class="sourceCode sql"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">SELECT</span> <span class="op">*</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="kw">FROM</span> Population</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="kw">WHERE</span> weight <span class="op">&gt;</span> <span class="dv">0</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="kw">ORDER</span> <span class="kw">BY</span> <span class="op">-</span><span class="fu">LN</span>(<span class="fl">1.0</span> <span class="op">-</span> <span class="kw">RANDOM</span>()) <span class="op">/</span> weight</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="kw">LIMIT</span> <span class="dv">100</span>  <span class="co">-- Sample size.</span></span></code></pre></div>
<p>You’ll note that we changed the ordering logic a bit.
A straight translation would have been</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode sql"><code class="sourceCode sql"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">ORDER</span> <span class="kw">BY</span> POW(<span class="kw">RANDOM</span>(), <span class="fl">1.0</span> <span class="op">/</span> weight) <span class="kw">DESC</span></span></code></pre></div>
<p>Our translation</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode sql"><code class="sourceCode sql"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">ORDER</span> <span class="kw">BY</span> <span class="op">-</span><span class="fu">LN</span>(<span class="fl">1.0</span> <span class="op">-</span> <span class="kw">RANDOM</span>()) <span class="op">/</span> weight</span></code></pre></div>
<p>is more numerically stable and also helps to show the connection between the algorithm and the fascinating theory of Poisson processes.
This connection makes it easier to understand how the algorithm works.
More on that in a moment.</p>
<h2 id="numerical-stability-and-other-tweaks">Numerical stability and other tweaks</h2>
<p>First, the numerical stability claim.
Assume that our SQL system uses <a href="https://en.wikipedia.org/wiki/IEEE_754-1985#Double_precision">IEEE double-precision floating-point values</a> under the hood.
When the weights are large, say on the order of <span class="math inline">\(w_i = 10^{17}\)</span>, it doesn’t matter what the random value <span class="math inline">\(u_i\)</span> is.
The corresponding sort key <span class="math inline">\(k_i = u_i^{1/w_i}\)</span> will almost always be <span class="math inline">\(1.0\)</span>.
Consider the interval <span class="math inline">\(0.01 \le u_i \le 1\)</span>, representing 99% of the possible random values <span class="math inline">\(u_i\)</span>.
This entire interval gets mapped to <span class="math inline">\(1.0\)</span> when <span class="math inline">\(w_i = 10^{17}\)</span>:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co"># Python.</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;&gt;&gt;</span> w_i <span class="op">=</span> <span class="fl">1e17</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;&gt;&gt;</span> math.<span class="bu">pow</span>(<span class="fl">0.01</span>, <span class="fl">1.0</span><span class="op">/</span>w_i) <span class="op">==</span> math.<span class="bu">pow</span>(<span class="fl">1.0</span>, <span class="fl">1.0</span><span class="op">/</span>w_i) <span class="op">==</span> <span class="fl">1.0</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a><span class="va">True</span></span></code></pre></div>
<p>Likewise, when weights are small, say <span class="math inline">\(w_i = 10^{-17}\)</span>, the corresponding sort key will almost always be zero.
Consider the interval <span class="math inline">\(0 \le u_i \le 0.99\)</span>, representing 99% of the possible random values <span class="math inline">\(u_i\)</span>:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;&gt;&gt;</span> w_i <span class="op">=</span> <span class="fl">1e-17</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;&gt;&gt;</span> math.<span class="bu">pow</span>(<span class="fl">0.0</span>, <span class="fl">1.0</span><span class="op">/</span>w_i) <span class="op">==</span> math.<span class="bu">pow</span>(<span class="fl">0.99</span>, <span class="fl">1.0</span><span class="op">/</span>w_i) <span class="op">==</span> <span class="fl">0.0</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="va">True</span></span></code></pre></div>
<p>For very large (or small) weights, then, the straightforward implementation doesn’t work.
The wanted sort ordering is destroyed when very large (or small) powers cause what should be distinct sort keys to collapse into indistinguishable fixed points.</p>
<p>Fortunately, logarithms are <a href="https://en.wikipedia.org/wiki/Monotonic_function">order-preserving</a> transformations, so sorting by <span class="math inline">\(\ln(u_i^{1/w_i}) = \ln(u_i) / w_i\)</span> produces the same ordering as sorting by <span class="math inline">\(u_i^{1/w_i}\)</span> when we’re using mathematically pure real numbers.
But the log-transformed version is much more stable when using floating-point numbers.
Distinct random inputs <span class="math inline">\(u_i\)</span> now produce reliably distinct sort keys <span class="math inline">\(k_i\)</span>, even when the input weights <span class="math inline">\(w_i\)</span> are very large or very small:</p>
<pre class="python&gt;"><code>&gt;&gt;&gt; [math.log(u_i) / 1e17 for u_i in (0.01, 0.010001, 0.99, 0.990001)]
[-4.605170185988091e-17, -4.605070190987759e-17,
 -1.005033585350145e-19, -1.0049325753001471e-19]

&gt;&gt;&gt; [math.log(u_i) / 1e-17 for u_i in (0.01, 0.010001, 0.99, 0.990001)]
[-4.605170185988091e+17, -4.605070190987758e+17,
 -1005033585350145.0, -1004932575300147.1]</code></pre>
<!-- ![Graph showing that as the weights w_i reach 10 to the power of 14, a substantial share of the possible random numbers u_i get mapped by our sort-keying function to a single value.](../images/public_html/blog/pix-20240601/deadspace_plot.svg) -->
<p>As a final tweak, we negate the sort keys so that instead of sorting by <span class="math inline">\(u_i^{1/w_i}\)</span> <strong>descending</strong>, as in the original algorithm, we do an equivalent sort by <span class="math inline">\(-\ln(u_i) / w_i\)</span> <strong>ascending</strong>.
Note the leading minus sign.
The rationale for flipping the sign will become apparent when we discuss Poisson processes in the next section.</p>
<p>One last numerical subtlety.
Why do we generate random numbers with the expression <code>1.0 - RANDOM()</code> instead of just <code>RANDOM()</code>?
Since most implementations of <code>RANDOM()</code>, such as the <a href="https://www.pcg-random.org/using-pcg-c-basic.html#generating-doubles">PCG</a> implementation used by DuckDB, return a floating-point value in the semi-closed range <span class="math inline">\([0, 1)\)</span>, they can theoretically return zero.
And we don’t want to take the logarithm of zero.
So we instead use <code>1.0 - RANDOM()</code> to generate a random number in the semi-closed range <span class="math inline">\((0, 1]\)</span>, which excludes zero.</p>
<h2 id="does-this-algorithm-actually-work">Does this algorithm actually work?</h2>
<p>First, what do we mean by <em>work</em>?
In this case, we’ll say that we want the algorithm to produce samples that are equivalent to a succession of random draws, each draw removing an item from the population, and each draw fair with respect to the population that remains <em>at the time of the draw</em>.
Because the population grows slightly smaller with each draw, the draws in the sample are not <a href="https://en.wikipedia.org/wiki/Independent_and_identically_distributed_random_variables">independent and identically distributed</a> (iid).
In practice, however, when your samples are very small compared to your population of interest, and your weights are such that it’s unlikely that iid draws would draw the same item more than once, you can generally pretend that the draws are iid and get away with it.
(When you can’t get away with it, you can use a reweighting scheme to extract unbiased estimates anyway. This subject is worth its own post, so I won’t say more here.)</p>
<p>Anyway, it’s not obvious that assigning a random number <span class="math inline">\(u_i\)</span> to every row <span class="math inline">\(i\)</span>, then sorting rows on <span class="math inline">\(-\ln(u_i)/w_i\)</span>, and finally taking the top <span class="math inline">\(m\)</span> rows is a recipe that would result in a sample that has the wanted properties.
But it does.</p>
<p>The clearest way to understand what’s going on is to first take a detour through the fascinating theory of <a href="https://ocw.mit.edu/courses/6-262-discrete-stochastic-processes-spring-2011/3a19ce0e02d0008877351bfa24f3716a_MIT6_262S11_chap02.pdf">Poisson processes</a>.
In short, <strong>a Poisson process with rate <span class="math inline">\(\lambda\)</span> is a sequence of arrivals such that the times between successive arrivals are all independent, <a href="https://en.wikipedia.org/wiki/Exponential_distribution">exponentially distributed</a> random variables with rate <span class="math inline">\(\lambda\)</span>.</strong>
<!-- For example, say $T_1 < T_2 < T_3 < \cdots$ is a sequence of arrival times, and $X_i = T_{i} - T_{i-1}$ for $i > 1$. Then if all $X_i$ are i.i.d. exponentially distributed with rate $\lambda$, the sequence is a Poisson process with rate $\lambda$. --></p>
<p>Poisson processes have some important (and useful!) properties:</p>
<ol type="1">
<li><em>They are memoryless</em>. No matter what has previously happened, the time until the next arrival from a Poisson process with rate <span class="math inline">\(\lambda\)</span> is exponentially distributed with rate <span class="math inline">\(\lambda\)</span>.</li>
<li><em>They can be merged.</em> If you have two Poisson processes with rates <span class="math inline">\(\lambda_1\)</span> and <span class="math inline">\(\lambda_2\)</span>, the arrivals from both processes form a combined Poisson process with rate <span class="math inline">\(\lambda_1 + \lambda_2\)</span>. This holds for any number of processes.</li>
<li><em>They win races in proportion to their rates.</em> In a race between the very next arrival from a Poisson process with rate <span class="math inline">\(\lambda_1\)</span> and the very next arrival from a Poisson process with rate <span class="math inline">\(\lambda_2\)</span>, the probability that the first process will win the race is <span class="math inline">\(\lambda_1 / (\lambda_1 + \lambda_2)\)</span>.</li>
</ol>
<p>Now that we know the basics of Poisson processes, there’s just one more tidbit we need:</p>
<ul>
<li><em>The uniform–exponential bridge.</em> If <span class="math inline">\(X\)</span> is a random variable having a uniform distribution between zero and one, then <span class="math inline">\(-\ln(X)/\lambda\)</span> has an exponential distribution with rate <span class="math inline">\(\lambda\)</span>.</li>
</ul>
<p>With the uniform–exponential bridge in mind, we can begin to see what the algorithm is doing when it assigns every row a key <span class="math inline">\(k_i = -\ln(u_i)/w_i\)</span> and sorts the population by that key.
It’s running a race over all the rows in the population!
In this race, each row arrives at the finish line at a time that’s an exponentially distributed random variable with a rate corresponding to the row’s weight <span class="math inline">\(w_i\)</span>.
The first <span class="math inline">\(m\)</span> arrivals form the sample.</p>
<p>To prove that this race does the sampling that we want, we will show that it is equivalent to a succession of one-row draws, each draw being fair with respect to the population that remains at the time of the draw.
Let the population’s total weight be <span class="math inline">\(w\)</span>, and consider an arbitrary row <span class="math inline">\(i\)</span> with weight <span class="math inline">\(w_i\)</span>.
The algorithm will assign it an exponentially distributed random variable with rate <span class="math inline">\(w_i\)</span>, which corresponds to the very next arrival from a Poisson process with the same rate.</p>
<p>Now consider all rows except <span class="math inline">\(i\)</span>.
They too correspond to Poisson processes with rates equal to their weights.
And we can merge them into a combined process with rate <span class="math inline">\(\sum_{j \ne i} w_j = w - w_i\)</span>.</p>
<p>Now, using the rule about Poisson races, we know that row <span class="math inline">\(i\)</span>, represented by a process with rate <span class="math inline">\(\lambda_1 = w_i\)</span>, will win the race against those other rows, represented by a combined process with rate <span class="math inline">\(\lambda_2 = w - w_i\)</span>, with probability</p>
<p><span class="math display">\[\frac{\lambda_1}{\lambda_1 + \lambda_2} = \frac{w_i}{w_i + (w - w_i)} = \frac{w_i}{w}.\]</span></p>
<p>And since we chose row <span class="math inline">\(i\)</span> arbitrarily, the same argument applies to all rows.
Thus every row’s probability of being drawn is equal to its weight in proportion to the population’s total weight.
This proves that running a “race of exponentials” lets us perform one fair draw from a population.</p>
<p>But, after we’ve drawn one row, what’s left but a new, slightly smaller population?
And can’t we run a new race on this slightly smaller population to correctly draw another row?</p>
<p>We can.
And, since Poisson processes are memoryless, we do not have to generate new arrival times to run this new race.
We can reuse the existing arrival times because the arrivals that have already happened have no effect on later arrivals.
Thus the next row we draw using the leftover arrival times will be another fair draw.</p>
<p>We can repeat this argument to show that successive rows are chosen fairly in relation to the population that remains at the time of each draw.
Thus algorithm A-ES selects a sample of size <span class="math inline">\(m\)</span> by making <span class="math inline">\(m\)</span> successive draws, each fair with respect to its remaining population.
And that’s the proof.</p>
<h2 id="tricks-for-faster-samples">Tricks for faster samples</h2>
<p>Most large analytical datasets will be stored in a column-oriented storage format, such as Parquet.
When reading from such datasets, you typically only have to pay for the columns you read.
(By “pay”, I mean wait for the query engine to do its work, but if you’re running your query on some tech company’s cloud, you may actually pay in currency too.)</p>
<p>For example, if your dataset contains a table having 100 columns but you need only four of them, the query engine will usually only read those four columns.
In row-oriented data stores, by contrast, you’ll generally have to decode entire rows, even if you only want four out of the 100 values in each row.
Additionally, most column-oriented stores support some kind of filter pushdown, allowing the storage engine to skip rows when a filtering expression evaluates to false.
These two properties—pay for what you read and filter pushdown—are ones we can exploit when taking samples.</p>
<p>Say we have a Population table with billions of rows and around 100 columns.
How can we efficiently take a weighted sample of 1000 rows?</p>
<p>We could use the basic sampling formulation, as discussed earlier:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode sql"><code class="sourceCode sql"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="kw">SELECT</span> <span class="op">*</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a><span class="kw">FROM</span> Population</span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="kw">WHERE</span> weight <span class="op">&gt;</span> <span class="dv">0</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a><span class="kw">ORDER</span> <span class="kw">BY</span> <span class="op">-</span><span class="fu">LN</span>(<span class="fl">1.0</span> <span class="op">-</span> <span class="kw">RANDOM</span>()) <span class="op">/</span> weight</span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a><span class="kw">LIMIT</span> <span class="dv">1000</span>  <span class="co">-- Sample size.</span></span></code></pre></div>
<p>But think about what the query engine must do to execute this query.
It must read and decode all 100 columns for all of those billions of rows so that it may pass those rows into the sort/limit logic (typically implemented as a <code>TOP_N</code> operation) to determine which rows to keep for the sample.
Even though the sample will keep only 0.00001% of those rows, you’ll have to pay to read the entire Population table!</p>
<p>A much faster approach is to only read the columns we need to determine <em>which</em> rows are in the sample.
Say our table has a primary key <code>pk</code> that uniquely identifies each row.
The following variant on our sampling formulation returns only the primary keys needed to identify the rows in the sample:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode sql"><code class="sourceCode sql"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="kw">SELECT</span> pk</span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="kw">FROM</span> Population</span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a><span class="kw">WHERE</span> weight <span class="op">&gt;</span> <span class="dv">0</span></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a><span class="kw">ORDER</span> <span class="kw">BY</span> <span class="op">-</span><span class="fu">LN</span>(<span class="fl">1.0</span> <span class="op">-</span> <span class="kw">RANDOM</span>()) <span class="op">/</span> weight</span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a><span class="kw">LIMIT</span> <span class="dv">1000</span>  <span class="co">-- Sample size.</span></span></code></pre></div>
<p>This variant only forces the query engine to read two columns: <code>pk</code> and <code>weight</code>:
Yes, it still must read those two columns for the billions of rows in the table, but those columns contain small values and can be scanned quickly.
After all, that’s what column-oriented stores are designed to do well.
The point is that we’re not paying to read about 100 additional columns whose values we’re just going to throw away 99.99999% of the time.</p>
<p>One we have identified the rows in our sample, we can run a second query to pull in the full set of wanted columns for just those rows.</p>
<h2 id="adding-determinism">Adding determinism</h2>
<p>Our sampling algorithm depends on randomization.
If we run our algorithm twice with the same inputs, we’ll get different results each time.
Often, that nondeterminism is exactly what we want.</p>
<p>But sometimes it isn’t.
Sometimes, it’s useful to be able to <em>control</em> the dice rolls that the algorithm depends on.
For example, sometimes it’s useful to be able to repeat a sample.
Or <em>almost</em> repeat a sample.</p>
<p>To allow us to control the nature of the randomization used when we take samples, we must replace calls to <code>RANDOM</code> with a deterministic pseudorandom function.
One common approach is to hash a primary key and then map the hashed value to a number in the range <span class="math inline">\([0, 1)\)</span>.
The following DuckDB macro <code>pseudorandom_uniform</code> will do exactly that:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode sql"><code class="sourceCode sql"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- Returns a pseudorandom fp64 number in the range [0, 1). The number</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="co">-- is determined by the given `key`, `seed` string, and integer `index`.</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> MACRO pseudorandom_uniform(<span class="kw">key</span>, seed, <span class="kw">index</span>)</span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a><span class="kw">AS</span> (</span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a>  (<span class="kw">HASH</span>(<span class="kw">key</span> <span class="op">||</span> seed <span class="op">||</span> <span class="kw">index</span>) <span class="op">&gt;&gt;</span> <span class="dv">11</span>) <span class="op">*</span> POW(<span class="fl">2.0</span>, <span class="op">-</span><span class="dv">53</span>)</span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a>);</span></code></pre></div>
<p>We can vary the <code>seed</code> and <code>index</code> parameters to generate independent random values for the same <code>key</code>.
For example, if I fix the <code>seed</code> to “demo-seed-20240601” and generate random numbers for the <code>key</code> “key123” over the <code>index</code> values <span class="math inline">\(1, 2, \ldots, 10\)</span>, I get 10 fresh random numbers:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode sql"><code class="sourceCode sql"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="kw">SELECT</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>  pseudorandom_uniform(<span class="st">&#39;key123&#39;</span>, <span class="st">&#39;demo-seed-20240601&#39;</span>, i) <span class="kw">AS</span> u_key123</span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a><span class="kw">FROM</span> <span class="kw">RANGE</span>(<span class="dv">1</span>, <span class="dv">11</span>) <span class="kw">AS</span> t(i);</span></code></pre></div>
<table>
<caption>Ten random numbers for the key “key123” and seed “demo-seed-20240601”.</caption>
<thead>
<tr class="header">
<th style="text-align: right;"><em>i</em></th>
<th style="text-align: left;"><em>u_key123</em></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: right;">1</td>
<td style="text-align: left;">0.9592606495318252</td>
</tr>
<tr class="even">
<td style="text-align: right;">2</td>
<td style="text-align: left;">0.6309411348395693</td>
</tr>
<tr class="odd">
<td style="text-align: right;">3</td>
<td style="text-align: left;">0.5673207749533353</td>
</tr>
<tr class="even">
<td style="text-align: right;">4</td>
<td style="text-align: left;">0.11182926321927167</td>
</tr>
<tr class="odd">
<td style="text-align: right;">5</td>
<td style="text-align: left;">0.3375806483238627</td>
</tr>
<tr class="even">
<td style="text-align: right;">6</td>
<td style="text-align: left;">0.12881607107157678</td>
</tr>
<tr class="odd">
<td style="text-align: right;">7</td>
<td style="text-align: left;">0.6993372364353198</td>
</tr>
<tr class="even">
<td style="text-align: right;">8</td>
<td style="text-align: left;">0.94031652266991</td>
</tr>
<tr class="odd">
<td style="text-align: right;">9</td>
<td style="text-align: left;">0.17893798791559323</td>
</tr>
<tr class="even">
<td style="text-align: right;">10</td>
<td style="text-align: left;">0.6903126337753016</td>
</tr>
</tbody>
</table>
<p>To take deterministic samples, we just replace calls to <code>RANDOM()</code> with calls to our function <code>pseudorandom_uniform()</code>.</p>
<p>Now that we can take deterministic samples, we can do even more useful things!
For example, we can take samples <em>with</em> replacement.</p>
<h2 id="sampling-with-replacement">Sampling with replacement</h2>
<p>Earlier, we proved that the A-ES algorithm allows us to take a sample without replacement as a series of successive draws, each draw removing an item from the population, and each draw fair with respect to the population that remains at the time of the draw.
But what if we wanted to take a sample <em>with replacement</em>?
A sample with replacement requires us to return each item to the population as it is selected so that every selection is fair with respect to the <em>original</em> population, and individual items may be selected more than once.</p>
<p>Can we efficiently implement sampling with replacement in SQL?
Yes!
But it’s a little trickier.
(I haven’t found this algorithm published anywhere; please let me know if you have.
It took me some effort to create, but I wouldn’t be surprised if it’s already known.)</p>
<p>Think back to our correctness proof for the A-ES algorithm.
For each row <span class="math inline">\(i\)</span> having a weight <span class="math inline">\(w_i\)</span>, the algorithm imagined a corresponding Poisson process with rate <span class="math inline">\(\lambda_i = w_i\)</span> and represented the row by the very next arrival from that process.
That arrival would occur at time <span class="math inline">\(k_i = -\ln(u_i) / w_i\)</span>, where <span class="math inline">\(u_i\)</span> is a uniformly distributed random number in the range <span class="math inline">\((0, 1]\)</span>.
Then the algorithm sorted all rows by their <span class="math inline">\(k_i\)</span> values and took the first <span class="math inline">\(m\)</span> arrivals as the sample.</p>
<p>With one minor tweak to this algorithm, we can take a sample with replacement.
That tweak is to consider not just the <em>very next</em> arrival from each row’s Poisson process but <em>all</em> arrivals.
Let <span class="math inline">\(k_{i,j}\)</span> denote the <span class="math inline">\(j\)</span>th arrival from row <span class="math inline">\(i\)</span>’s process.
Since we know that in a Poisson process the times between successive arrivals are exponentially distributed random variables, we can take the running sum over interarrival times to give us the needed arrival times.
That is, <span class="math inline">\(k_{i,j} = \sum_{r=1}^{j} -\ln(u_{i,r}) / w_i\)</span>, where <span class="math inline">\(u_{i,r}\)</span> represents the <span class="math inline">\(r\)</span>th uniformly distributed random variable for row <span class="math inline">\(i\)</span>.</p>
<p>One minor problem with this tweaked algorithm is that a Poisson process generates a theoretically infinite series of arrivals.
Creating an infinite series for each row and then sorting the arrivals from all of these series is intractable.</p>
<p>Fortunately, we can avoid this problem!
Think about how the A-ES algorithm for taking a sample <em>without</em> replacement relates to our proposed intractable algorithm for taking a sample <em>with</em> replacement.
We could describe the <em>without</em> algorithm in terms of the <em>with</em> algorithm like so:
Prepare to take a sample <em>with</em> replacement, but then ignore all arrivals <span class="math inline">\(k_{i,j}\)</span> for <span class="math inline">\(j &gt; 1\)</span>; the remaining arrivals must be of the form <span class="math inline">\(k_{i,1}\)</span>, where <span class="math inline">\(i\)</span> indicates the corresponding row.
Then take the first <span class="math inline">\(m\)</span> of these remaining arrivals as your sample, as before.</p>
<p>Now think about going the other way, from having a <em>without</em>-replacement sample and needing to construct a corresponding <em>with</em>-replacement sample.
Let <span class="math inline">\(S\)</span> be the set of rows sampled <em>without</em> replacement.
We know these rows were represented by a corresponding set of arrival times <span class="math inline">\(k_{i,1}\)</span> for <span class="math inline">\(i\)</span> in <span class="math inline">\(S\)</span>.
We also know that, had the sample been taken <em>with</em> replacement, the race would have included some additional arrival times <span class="math inline">\(k_{i,j}\)</span> for <span class="math inline">\(j &gt; 1\)</span> that could have displaced some of the winning rows in <span class="math inline">\(S\)</span>.
But, crucially, we also know that if <span class="math inline">\(S_{*}\)</span> represents the set of rows in the corresponding sample <em>with</em> replacement, then <span class="math inline">\(S_{*}\)</span> must be contained within <span class="math inline">\(S\)</span>.
This claim follows from the fact that if any arrival <span class="math inline">\(k_{i,j}\)</span> for <span class="math inline">\(j &gt; 1\)</span> does displace a row among the first <span class="math inline">\(m\)</span> arrivals, the <span class="math inline">\(j&gt;1\)</span> requirement implies that the displacing row is a duplicate of some row <span class="math inline">\(i\)</span> that arrived earlier in the sample at time <span class="math inline">\(k_{i,1}\)</span>; thus, displacement cannot introduce a new row from outside of <span class="math inline">\(S\)</span>.</p>
<p>Therefore, if we have a sample <em>without</em> replacement, we can construct a sample <em>with</em> replacement from its rows.
<em>We can ignore all other rows in the population.</em>
This makes the problem much more approachable.</p>
<p>So now we can see an algorithm taking shape for taking a sample <em>with</em> replacement of size <span class="math inline">\(m\)</span>:</p>
<ol type="1">
<li>First, take an <span class="math inline">\(m\)</span>-sized sample <span class="math inline">\(S\)</span> <em>without</em> replacement using the efficient A-ES algorithm.</li>
<li>For each sampled row <span class="math inline">\(i\)</span> in <span class="math inline">\(S\)</span>, generate <span class="math inline">\(m\)</span> arrivals <span class="math inline">\(k_{i,j}\)</span> for <span class="math inline">\(j = 1, 2, \ldots, m\)</span> using the same pseudorandom universe that was used to generate <span class="math inline">\(S\)</span>.</li>
<li>Take the first <span class="math inline">\(m\)</span> arrivals as the sample.</li>
</ol>
<p>You may have noticed that step 2 of this algorithm requires us to create <span class="math inline">\(m\)</span> arrivals for each of the <span class="math inline">\(m\)</span> rows in <span class="math inline">\(S\)</span>.
This step thus requires <span class="math inline">\(O(m^2)\)</span> time.
When <span class="math inline">\(m\)</span> is large, this time can be prohibitive.</p>
<p>Fortunately, we can use probability theory to reduce this cost to <span class="math inline">\(O(m)\)</span>.
The idea is that if row <span class="math inline">\(i\)</span> is expected to occur in the sample <span class="math inline">\(n_i\)</span> times, it is very unlikely to occur more than <span class="math inline">\(c \cdot n_i\)</span> times when <span class="math inline">\(c \ge 2\)</span>.
So we don’t need to generate a full <span class="math inline">\(m\)</span> arrivals for each row <span class="math inline">\(i\)</span> in <span class="math inline">\(S\)</span>; we can get away with generating <span class="math inline">\(m_i = \lceil{c \cdot n_i}\rceil\)</span> arrivals instead, for a suitably large <span class="math inline">\(c\)</span> to assuage our personal level of paranoia.</p>
<p>Here’s a sample implementation as a DuckDB macro:</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode sql"><code class="sourceCode sql"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- Takes a weighted sample with replacement from a table.</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a><span class="co">--</span></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a><span class="co">-- Args:</span></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a><span class="co">--  population_table: The table to sample from. It must have a `pk` column</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a><span class="co">--    of unique primary keys and a `weight` column of non-negative weights.</span></span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a><span class="co">--  seed: A string that determines the pseudorandom universe in which the</span></span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a><span class="co">--    sample is taken. Samples taken with distinct seeds are independent.</span></span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a><span class="co">--    If you wish to repeat a sample, reuse the sample&#39;s seed.</span></span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true" tabindex="-1"></a><span class="co">--  sample_size: The number of rows to include in the sample. This value</span></span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true" tabindex="-1"></a><span class="co">--    may be larger than the number of rows in the `population_table`.</span></span>
<span id="cb11-11"><a href="#cb11-11" aria-hidden="true" tabindex="-1"></a><span class="co">--</span></span>
<span id="cb11-12"><a href="#cb11-12" aria-hidden="true" tabindex="-1"></a><span class="co">-- Returns a sample of rows from the `population_table`.</span></span>
<span id="cb11-13"><a href="#cb11-13" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> MACRO sample_with_replacement(population_table, seed, sample_size)</span>
<span id="cb11-14"><a href="#cb11-14" aria-hidden="true" tabindex="-1"></a><span class="kw">AS</span> <span class="kw">TABLE</span> (</span>
<span id="cb11-15"><a href="#cb11-15" aria-hidden="true" tabindex="-1"></a>  <span class="kw">WITH</span></span>
<span id="cb11-16"><a href="#cb11-16" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- First, take a sample *without* replacement of the wanted size.</span></span>
<span id="cb11-17"><a href="#cb11-17" aria-hidden="true" tabindex="-1"></a>    SampleWithoutReplacement <span class="kw">AS</span> (</span>
<span id="cb11-18"><a href="#cb11-18" aria-hidden="true" tabindex="-1"></a>      <span class="kw">SELECT</span> <span class="op">*</span></span>
<span id="cb11-19"><a href="#cb11-19" aria-hidden="true" tabindex="-1"></a>      <span class="kw">FROM</span> query_table(population_table:<span class="ch">:varchar</span>)</span>
<span id="cb11-20"><a href="#cb11-20" aria-hidden="true" tabindex="-1"></a>      <span class="kw">WHERE</span> weight <span class="op">&gt;</span> <span class="dv">0</span></span>
<span id="cb11-21"><a href="#cb11-21" aria-hidden="true" tabindex="-1"></a>      <span class="kw">ORDER</span> <span class="kw">BY</span> <span class="op">-</span><span class="fu">LN</span>(pseudorandom_uniform(pk, seed, <span class="dv">1</span>)) <span class="op">/</span> weight</span>
<span id="cb11-22"><a href="#cb11-22" aria-hidden="true" tabindex="-1"></a>      <span class="kw">LIMIT</span> sample_size</span>
<span id="cb11-23"><a href="#cb11-23" aria-hidden="true" tabindex="-1"></a>    ),</span>
<span id="cb11-24"><a href="#cb11-24" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- Compute the total weight over the sample.</span></span>
<span id="cb11-25"><a href="#cb11-25" aria-hidden="true" tabindex="-1"></a>    SampleWithoutReplacementTotals <span class="kw">AS</span> (</span>
<span id="cb11-26"><a href="#cb11-26" aria-hidden="true" tabindex="-1"></a>      <span class="kw">SELECT</span> <span class="fu">SUM</span>(weight) <span class="kw">AS</span> weight</span>
<span id="cb11-27"><a href="#cb11-27" aria-hidden="true" tabindex="-1"></a>      <span class="kw">FROM</span> SampleWithoutReplacement</span>
<span id="cb11-28"><a href="#cb11-28" aria-hidden="true" tabindex="-1"></a>    ),</span>
<span id="cb11-29"><a href="#cb11-29" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- Generate a series of arrivals for each row in the sample.</span></span>
<span id="cb11-30"><a href="#cb11-30" aria-hidden="true" tabindex="-1"></a>    SampleWithReplacementArrivals <span class="kw">AS</span> (</span>
<span id="cb11-31"><a href="#cb11-31" aria-hidden="true" tabindex="-1"></a>      <span class="kw">SELECT</span></span>
<span id="cb11-32"><a href="#cb11-32" aria-hidden="true" tabindex="-1"></a>        S.<span class="op">*</span>,</span>
<span id="cb11-33"><a href="#cb11-33" aria-hidden="true" tabindex="-1"></a>        <span class="fu">SUM</span>(<span class="op">-</span><span class="fu">LN</span>(pseudorandom_uniform(pk, seed, trial_index)) <span class="op">/</span> S.weight)</span>
<span id="cb11-34"><a href="#cb11-34" aria-hidden="true" tabindex="-1"></a>          <span class="kw">OVER</span> (<span class="kw">PARTITION</span> <span class="kw">BY</span> pk <span class="kw">ORDER</span> <span class="kw">BY</span> trial_index)</span>
<span id="cb11-35"><a href="#cb11-35" aria-hidden="true" tabindex="-1"></a>          <span class="kw">AS</span> rws_sort_key</span>
<span id="cb11-36"><a href="#cb11-36" aria-hidden="true" tabindex="-1"></a>      <span class="kw">FROM</span> SampleWithoutReplacement <span class="kw">AS</span> S</span>
<span id="cb11-37"><a href="#cb11-37" aria-hidden="true" tabindex="-1"></a>      <span class="kw">CROSS</span> <span class="kw">JOIN</span> SampleWithoutReplacementTotals <span class="kw">AS</span> T</span>
<span id="cb11-38"><a href="#cb11-38" aria-hidden="true" tabindex="-1"></a>      <span class="kw">CROSS</span> <span class="kw">JOIN</span></span>
<span id="cb11-39"><a href="#cb11-39" aria-hidden="true" tabindex="-1"></a>        UNNEST(</span>
<span id="cb11-40"><a href="#cb11-40" aria-hidden="true" tabindex="-1"></a>          <span class="kw">RANGE</span>(<span class="dv">1</span>, <span class="fu">CAST</span>(<span class="fl">2.0</span> <span class="op">*</span> sample_size <span class="op">*</span> S.weight <span class="op">/</span> T.weight <span class="op">+</span> <span class="dv">2</span> <span class="kw">AS</span> <span class="dt">INT</span>)))</span>
<span id="cb11-41"><a href="#cb11-41" aria-hidden="true" tabindex="-1"></a>        <span class="kw">AS</span> I(trial_index)</span>
<span id="cb11-42"><a href="#cb11-42" aria-hidden="true" tabindex="-1"></a>    )</span>
<span id="cb11-43"><a href="#cb11-43" aria-hidden="true" tabindex="-1"></a>  <span class="co">-- Form the sample *with* replacement from the first `sample_size` arrivals.</span></span>
<span id="cb11-44"><a href="#cb11-44" aria-hidden="true" tabindex="-1"></a>  <span class="kw">SELECT</span> <span class="op">*</span> EXCLUDE (rws_sort_key)</span>
<span id="cb11-45"><a href="#cb11-45" aria-hidden="true" tabindex="-1"></a>  <span class="kw">FROM</span> SampleWithReplacementArrivals</span>
<span id="cb11-46"><a href="#cb11-46" aria-hidden="true" tabindex="-1"></a>  <span class="kw">ORDER</span> <span class="kw">BY</span> rws_sort_key</span>
<span id="cb11-47"><a href="#cb11-47" aria-hidden="true" tabindex="-1"></a>  <span class="kw">LIMIT</span> sample_size</span>
<span id="cb11-48"><a href="#cb11-48" aria-hidden="true" tabindex="-1"></a>);</span></code></pre></div>
<h3 id="example-of-sampling-with-replacement">Example of sampling with replacement</h3>
<p>As an example of when we might want to sample with replacement instead of without, consider the following population table <code>ThreeToOne</code> that represents the possible outcomes of tossing a biased coin:</p>
<table>
<caption><code>ThreeToOne</code> population table.</caption>
<thead>
<tr class="header">
<th style="text-align: left;"><em>pk</em></th>
<th style="text-align: right;"><em>weight</em></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: left;">heads</td>
<td style="text-align: right;">3</td>
</tr>
<tr class="even">
<td style="text-align: left;">tails</td>
<td style="text-align: right;">1</td>
</tr>
</tbody>
</table>
<p>For this biased coin, “heads” is 3 times as likely as “tails.”
We can simulate flipping this coin 10 times by sampling 10 rows from the <code>ThreeToOne</code> population table with replacement:</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode sql"><code class="sourceCode sql"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="kw">SELECT</span> pk</span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a><span class="kw">FROM</span> sample_with_replacement(ThreeToOne, <span class="st">&#39;test-seed-20240601&#39;</span>, <span class="dv">10</span>);</span></code></pre></div>
<table>
<caption>Results of drawing a 10-row sample with replacement from <code>ThreeToOne</code>.</caption>
<thead>
<tr class="header">
<th style="text-align: left;"><em>pk</em></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: left;">heads</td>
</tr>
<tr class="even">
<td style="text-align: left;">heads</td>
</tr>
<tr class="odd">
<td style="text-align: left;">heads</td>
</tr>
<tr class="even">
<td style="text-align: left;">tails</td>
</tr>
<tr class="odd">
<td style="text-align: left;">tails</td>
</tr>
<tr class="even">
<td style="text-align: left;">heads</td>
</tr>
<tr class="odd">
<td style="text-align: left;">heads</td>
</tr>
<tr class="even">
<td style="text-align: left;">heads</td>
</tr>
<tr class="odd">
<td style="text-align: left;">tails</td>
</tr>
<tr class="even">
<td style="text-align: left;">heads</td>
</tr>
</tbody>
</table>
<p>In this sample, we got 7 heads and 3 tails.
On average, we would expect about 7.5 heads in each sample of size 10, so our observed sample is close to our expectations.</p>
<p>But maybe we just got lucky.
As a stronger test of our SQL sampling logic, let’s take 10,000 samples of size 40 and look at the count of heads across all of the samples.
We would expect this count to have a Binomial(<em>size</em> = 40, <em>p</em> = 3/4) distribution.
To compare our observed results to the expected distribution, I’ll compute the empirical distribution of the results and plot that over the expected distribution.
As you can see in the figure below, the observed distribution closely matches the expected distribution.</p>
<figure>
<img src="../images/public_html/blog/pix-20240601/observed_samples_vs_expected_three_to_one.svg" alt="When we take 10,000 independent samples of size n = 40 from a 3:1 biased-coin distribution, we find that the count of “heads” over the samples agrees with the expected Binomial(size = 40, p = 3/4) distribution." />
<figcaption aria-hidden="true">When we take 10,000 independent samples of size <span class="math inline">\(n = 40\)</span> from a 3:1 biased-coin distribution, we find that the count of “heads” over the samples agrees with the expected Binomial(<em>size</em> = 40, <em>p</em> = 3/4) distribution.</figcaption>
</figure>
<h2 id="conclusion">Conclusion</h2>
<p>Sampling is a powerful tool.
And with the SQL logic we’ve just discussed, you can take fast, easy samples from virtually any dataset, no matter how large.
And you can take those samples with or without replacement.</p>
<p>What makes it all work is a clever connection to the theory of Poisson processes.
Those processes are memoryless and mergeable, and their arrivals win races in proportion to their rates.
These properties are exactly what we need to run races that let us take samples.</p>
<p>Beyond what we’ve discussed in this article, there are further ways we can exploit these properties.
For example, as a performance optimization, we can predict the arrival time <span class="math inline">\(t\)</span> of the final row in a sample.
Then we can augment our SQL sampling logic with a pushdown filter that eliminates population rows with arrival times greater than <span class="math inline">\(c \cdot t\)</span> for some constant <span class="math inline">\(c\)</span>.
This filtering happens before <code>ORDER/LIMIT</code> processing and can greatly speed queries by eliminating more than 99.99% of rows early on, before they are even fully read on systems that support “late materialization.”</p>
<p>But this article is already too long, so I’ll stop here for now.</p>
<h2 id="references">References</h2>
<p>Pavlos S. Efraimidis and Paul G. Spirakis. Weighted random sampling with a reservoir. <em>Information Processing Letters</em>, 97(5):181–185, 2006.</p>
</div>
]]></summary>
</entry>
<entry>
    <title>Dice Race (part 1)</title>
    <link href="http://blog.moertel.com/posts/2023-06-24-dice-race-1.html" />
    <id>http://blog.moertel.com/posts/2023-06-24-dice-race-1.html</id>
    <published>2023-06-24T00:00:00Z</published>
    <updated>2023-06-24T00:00:00Z</updated>
    <summary type="html"><![CDATA[<div class="info byline">By
  <span itemprop="author" itemscope itemtype="http://schema.org/Person"><a href="https://blog.moertel.com/about.html" itemprop="url"><span itemprop="name">Tom Moertel</span></a></span></div>
<div class="info">Posted on <time itemprop="datePublished" datetime="2023-06-24">June 24, 2023</time></div>
<div class="tags">Tags: <span itemprop="keywords"><a title="All pages tagged &#39;interview-problems&#39;." href="/tags/interview-problems.html" rel="tag">interview-problems</a>, <a title="All pages tagged &#39;probability&#39;." href="/tags/probability.html" rel="tag">probability</a>, <a title="All pages tagged &#39;dice-race&#39;." href="/tags/dice-race.html" rel="tag">dice-race</a></span></div>

<div itemprop="articleBody">
<p>In a dice race of degree <span class="math inline">\(m\)</span> and length <span class="math inline">\(n\)</span>, each player chooses a sequence of <span class="math inline">\(m\)</span> die rolls.
Then we roll a die <span class="math inline">\(n\)</span> times to generate the race sequence.
The player whose sequence occurs first in the race sequence wins.
If no player’s sequence occurs in the race sequence, the race is a tie.
(If we want to guarantee a winner, we can always set <span class="math inline">\(n = \infty\)</span>.)</p>
<p>For example, say you and I run a race of degree <span class="math inline">\(m=2\)</span> and length <span class="math inline">\(n=20\)</span>.
I might choose 55 as my sequence.
You might choose 23.
Then we roll a die <span class="math inline">\(n=20\)</span> times to generate the race sequence:</p>
<p>6 1 6 6 1 <strong>5 5</strong> 3 1 4 5 <strong>2 3</strong> 3 2 2 6 4 3 4</p>
<p>In this race, I would win since 55 occurred before 23 in the race sequence.</p>
<p>Now consider the following questions:</p>
<ol type="1">
<li>Given a choice between 55 and 23, which should you choose? Why?</li>
<li>What is the expected number of die rolls before 55 occurs? What about for 23?</li>
<li>Write code to run a dice race, given degree <span class="math inline">\(m\)</span>, length <span class="math inline">\(n\)</span>, and the players’ chosen sequences.</li>
<li>Use the code to simulate many races between 55 and 23.</li>
<li>How do the results compare with your answer to question 2?</li>
</ol>
<p>I’ll answer the first two questions in this post and save the rest for a later post.</p>
<p>(Now would be a good time to pause and try to answer the questions yourself.)</p>
<h2 id="question-1-given-a-choice-between-55-and-23-which-should-you-choose-why">Question 1: Given a choice between 55 and 23, which should you choose? Why?</h2>
<p>In the example above, 55 won the race.
Is it the better choice, in general? To answer the question, let’s think about the task of matching the sequence 55 in a series of die rolls.
This task can be modeled as a finite automaton:</p>
<!--

// Graphviz source:
// Automaton to recognize the sequence 55 in a series of die rolls.

digraph G {

  node [fontname="Lato, sans-serif", fillcolor=lightgrey, style=filled]
  edge [fontname="Lato, sans-serif"]

  rankdir=LR

  Start [style=invis]
  S0 [shape=circle]
  S1 [shape=circle]
  S2 [shape=doublecircle]

  Start -> S0

  S0 -> S0 [label = "1 | 2 | 3 | 4 | 6"]
  S0 -> S1 [label = "5"];

  S1 -> S0 [label = "1 | 2 | 3 | 4 | 6"]
  S1 -> S2 [label = "5"]

}

-->
<!-- Generated by graphviz version 2.40.1 (20161225.0304) -->
<svg width="392pt" height="91pt" viewBox="0.00 0.00 392.14 90.80" 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 86.8019)">
<title>
Finite automaton for recognizing the sequence 55 in a series of die rolls
</title>
<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-86.8019 388.1414,-86.8019 388.1414,4 -4,4"/>
<!-- Start -->
<!-- S0 -->
<g id="node2" class="node">
<title>
S0
</title>
<ellipse fill="#d3d3d3" stroke="#000000" cx="116.7806" cy="-26.7686" rx="21.4691" ry="21.4691"/>
<text text-anchor="middle" x="116.7806" y="-22.5686" font-family="Lato, sans-serif" font-size="14.00" fill="#000000">S0</text>
</g>
<!-- Start&#45;&gt;S0 -->
<g id="edge1" class="edge">
<title>
Start-&gt;S0
</title>
<path fill="none" stroke="#000000" d="M58.8995,-26.7686C67.4301,-26.7686 76.7633,-26.7686 85.3965,-26.7686"/>
<polygon fill="#000000" stroke="#000000" points="85.4789,-30.2687 95.4789,-26.7686 85.4788,-23.2687 85.4789,-30.2687"/>
</g>
<!-- S0&#45;&gt;S0 -->
<g id="edge2" class="edge">
<title>
S0-&gt;S0
</title>
<path fill="none" stroke="#000000" d="M109.8661,-46.9549C108.9093,-57.0221 111.2142,-66.0019 116.7806,-66.0019 120.3466,-66.0019 122.574,-62.3166 123.4629,-57.021"/>
<polygon fill="#000000" stroke="#000000" points="126.9634,-57.033 123.6951,-46.9549 119.9653,-56.8715 126.9634,-57.033"/>
<text text-anchor="middle" x="116.7806" y="-70.2019" font-family="Lato, sans-serif" font-size="14.00" fill="#000000">1 | 2 | 3 | 4 | 6</text>
</g>
<!-- S1 -->
<g id="node3" class="node">
<title>
S1
</title>
<ellipse fill="#d3d3d3" stroke="#000000" cx="269.4415" cy="-26.7686" rx="21.4691" ry="21.4691"/>
<text text-anchor="middle" x="269.4415" y="-22.5686" font-family="Lato, sans-serif" font-size="14.00" fill="#000000">S1</text>
</g>
<!-- S0&#45;&gt;S1 -->
<g id="edge3" class="edge">
<title>
S0-&gt;S1
</title>
<path fill="none" stroke="#000000" d="M138.3294,-26.7686C164.2422,-26.7686 207.958,-26.7686 237.7253,-26.7686"/>
<polygon fill="#000000" stroke="#000000" points="238.0024,-30.2687 248.0023,-26.7686 238.0023,-23.2687 238.0024,-30.2687"/>
<text text-anchor="middle" x="193.111" y="-30.9686" font-family="Lato, sans-serif" font-size="14.00" fill="#000000">5</text>
</g>
<!-- S1&#45;&gt;S0 -->
<g id="edge4" class="edge">
<title>
S1-&gt;S0
</title>
<path fill="none" stroke="#000000" d="M251.4333,-14.8629C245.008,-11.2674 237.5401,-7.7919 230.2082,-5.9686 198.2077,1.9895 188.0144,1.9895 156.0138,-5.9686 151.8897,-6.9942 147.7225,-8.5426 143.722,-10.3411"/>
<polygon fill="#000000" stroke="#000000" points="142.1302,-7.2239 134.7888,-14.8629 145.2916,-13.4693 142.1302,-7.2239"/>
<text text-anchor="middle" x="193.111" y="-9.9686" font-family="Lato, sans-serif" font-size="14.00" fill="#000000">1 | 2 | 3 | 4 | 6</text>
</g>
<!-- S2 -->
<g id="node4" class="node">
<title>
S2
</title>
<ellipse fill="#d3d3d3" stroke="#000000" cx="358.9081" cy="-26.7686" rx="21.4314" ry="21.4314"/>
<ellipse fill="none" stroke="#000000" cx="358.9081" cy="-26.7686" rx="25.4687" ry="25.4687"/>
<text text-anchor="middle" x="358.9081" y="-22.5686" font-family="Lato, sans-serif" font-size="14.00" fill="#000000">S2</text>
</g>
<!-- S1&#45;&gt;S2 -->
<g id="edge5" class="edge">
<title>
S1-&gt;S2
</title>
<path fill="none" stroke="#000000" d="M291.0989,-26.7686C300.818,-26.7686 312.5384,-26.7686 323.4814,-26.7686"/>
<polygon fill="#000000" stroke="#000000" points="323.6442,-30.2687 333.6442,-26.7686 323.6442,-23.2687 323.6442,-30.2687"/>
<text text-anchor="middle" x="312.1748" y="-30.9686" font-family="Lato, sans-serif" font-size="14.00" fill="#000000">5</text>
</g>
</g>
</svg>
<p>We start in the initial state S0.
If the next die roll is a 5, we advance to state S1; otherwise, we remain at S0.
If we’re at S1 and the next roll is another 5, we advance to S2 and have successfully matched the sequence 55; otherwise, we go all the way back to S0 and start over from the beginning.</p>
<p>Now let’s consider the automaton for the sequence 23:</p>
<!--

// Graphviz source:
// Automaton to recognize the sequence 23 in a series of die rolls.

digraph G {

  node [fontname="Lato, sans-serif", fillcolor=lightgrey, style=filled]
  edge [fontname="Lato, sans-serif"]

  rankdir=LR

  Start [style=invis]
  S0 [shape=circle]
  S1 [shape=circle]
  S2 [shape=doublecircle]

  Start -> S0

  S0 -> S0 [label = "1  | 3 | 4 | 5 | 6"]
  S0 -> S1 [label = "2"];

  S1 -> S0 [label = "1 | 4 | 5 | 6"]
  S1 -> S1 [label = "2"]
  S1 -> S2 [label = "3"]

}

-->
<!-- Generated by graphviz version 2.40.1 (20161225.0304) -->
<svg width="375pt" height="89pt" viewBox="0.00 0.00 375.34 89.45" 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 85.4505)">
<title>
Finite automaton to recognize the sequence 23 in a series of die rolls.
</title>
<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-85.4505 371.3428,-85.4505 371.3428,4 -4,4"/>
<!-- Start -->
<!-- S0 -->
<g id="node2" class="node">
<title>
S0
</title>
<ellipse fill="#d3d3d3" stroke="#000000" cx="116.7806" cy="-25.4172" rx="21.4691" ry="21.4691"/>
<text text-anchor="middle" x="116.7806" y="-21.2172" font-family="Lato, sans-serif" font-size="14.00" fill="#000000">S0</text>
</g>
<!-- Start&#45;&gt;S0 -->
<g id="edge1" class="edge">
<title>
Start-&gt;S0
</title>
<path fill="none" stroke="#000000" d="M58.8995,-25.4172C67.4301,-25.4172 76.7633,-25.4172 85.3965,-25.4172"/>
<polygon fill="#000000" stroke="#000000" points="85.4789,-28.9173 95.4789,-25.4172 85.4788,-21.9173 85.4789,-28.9173"/>
</g>
<!-- S0&#45;&gt;S0 -->
<g id="edge2" class="edge">
<title>
S0-&gt;S0
</title>
<path fill="none" stroke="#000000" d="M109.8661,-45.6035C108.9093,-55.6707 111.2142,-64.6505 116.7806,-64.6505 120.3466,-64.6505 122.574,-60.9652 123.4629,-55.6696"/>
<polygon fill="#000000" stroke="#000000" points="126.9634,-55.6816 123.6951,-45.6035 119.9653,-55.5201 126.9634,-55.6816"/>
<text text-anchor="middle" x="116.7806" y="-68.8505" font-family="Lato, sans-serif" font-size="14.00" fill="#000000">1  | 3 | 4 | 5 | 6</text>
</g>
<!-- S1 -->
<g id="node3" class="node">
<title>
S1
</title>
<ellipse fill="#d3d3d3" stroke="#000000" cx="252.6429" cy="-25.4172" rx="21.4691" ry="21.4691"/>
<text text-anchor="middle" x="252.6429" y="-21.2172" font-family="Lato, sans-serif" font-size="14.00" fill="#000000">S1</text>
</g>
<!-- S0&#45;&gt;S1 -->
<g id="edge3" class="edge">
<title>
S0-&gt;S1
</title>
<path fill="none" stroke="#000000" d="M138.3084,-25.4172C160.5683,-25.4172 195.5619,-25.4172 220.9758,-25.4172"/>
<polygon fill="#000000" stroke="#000000" points="221.2159,-28.9173 231.2158,-25.4172 221.2158,-21.9173 221.2159,-28.9173"/>
<text text-anchor="middle" x="184.7117" y="-29.6172" font-family="Lato, sans-serif" font-size="14.00" fill="#000000">2</text>
</g>
<!-- S1&#45;&gt;S0 -->
<g id="edge4" class="edge">
<title>
S1-&gt;S0
</title>
<path fill="none" stroke="#000000" d="M234.6347,-13.5116C228.2094,-9.9161 220.7415,-6.4406 213.4096,-4.6172 188.6544,1.5391 180.7691,1.5391 156.0138,-4.6172 151.8897,-5.6429 147.7225,-7.1912 143.722,-8.9897"/>
<polygon fill="#000000" stroke="#000000" points="142.1302,-5.8725 134.7888,-13.5116 145.2916,-12.118 142.1302,-5.8725"/>
<text text-anchor="middle" x="184.7117" y="-8.6172" font-family="Lato, sans-serif" font-size="14.00" fill="#000000">1 | 4 | 5 | 6</text>
</g>
<!-- S1&#45;&gt;S1 -->
<g id="edge5" class="edge">
<title>
S1-&gt;S1
</title>
<path fill="none" stroke="#000000" d="M245.0006,-45.6035C243.9431,-55.6707 246.4906,-64.6505 252.6429,-64.6505 256.5843,-64.6505 259.0462,-60.9652 260.0286,-55.6696"/>
<polygon fill="#000000" stroke="#000000" points="263.5291,-55.6895 260.2853,-45.6035 256.5314,-55.511 263.5291,-55.6895"/>
<text text-anchor="middle" x="252.6429" y="-68.8505" font-family="Lato, sans-serif" font-size="14.00" fill="#000000">2</text>
</g>
<!-- S2 -->
<g id="node4" class="node">
<title>
S2
</title>
<ellipse fill="#d3d3d3" stroke="#000000" cx="342.1095" cy="-25.4172" rx="21.4314" ry="21.4314"/>
<ellipse fill="none" stroke="#000000" cx="342.1095" cy="-25.4172" rx="25.4687" ry="25.4687"/>
<text text-anchor="middle" x="342.1095" y="-21.2172" font-family="Lato, sans-serif" font-size="14.00" fill="#000000">S2</text>
</g>
<!-- S1&#45;&gt;S2 -->
<g id="edge6" class="edge">
<title>
S1-&gt;S2
</title>
<path fill="none" stroke="#000000" d="M274.3003,-25.4172C284.0194,-25.4172 295.7398,-25.4172 306.6828,-25.4172"/>
<polygon fill="#000000" stroke="#000000" points="306.8456,-28.9173 316.8456,-25.4172 306.8456,-21.9173 306.8456,-28.9173"/>
<text text-anchor="middle" x="295.3762" y="-29.6172" font-family="Lato, sans-serif" font-size="14.00" fill="#000000">3</text>
</g>
</g>
</svg>
<p>It’s similar to the automaton for 55 except that when in state S1, we have a 1-in-6 chance to <em>stay</em> at S1.
Failure to advance does not always force us to start over at S0.
That reduced risk gives the sequence 23 an advantage over 55.</p>
<p>So, given a choice between 55 and 23, you should choose to race with 23.</p>
<h2 id="question-2-what-is-the-expected-number-of-die-rolls-before-55-occurs-what-about-for-23">Question 2: What is the expected number of die rolls before 55 occurs? What about for 23?</h2>
<p>To answer this question, we’ll have to use a little algebra and probability theory.
Let’s start by finding the expected number of rolls needed to win with the sequence 55.</p>
<p>Let <span class="math inline">\(x_i\)</span> be the expected number of die rolls needed to reach state S2 from S<span class="math inline">\(i\)</span>.
We want to find <span class="math inline">\(x_0\)</span>, the expected number of rolls needed to reach the “match” state S2 from the initial state S0.
Referring to the diagram for the 55 automaton, we know that <span class="math inline">\(x_0\)</span> must satisfy the equation</p>
<p><span class="math display">\[ x_0 = 1 + \tfrac{5}{6} x_0 + \tfrac{1}{6} x_1. \]</span></p>
<p>That’s because from S0 we must roll one die for a chance to advance, and, once rolled, we have a 5/6 probability of remaining at S0 and a 1/6 probability of advancing to S1.
If we remain at S0, the expected cost to reach S2 is <span class="math inline">\(x_0\)</span>; and if we advance to S1, the expected cost to reach S2 is <span class="math inline">\(x_1\)</span>.
(While this reasoning is intuitive, it is also justified by the law of total expectation.)</p>
<p>Using similar logic, we can find the equation for <span class="math inline">\(x_1\)</span>:</p>
<p><span class="math display">\[ x_1 = 1 + \tfrac{5}{6} x_0 + \tfrac{1}{6} x_2. \]</span></p>
<p>And the equation for <span class="math inline">\(x_2\)</span> is trivial, since it takes zero rolls to reach S2 from S2:</p>
<p><span class="math display">\[ x_2 = 0. \]</span></p>
<p>Now it’s a matter of solving the system of equations for <span class="math inline">\(x_0\)</span>, <span class="math inline">\(x_1\)</span>, and <span class="math inline">\(x_2\)</span>.
Substituting 0 for <span class="math inline">\(x_2\)</span> in the equation for <span class="math inline">\(x_1\)</span> gives</p>
<p><span class="math display">\[ x_1 = 1 + \tfrac{5}{6} x_0. \]</span></p>
<p>And substituting this expression for <span class="math inline">\(x_1\)</span> in the equation for <span class="math inline">\(x_0\)</span> gives</p>
<p><span class="math display">\[ x_0  = 1 + \tfrac{5}{6} x_0  + \tfrac{1}{6} \left( 1 + \tfrac{5}{6} x_0 \right) = \tfrac{7}{6}  + \tfrac{35}{36} x_0. \]</span></p>
<p>Subtracting <span class="math inline">\(\tfrac{35}{36} x_0\)</span> from both sides gives us</p>
<p><span class="math display">\[ \tfrac{1}{36} x_0 = \tfrac{7}{6}. \]</span></p>
<p>And multiplying both sides by 36 solves the equation:</p>
<p><span class="math display">\[ x_0 = \tfrac{7}{6}(36) = (7)(6) = 42. \]</span></p>
<p>So, on average, you need 42 rolls to find your sequence when you choose 55.</p>
<p>Now let’s consider the case when you choose 23 as your sequence.
Referring to the diagram for the 23 automaton, the corresponding equations are:</p>
<p><span class="math display">\[\begin{align}
x_0 &amp; = 1 + \tfrac{5}{6} x_0 + \tfrac{1}{6} x_1 \\
x_1 &amp; = 1 + \tfrac{4}{6} x_0 + \tfrac{1}{6} x_1 + \tfrac{1}{6} x_2 \\
x_2 &amp; = 0
\end{align}\]</span></p>
<p>Solving this system of equations (I’ll skip the algebra), we find that <span class="math inline">\(x_0 = 36\)</span>.</p>
<p>So, on average, you need 36 die rolls to find your sequence when you choose 23.
That’s notably faster than 42 rolls when you choose 55.</p>
</div>
]]></summary>
</entry>
<entry>
    <title>A Great Old-Timey Game-Programming Hack</title>
    <link href="http://blog.moertel.com/posts/2013-12-14-great-old-timey-game-programming-hack.html" />
    <id>http://blog.moertel.com/posts/2013-12-14-great-old-timey-game-programming-hack.html</id>
    <published>2013-12-14T00:00:00Z</published>
    <updated>2013-12-14T00:00:00Z</updated>
    <summary type="html"><![CDATA[<div class="info byline">By
  <span itemprop="author" itemscope itemtype="http://schema.org/Person"><a href="https://blog.moertel.com/about.html" itemprop="url"><span itemprop="name">Tom Moertel</span></a></span></div>
<div class="info">Posted on <time itemprop="datePublished" datetime="2013-12-14">December 14, 2013</time></div>
<div class="tags">Tags: <span itemprop="keywords"><a title="All pages tagged &#39;assembly&#39;." href="/tags/assembly.html" rel="tag">assembly</a>, <a title="All pages tagged &#39;6809&#39;." href="/tags/6809.html" rel="tag">6809</a>, <a title="All pages tagged &#39;games&#39;." href="/tags/games.html" rel="tag">games</a>, <a title="All pages tagged &#39;hacks&#39;." href="/tags/hacks.html" rel="tag">hacks</a></span></div>

<div itemprop="articleBody">
<p>A long time ago, when I was a college undergrad, I spent some time working on computer video games.
This was in the 8-bit PC era, when the gaming hardware was almost impossibly slow by today’s standards.</p>
<p>It might not surprise you, then, to learn that game programmers of yore did all sorts of crazy things to make their games run at playable speeds.
Crazy, crazy things.</p>
<p>This is a story about one of those things.</p>
<p>While I’ve done my best to recall the important details, I may have gotten some things wrong.
If I have, please forgive me.
It was a long time ago.</p>
<p>(<em>Note: This post contains some small images as inline-data URLs that some feed readers apparently don’t handle well. If you can’t see the images in your feed, click through to the original article to see them.</em>)</p>
<p><em>Update 2013-12-16: There have been lots of great comments about this story on <a href="https://news.ycombinator.com/item?id=6913467">Hacker News</a> and <a href="http://www.reddit.com/r/programming/comments/1t05uy/a_great_oldtimey_gameprogramming_hack/">Proggit</a>, as well as <a href="http://blog.moertel.com/posts/2013-12-14-great-old-timey-game-programming-hack.html#disqus_thread">here on the blog</a>. Thanks for sharing, everyone!</em></p>
<h3 id="the-set-up">The set-up</h3>
<p>A friend of mine, a gifted programmer, was nearly finished with a new game.
Somehow, he had squeezed into a 1980’s-era PC, pretty faithfully, what was at the time a graphically impressive coin-op game popular at the arcades.</p>
<p>The only problem was that his version of the game was unplayable.
It was too slow, and the choppy motion broke the player’s immersion.
It was after all a side-scroller.</p>
<p>My friend, who had been working on the game while also taking a full load of college courses, was starting to seem a little frazzled.
Fearing that he had missed some easy optimization, he asked me to take a look at the code.</p>
<p>I did. But there was no easy optimization to be had.</p>
<p>The code was <em>tight</em>.
Everywhere I looked, he had already done all the things I could think of.
Loops had been unrolled.
Unneeded draws had been eliminated.
All evidence of waste had vanished.</p>
<p>And, with them, our hopes of an easy fix.</p>
<p>But what about a hard fix?
A crazy fix?</p>
<p>Well, that was always a possibility.</p>
<p>Before I get to the crazy stuff, however, I should back up and explain how graphics hardware worked back then.</p>
<h3 id="how-graphics-hardware-worked-back-then">How graphics hardware worked back then</h3>
<p>Here’s the quick version:
The graphics hardware didn’t do jack squat.</p>
<p>On the PC in question, if you wanted to draw something onto the screen, you had to do that yourself, byte by byte.
No texture mappers, no blitters.
Just bytes.
Bytes that you had to move yourself.</p>
<p>Fun.</p>
<p>My friend’s game spent most of its time redrawing the background.
(Again: side-scroller.)
For every frame, it had to draw nearly a screen’s worth of background tiles, shifted for the player’s position.</p>
<p>If my memory serves me, each tile was 28 by 28 pixels.
Each pixel was one of 16 colors, taking half a byte to represent.
Therefore, tiles were represented in memory as 28 contiguous rows of 14 bytes each.
The first 14 bytes represented the first row, the second 14 bytes the second row, and so on.</p>
<p>The screen, however, was 320 pixels wide by 240 pixels high.
In memory, then, a screen buffer was laid out as 240 contiguous rows of 160 bytes each.</p>
<p>So when copying a tile at address <em>X</em> onto a screen buffer location starting at address <em>Y</em>, you had to copy 28 rows.
To copy each row, you had to copy 14 bytes.
Fortunately, the processor for this game was a 6809, which had a few 16-bit index registers and delightful “auto-increment” addressing modes (sort of like applying the postfix <code>++</code> operator to pointers in C).
That meant you could copy 4 pixels at a time while advancing both the <em>X</em> and <em>Y</em> registers in passing:</p>
<pre><code>    LDU ,X++     ; read a 2-byte word (= 4 pixels) from source tile
    STU ,Y++     ; write it to screen buffer</code></pre>
<p>To copy a full row, you had to do that seven times, so you might sandwich those lines within a 7-count loop:</p>
<pre><code>    LDB #7       ; remaining words &lt;- tile width in words
@LOOP
    LDU ,X++     ; read a 2-byte word (= 4 pixels) from source tile
    STU ,Y++     ; write it to screen buffer
    DECB         ; reduce remaining-word count
    BNE @LOOP    ; loop while words remain</code></pre>
<p>When you were done copying the row, you needed to advance the destination pointer <em>Y</em> so that it pointed to the starting address for the next row you would draw into the screen buffer.
Since the screen buffer was 160 bytes wide and a tile was only 14 bytes wide, that meant you had to add their difference to <em>Y</em>:</p>
<pre><code>    LEAY 160-14,Y</code></pre>
<p>And, just like that, you have copied a row onto the screen.</p>
<p>That’s one.
To copy a full tile, you need to do the same thing 28 times.
So, in turn, you sandwich <em>that</em> code within a 28-count loop.</p>
<p>Putting it all together, and giving names to the important numbers, you might end up with a subroutine like this:</p>
<pre><code>;;; important constants

SCRW = 160          ; screen-buffer width in bytes (= 320 4-bit pixels)
TILW =  14          ; background-tile width in bytes (= 28 4-bit pixels)
TILH =  28          ; background-tile height in rows
WOFF = SCRW - TILW  ; s-b offset from end of one tile row to start of next


COPYTILE
;;;
;;; Copy a 28x28 background tile into a screen buffer.
;;; Arguments:
;;;   X = starting address of background tile
;;;   Y = starting address of destination in screen buffer
;;;
    LDA #TILH               ; remaining rows &lt;- tile height
@COPYROW
    LDB #TILW/2             ; remaining words &lt;- tile width in words
@LOOP
    LDU ,X++                ; read a word (= 4 pixels) from source tile
    STU ,Y++                ; write it to screen buffer
    DECB                    ; reduce remaining-word count
    BNE @LOOP               ; loop while words remain
    ;;
    LEAY WOFF,Y             ; advance dst ptr to start of next dst row
    DECA                    ; reduce remaining-row count
    BNE @COPYROW            ; loop while rows remain
    ;;
    RTS                     ; done! return to caller</code></pre>
<p>And that code would be fine.</p>
<p>If you didn’t care about speed.</p>
<h3 id="caring-about-speed">Caring about speed</h3>
<p>Knowing that the game is probably going to spend most of its time running that code, you do what any good programmer would: start counting cycles.
Here’s the inner loop again, with setup and finishing, annotated with cycle counts:</p>
<pre><code>    LDB #TILW/2             ; 2 cycles (set-up)
@LOOP
    LDU ,X++                ; 8
    STU ,Y++                ; 8
    DECB                    ; 2
    BNE @LOOP               ; 3
    ;;
    LEAY WOFF,Y             ; 8 (finishing)</code></pre>
<p>Looking at those counts within the loop, you’re not likely to miss that you’re burning 21 cycles to copy just 4 pixels.
To copy a full row, then, works out to 2 cycles + (7 iterations) * (21 cycles/iteration) + 8 cycles = 157 cycles.
Ouch.</p>
<p>But this isn’t your first time at the keyboard.
You know what to do.
Unroll that loop!</p>
<pre><code>    LDU ,X++                ; 8 cycles
    STU ,Y++                ; 8
    LDU ,X++                ; 8
    STU ,Y++                ; 8
    LDU ,X++                ; 8
    STU ,Y++                ; 8
    LDU ,X++                ; 8
    STU ,Y++                ; 8
    LDU ,X++                ; 8
    STU ,Y++                ; 8
    LDU ,X++                ; 8
    STU ,Y++                ; 8
    LDU ,X++                ; 8
    STU ,Y++                ; 8
    LEAY WOFF,Y             ; 8 (finishing)</code></pre>
<p>Now, with the loop overhead reduced to zero – you’ve even eliminated the set-up – each row takes only 7 * (8 + 8) + 8 = 120 cycles.
That’s a 30-percent speed-up.
Pretty good.</p>
<p>And that’s where most programmers probably would have left it.</p>
<p>But not my friend.</p>
<p>He knew that those <code>++</code> operations were costly, 3 cycles apiece.
And, with the loop unrolled, he also knew exactly where each word to be read or written was located with respect to <em>X</em> or <em>Y</em>.
So he cleverly replaced those 3-cycle post-increments with exact offsets.
They cost only 1 cycle apiece, and the 0 offset is actually free:</p>
<pre><code>    LDU  ,X                 ; 5 cycles
    STU  ,Y                 ; 5
    LDU 2,X                 ; 6
    STU 2,Y                 ; 6
    LDU 4,X                 ; 6
    STU 4,Y                 ; 6
    LDU 6,X                 ; 6
    STU 6,Y                 ; 6
    LDU 8,X                 ; 6
    STU 8,Y                 ; 6
    LDU 10,X                ; 6
    STU 10,Y                ; 6
    LDU 12,X                ; 6
    STU 12,Y                ; 6
    LEAX TILW,X             ; 8 (finishing)
    LEAY SCRW,Y             ; 8 (finishing)</code></pre>
<p>With his optimizations, the cycle count per row had been reduced to (5 + 5) + 6 * (6 + 6) + (8 + 8) = 98 cycles.
Compared to the original code, that’s 60 percent faster:</p>
<pre><code>original_speed  = (1*row) / (157*cycle)
optimized_speed = (1*row) /  (98*cycle)

speed_up  =  optimized_speed / original_speed - 1  =  157 / 98 - 1  =  0.60.</code></pre>
<p>Putting it all together – and, again, I’m going by memory so the code might have been slightly different – the copy-tile subroutine looked something like this, and it copied a full tile, all 28 rows, in a lean 2893 cycles:</p>
<pre><code>COPYTILE2
;;;
;;; Copy a 28x28 screen tile into a screen buffer.
;;; Arguments:
;;;   X = starting address of background tile
;;;   Y = starting address of destination in screen buffer
;;; Execution time:
;;;   4 + 28 * (82 + 8 + 8 + 2 + 3) + 5 = 2893 cycles
;;;
    LDA #TILH      ; initialize row count (4 cycles)
    ;;
@COPY1
    ;; unroll inner loop (copies one row of 28 pixels in 82 cycles)
    LDU ,X         ; (1) read 4 pixels (5 cycles)
    STU ,Y         ;     write 4 pixels (5 cycles)
    LDU 2,X        ; (2) (6 cycles)
    STU 2,Y        ;     (6 cycles)
    LDU 4,X        ; (3) ...
    STU 4,Y        ;     ...
    LDU 6,X        ; (4)
    STU 6,Y        ;
    LDU 8,X        ; (5)
    STU 8,Y        ;
    LDU 10,X       ; (6)
    STU 10,Y       ;
    LDU 12,X       ; (7)
    STU 12,Y       ;
    ;;
    LEAX TILW,X    ; advance src to start of next row (8 cycles)
    LEAY SCRW,Y    ; advance dst to start of next row (8 cycles)
    DECA           ; reduce remaining count by one (2 cycles)
    BNE @COPY1     ; loop while rows remain (3 cycles)
    ;;
    RTS            ; done!  return to caller (5 cycles)</code></pre>
<p>This code, all in all, was 60% faster than the naive <code>COPYTILE</code> code we started out with.</p>
<p>But that wasn’t fast enough.
Not nearly.</p>
<p>So when my friend showed me his code and asked if I could make it faster, I really wanted to help him.
I really wanted to be able to answer yes.</p>
<p>But I had to answer no.
I hated giving that answer.
But, studying the code, I couldn’t see any way to speed it up.</p>
<p>And yet, the hook was set.</p>
<p>Later, I couldn’t get the problem out of my head.
Maybe I had missed something.
I had grown up on Apple II computers and their 6502 processors.
My friend’s code, however, was for the 6809.
Maybe it afforded optimizations that I didn’t know about.</p>
<p>With renewing optimism, I dialed into the university’s library to access the card catalog.
(These were the days before the World Wide Web.)
Through a VT220 terminal emulator, I searched for books about the 6809.</p>
<p>There was one.
A 6809 microprocessor manual.
It was in the engineering library.
Luckily, I was an engineering student and had sign-out privileges.</p>
<p>Just like that, I was off to the library.</p>
<h3 id="a-crazy-idea">A crazy idea</h3>
<p>When I got to the engineering library, I found the book right where it was supposed to be, looking a little beat up:</p>
<p><a href="https://archive.org/details/bitsavers_motorola68_13419254/mode/2up"><em>The MC6809-MC6809E Microprocessor Programming Manual</em></a>.</p>
<p>Not bothering to find a chair, I flipped through the pages standing, looking for some oddball, 6809-specific instruction that might throw a lot of bytes and throw them fast.
Page after page, though, turned up nothing.</p>
<p>Then I came upon <a href="https://archive.org/details/bitsavers_motorola68_13419254/page/n99/mode/2up?q=pshs">PSHS</a>.
“Push registers on the hardware stack.”</p>
<p>On my beloved 6502, if you wanted to save registers on the stack, you had to do it one register at a time and, even then, by transferring them through the accumulator.
It was slow and expensive.
So I had learned to avoid the stack when speed mattered.</p>
<p>But on the 6809 you could save <em>all</em> of the registers – or any subset of them – with <em>a single instruction</em>.
And, amazingly, it cost a mere 5 cycles, plus 1 cycle more per byte pushed.</p>
<p>Since the processor had three 16-bit general-purpose registers – <em>D</em>, <em>X</em>, and <em>Y</em> – I could load them up and then use a single <code>PSHS</code> instruction to write 6 bytes in just 11 cycles.
The corresponding pull instruction, <a href="https://archive.org/details/bitsavers_motorola68_13419254/page/n101/mode/2up?q=puls"><code>PULS</code></a>, had the same low cost.</p>
<p>Further, the 6809 had <em>two</em> stack registers, <em>S</em> and <em>U</em>.
I could use one as a source pointer and the other as a destination pointer.
In theory, with a single <code>PULS</code>/<code>PSHU</code> pair, I could copy 6 bytes in 22 cycles.</p>
<p>That’s crazy, crazy fast.</p>
<p>Excitement mounting, I headed for the front desk and reached for my student ID.
I was going to sign out that wonderful little book for further study.</p>
<h3 id="a-crazy-plan">A crazy plan</h3>
<p>Walking back to the dorms, I formed my plan.</p>
<p>I would save the <em>S</em> and <em>U</em> registers somewhere, and then point <em>S</em> at the background tile and <em>U</em> at the screen buffer.
Then I would pull from <em>S</em> and push to <em>U</em>, copying 6 bytes at a time, using <em>D</em>, <em>X</em>, and <em>Y</em> as go-betweens.
To copy the 14 bytes that make up a row would require three such iterations, which unrolled would be about 60 cycles.</p>
<p>When I reached my room, I found a piece of paper and sketched it out:</p>
<pre><code>    PULS D,X,Y    ; first 6 bytes    11 cycles
    PSHU D,X,Y    ;                  11
    PULS D,X,Y    ; second 6 bytes   11
    PSHU D,X,Y    ;                  11
    PULS D        ; final 2 bytes     7
    PSHU D        ;                   7
    LEAU -WOFF,U  ; advance dst ptr   8</code></pre>
<p>Just 66 cycles, including the after-row adjustment to <em>U</em> that prepares it for the next row.
(Note that the adjustment is now negative.)
For comparison, the naive row-copy loop that we looked at earlier took 157 cycles to do the same thing.
And my friend’s optimized code took 98.
Already, this crazy idea was looking like a big win.</p>
<p>Still, that last <code>PULS</code>/<code>PSHU</code> pair!
What a clunker.
It was needed to handle the final two bytes per row, since the rows were 28 pixels = 14 bytes wide, and 6 doesn’t divide 14 evenly.</p>
<p><em>That darn remainder of 2 bytes!</em></p>
<p>If only the game had used 24-by-24 tiles instead…
But it hadn’t, so I pored through the manual, looking for a way to lower the cost of that inevitable last pair.</p>
<p>And then, by surprise, I struck gold!
It was another 6809 oddity, the <em>DP</em> register.</p>
<p>On the 6502 and most 8-bit processors of the era, the lowest 256 bytes of memory was called the <em>zero page</em>.
The zero page was special because its memory locations had single-byte addresses and could be accessed with shorter and usually faster instructions.</p>
<p>The 6809’s designers took this idea one step further.
They let you use the <em>DP</em> register to designate <em>any</em> page as the zero page, which they called the “direct page.”</p>
<p>But none of my byte-slinging instructions needed to use the direct page.
That meant I could use the <em>DP</em> register as an additional one-byte go-between.
Now I could copy 7 bytes per pull-push pair!</p>
<p>And 14 <em>is</em> evenly divisible by 7.</p>
<p>With this change, I could copy a whole 28-pixel row and advance to the next in just 5 instructions:</p>
<pre><code>    PULS D,X,Y,DP  ; first 7 bytes    12 cycles
    PSHU D,X,Y,DP  ;                  12
    PULS D,X,Y,DP  ; second 7 bytes   12
    PSHU D,X,Y,DP  ;                  12
    LEAU -WOFF,U   ; advance dst ptr   8</code></pre>
<p>56 cycles!
Fifty. Six. Cycles.</p>
<p>That bit of code made me feel just <em>awesome</em>.
I had managed to harness every single available register on the machine for byte-slinging!
<em>D</em>, <em>X</em>, <em>Y</em>, <em>U</em>, <em>S</em>, and even the oddball <em>DP</em> – they were all fully engaged.</p>
<p>I was feeling great about this solution.</p>
<p>Except for just one thing…</p>
<h3 id="ill-have-a-little-more-crazy-please">I’ll have a little more crazy, please</h3>
<p>If you’re acquainted with stacks, you may have noticed a subtle flaw in my brilliant plan:</p>
<p><em>Pushes and pulls work in opposite directions.</em></p>
<p>See, I lied about that little block of code I just showed you.
It didn’t <em>actually</em> copy one row from a background tile onto the screen.
What it actually did was break the row into two 7-byte blocks – I’ll call them “septets” – and then draw them onto the screen <em>swapped</em>.</p>
<p>Recall that tile rows were laid out sensibly in memory as 14 contiguous bytes, like this:</p>
<pre><code>+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0   1   2   3   4   5   6   7   8   9   A   B   C   D |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+</code></pre>
<p>So when you pulled-and-pushed a row onto the screen in 7-byte septets, it would get horizontally split like this:</p>
<pre><code>+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 7   8   9   A   B   C   D | 0   1   2   3   4   5   6 |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+</code></pre>
<p>The first septet was now second, and the second now first.
Note, however, that the bytes within each septet were unchanged; they retained their original ordering.</p>
<p>Further, if you ran the row-copy code multiple times, the vertical stack of rows you copied would end up being upside down.
That’s because the code uses pushes to write the rows onto the screen.
Pushes move the stack pointer toward lower addresses, and lower addresses correspond to screen lines higher up on the display.</p>
<p>To visualize the havoc that this row-reversal and septet-swapping would wreak, let’s say that you had the following 28-by-28 tile, representing a key:</p>
<p><img style="image-rendering: pixelated; border: solid 1px black; width: 50%; height: 50%;" src="data:image/gif;base64,R0lGODlhHAAcAIQQAE88FHZNAH19crB7ApCAT5+BNL6bN8+iIN2sEMqwavTGIfDNTfbbcPflmPbsvPz43v///////////////////////////////////////////////////////////////yH5BAEKABAALAAAAAAcABwAAAXOICSOZGmeaKqupeK6iMGiytI4TcMshjCTisZjSGzwfqLg0JEoJBwPY+KnYCwNBULB8GQcpiwFlDkYEATbHOIwUywThsOBABjkFogZ4uHA5eQEdg54en05OQxrCGpsLIs6hwwuDH8yjjoMmZkLlEYGBI6aopl3cz4qB5ScozsLBwWnKakNCK0Lt7cHWLEoswi4CGXCWSypiQvCA0gjA5kwysvMmQjP0SLNiS8DAdbNeM/c0QOKwWXh4tQKBwEAANYiAenr7yMBA3Lb9Pr7PyEAOw==" alt="A pixel-art image of a key."/></p>
<p>If you had used 28 applications of my row-copy code to draw it onto the screen, you would have ended up with this not-a-key:</p>
<p><img style="image-rendering: pixelated; border: solid 1px black; width: 50%; height: 50%;" src="data:image/gif;base64,R0lGODlhHAAcAIQQAE88FHZNAH19crB7ApCAT5+BNL6bN8+iIN2sEMqwavTGIfDNTfbbcPflmPbsvPz43v///////////////////////////////////////////////////////////////yH5BAEKABAALAAAAAAcABwAAAXQICSOZGmeaKquZTAcxxCwaoAoyjHT6HDnAQCAZ/IdEIiBckccDRiLX5LZhDwZP4VMNKhepV1IUqksEFZXXFK0aLsPhoJAdWBgF2S7Hro4yOkMDQhue3YNDX0Ecyl1glCFC4GIBmeAiIaHDDiScCwIeoeZCEeHozQIDQwOoTAEAw0OUaeHDgkGrQCviAinDr4JSooFBqUHpw8OyHEEwwkODAcJNAoP1cgJBc4PiNLTyQ3W1pNECgwPCguwmQsGizwKDRA48wgGVSIK9/r7/P0sIQA7" alt="A distorted pixel-art image of a key; the original image has been reflected across its horizontal axis, cut in half from top to bottom, and had its left and right halves swapped."/></p>
<p>So . . . <em>that</em> was a problem.</p>
<p>But this problem, too, had an elegant solution!</p>
<p>To review:</p>
<ul>
<li>the rows were upside down</li>
<li>the septets within each row were swapped</li>
<li>but the bytes within each septet were unchanged</li>
</ul>
<p>The breakthrough was to see “upside down” and “swapped” as two instances of the same thing. <em>Reversal.</em></p>
<p>Reversal has this handy property: it’s an <a href="http://en.wikipedia.org/wiki/Involution_(mathematics)"><em>involution</em></a>.
Reverse something twice, and you get back the original.
With this in mind, I reasoned that if the tiles were pre-processed to reverse their rows and then the septets within the rows, the tiles would end up looking fine when they hit the screen.
The mangling that occurred during copying and pre-processing would, in effect, undo each other.</p>
<p>Working it out on paper, I also discovered that the pre-processing step could ignore the rows and just treat the tiles as one long, linear sequence of septets.<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a>
To see why this is so, let’s consider a small tile of 2 rows of 2 septets each:</p>
<pre><code>+---+---+
| a   b |
+---+---+
| c   d |
+---+---+</code></pre>
<p>In memory, it would be laid out in row-major order, that is, as 4 sequential septets:</p>
<pre><code>+---+---+---+---+
| a   b | c   d |
+---+---+---+---+</code></pre>
<p>So, reversing the rows and then swapping the septets within each row is the same as just reversing the septet order in memory:</p>
<pre><code>+---+---+                  +---+---+                  +---+---+
| a   b |                  | c   d |                  | d   c |
+---+---+        =======&gt;  +---+---+        =======&gt;  +---+---+
| c   d |        reverse   | a   b |        swap      | b   a |
+---+---+        rows      +---+---+        septets   +---+---+

+---+---+---+---+          +---+---+---+---+          +---+---+---+---+
| a   b | c   d |          | c   d | a   b |          | d   c | b   a |
+---+---+---+---+          +---+---+---+---+          +---+---+---+---+</code></pre>
<p>So the solution to the mangling problem turned out to be a simple, one-time pre-processing step:
Just break each tile into septets and reverse their order.</p>
<p>Knowing that the mangling problem could be solved, I was feeling good about my overall idea.
I couldn’t see any other problems, so I sketched out a new copy-tile subroutine to present to my friend.
Since the row-copy logic was now just 5 instructions, I used it 4 times, unrolling the sole remaining loop a bit.
Now, instead of copying 28 single rows, I copied 7 quad-rows.</p>
<p>The code looked something like this:</p>
<pre><code>COPYTILE3
;;;
;;; Copy a 28x28 screen tile into a screen buffer.
;;; Arguments:
;;;   X = starting address of *septet-reversed* background tile
;;;   Y = starting address of destination in screen buffer
;;; Execution time:
;;;   34 + 7 * (224 + 7 + 3) + 7 + 10 = 1689 cycles
;;;
    ;; setup:  34 cycles
    PSHS U,DP      ; save U and DP (8 cycles)
    STS &gt;SSAVE     ; save S (7 cycles)
    ;;
    LDA #TILH/4    ; initial quad-row count (2 cycles)
    STA &gt;ROWCT     ; (5 cycles)
    ;;
    LEAS ,X                    ; initialize src ptr (4 cycles)
    LEAU (TILH-1)*SCRW+TILW,Y  ; initialize dst ptr (8 cycles)
    ;;
@COPY1
    ;; copy four rows of 28 pixels in 4 * (48 + 8) = 224 cycles
    PULS X,Y,D,DP
    PSHU X,Y,D,DP
    PULS X,Y,D,DP
    PSHU X,Y,D,DP
    LEAU -WOFF,U
    PULS X,Y,D,DP
    PSHU X,Y,D,DP
    PULS X,Y,D,DP
    PSHU X,Y,D,DP
    LEAU -WOFF,U
    PULS X,Y,D,DP
    PSHU X,Y,D,DP
    PULS X,Y,D,DP
    PSHU X,Y,D,DP
    LEAU -WOFF,U
    PULS X,Y,D,DP
    PSHU X,Y,D,DP
    PULS X,Y,D,DP
    PSHU X,Y,D,DP
    LEAU -WOFF,U
    ;;
    DEC &gt;ROWCT     ; reduce remaining quad-row count by one (7 cycles)
    BNE @COPY1     ; loop while quad-rows remain (3 cycles)
    ;;
    LDS  &gt;SSAVE    ; restore S (7 cycles)
    PULS U,DP,PC   ; restore regs and return to caller (10 cycles)

SSAVE ZMD  1       ; stash for saving S while drawing
ROWCT ZMB  1       ; var: remaining rows to copy</code></pre>
<p>Adding up the cycles, the total was just 1689.
I had shaved almost 1200 cycles off of my friend’s code.
That’s a 70 percent speed-up!</p>
<p>Beaming, I went to show my friend.</p>
<h3 id="the-acid-test">The acid test</h3>
<p>When I caught up with my friend and said I’d figured out how to make the copy-tile routine 70% faster, his face lit up.
When I explained about the whole septets-and-mangling thing, though, his face unlit, doubts rising.
But when I showed him the code, he got it.
It clicked.</p>
<p>“Let’s try it out!” he said.</p>
<p>In about a half hour’s work, he had the code integrated into the game.
After a rebuild and reboot, the game was loading.</p>
<p>The title screen came up.</p>
<p>He was into the game.</p>
<p>And . . .</p>
<p><em>Holy crap!</em></p>
<p>The game seemed amazingly fast.
In truth, it was probably only a third to a half faster, but that was enough.
Some important perceptual threshold had been crossed.
The game-play was now smooth, natural.
You could feel the difference.</p>
<p>We just sat there, playing the game and grinning.
It was a good day.</p>
<p>But it would not last.</p>
<h3 id="in-for-a-penny-.-.-.">In for a penny . . .</h3>
<p>A few days after the speed problem was behind us – or so we thought – the game’s finishing touches were applied.
One of those finishing touches was sampled sound effects.
This required feeding byte-sized morsels to the audio-output DAC a few thousand times each second.
To schedule the feedings, my friend had turned on a hardware-clocked interrupt.</p>
<p>Worked like a charm.
The sounds sounded great, and everything was perfect.</p>
<p>Except for one thing.</p>
<p>After playing into the game one night, we noticed that some of the tiles were getting corrupted.
The more we played, the more corruption we saw.</p>
<p><em>Oh, no.</em></p>
<p>Something very bad was happening.</p>
<p>And then it hit us.</p>
<p>What happens if an interrupt goes off during the copy-tile routine?</p>
<p>The processor, to prepare for calling the interrupt handler, would push its current state onto the system stack.
But, during the copy-tile routine, <em>there was no system stack</em>.
The system-stack register <em>S</em> had been commandeered.
And where was it pointing?
Right into the memory buffer that held the reference tiles!</p>
<p>Oops.</p>
<p>We slumped.
After all that effort, to think that our all-important speed-up was causing memory corruption…</p>
<p>Needing a break, we walked to an all-night diner near campus to eat and think.
Over pancakes and bacon, scrapple and grilled stickies, we talked through the problem.</p>
<p>There were only two stack registers, and without commandeering both of them, the copy-tile routine wouldn’t be nearly as fast.
There was no way, then, to return the <em>S</em> register to the system without losing our hard-won speed.
But there was also no way that we could get reliable sound without using interrupts.</p>
<p>One way or another, then, interrupts were going to be triggered.
And, when they were, if copy-tile was running, whatever <em>S</em> was pointing to was going to get hammered.</p>
<p>“How can we prevent the corruption?” I asked.</p>
<p>We sat there, ignoring our food, the question hanging in the air.</p>
<p>Suddenly, my friend slapped the table.
He had it.</p>
<p>“Don’t prevent it!” he said.</p>
<p>“What?” I asked.</p>
<p>“Don’t prevent the corruption,” he explained.
“Let it happen.
Just not to the reference tiles.”</p>
<p>It was simple, really.
He continued:</p>
<p>“Just swap <em>S</em> and <em>U</em> in the copy-tile routine.
<em>U</em> will point at the reference tiles, and <em>S</em> at the screen buffer.
If an interrupt goes off, the corruption will happen where <em>S</em> is pointing – <em>on the screen.</em>
The corruption will last only until we redraw the next frame.”</p>
<p>“That’s brilliant!” I said.</p>
<p>Eager to try his solution, we quickly finished our meal.</p>
<h3 id="in-for-a-pound">. . . In for a pound</h3>
<p>Walking back to the dorms, though, it bothered us that players might see screen glitches, even if for just a frame.
We both felt that there had to be some way to make the hack perfect, if only we could find it.</p>
<p>Later that night, we found it.</p>
<p>Again, it was simple once we saw it.
All we had to do was change the tile ordering.
Instead of placing the tiles onto the screen from top to bottom, left to right, we would go right to left, bottom to top.
In other words, from high addresses to low addresses.</p>
<p>That way, when an interrupt went off and corrupted the memory locations immediately before the tile we were currently drawing, the corruption would be fixed when the very next tile was drawn.
And since tile-drawing took place in a buffer that wasn’t displayed until it was fully drawn (and the vertical refresh had occurred), nobody would ever see the corruption.<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a></p>
<p>It was the perfect hack!</p>
<h3 id="to-the-old-days">To the old days!</h3>
<p>And that’s the way things were.
The challenge wasn’t overwhelming complexity, as it is today.
The challenge was cramming your ideas into machines so slow, so limited that most ideas didn’t fit.</p>
<p>You had to bang your ideas around, twist them, turn them, searching for something, <em>anything</em> that would help you squeeze them into the machine.
Sometimes you found it, and you got one step closer to realizing your ideas.
Sometimes you didn’t.</p>
<p>But the search was always instructive.</p>
<p>In this case, the search yielded several small victories that, together, solved the problem.
But when you consider all the things we had to do to make that darn game fast enough, it does seem crazy.</p>
<p>We started with a tile-copying subroutine whose core was a hand-tuned, unrolled loop of machine instructions.
Then, to buy a 70-percent speed-up:</p>
<ol type="1">
<li>We replaced this subroutine with a very special manifestation of insanity that commandeered both stack pointers and used pulls and pushes, and every single available register, to draw tiles <em>upside down and horizontally broken</em>.</li>
<li>Then we pre-processed the tiles so that drawing them would actually <em>fix</em> them.</li>
<li>But then – <em>dammit!</em> – interrupts that occurred during drawing could corrupt the reference tiles.</li>
<li>So, to protect the reference tiles, we corrupted the screen buffer instead.</li>
<li>But that corruption would be visible.</li>
<li>So we changed the tile-placement order to repair – <em>on the fly</em> – any corruption that might have occurred, before it could ever be displayed.</li>
<li>And it all worked!</li>
</ol>
<p>We did <em>that.</em>
For 70 percent.</p>
<p>And it was <em>so</em> worth it.</p>
<h3 id="tell-em-if-you-got-em">Tell ’em if you got ’em</h3>
<p>Anyway, I wanted to share that story with you for two reasons.</p>
<p>The first is that it’s a fun story.
It’s one my earliest memories of struggling with a messy computing problem and, through dogged persistence, zeroing in on a solution that was both effective and elegant.
The result was powerfully satisfying.</p>
<p>The struggle, I learned, is worth it.</p>
<p>The second reason is to encourage you to tell your stories.
I know that, “back in the day,” most video games probably gave rise to many stories like this one.
I’d love to hear them.
But too many of them are lost, having faded out of memory before anyone thought to preserve them.</p>
<p>If you have a story, please don’t wait.
Tell it.
Every day you wait, it gets harder.</p>
<p>Tell it.</p>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>Exercise: Prove that for all finite sequences of finite sequences, applying (<em>reverse</em> ⋅ <em>concat</em>) is the same as applying (<em>concat</em> ⋅ <em>reverse</em> ⋅ <em>map</em> <em>reverse</em>).<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn2"><p>We also had to make sure that nothing valuable was stored in the memory locations just before a screen buffer.
They could, in theory, also be corrupted if an interrupt occurred while placing the top-left septet of the top-left corner tile.
This septet’s address corresponded to the start of the buffer.<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>
</div>
]]></summary>
</entry>
<entry>
    <title>Tricks of the trade: Recursion to Iteration, Part 4: The Trampoline</title>
    <link href="http://blog.moertel.com/posts/2013-06-12-recursion-to-iteration-4-trampolines.html" />
    <id>http://blog.moertel.com/posts/2013-06-12-recursion-to-iteration-4-trampolines.html</id>
    <published>2013-06-12T00:00:00Z</published>
    <updated>2013-06-12T00:00:00Z</updated>
    <summary type="html"><![CDATA[<div class="info byline">By
  <span itemprop="author" itemscope itemtype="http://schema.org/Person"><a href="https://blog.moertel.com/about.html" itemprop="url"><span itemprop="name">Tom Moertel</span></a></span></div>
<div class="info">Posted on <time itemprop="datePublished" datetime="2013-06-12">June 12, 2013</time></div>
<div class="tags">Tags: <span itemprop="keywords"><a title="All pages tagged &#39;programming&#39;." href="/tags/programming.html" rel="tag">programming</a>, <a title="All pages tagged &#39;recursion&#39;." href="/tags/recursion.html" rel="tag">recursion</a>, <a title="All pages tagged &#39;iteration&#39;." href="/tags/iteration.html" rel="tag">iteration</a>, <a title="All pages tagged &#39;python&#39;." href="/tags/python.html" rel="tag">python</a>, <a title="All pages tagged &#39;recursion-to-iteration series&#39;." href="/tags/recursion-to-iteration%20series.html" rel="tag">recursion-to-iteration series</a>, <a title="All pages tagged &#39;tail calls&#39;." href="/tags/tail%20calls.html" rel="tag">tail calls</a>, <a title="All pages tagged &#39;data structures&#39;." href="/tags/data%20structures.html" rel="tag">data structures</a>, <a title="All pages tagged &#39;trampolines&#39;." href="/tags/trampolines.html" rel="tag">trampolines</a></span></div>

<div itemprop="articleBody">
<p>This is the fourth article in <a href="http://blog.moertel.com/tags/recursion-to-iteration%20series.html">a series on converting recursive algorithms into iterative algorithms</a>.
If you haven’t read the earlier articles first, you may want to do so before continuing.</p>
<p>In <a href="http://blog.moertel.com/posts/2013-05-11-recursive-to-iterative.html">the first article of our series</a>, we showed that if you can convert an algorithm’s recursive calls into tail calls, you can eliminate those tail calls to create an iterative version of the algorithm using The Simple Method.
In this article, we’ll look at another way to eliminate tail calls: the <em>trampoline</em>.</p>
<p>The idea behind the trampoline is this: before making a tail call, manually remove the current execution frame from the stack, eliminating stack build-up.</p>
<h3 id="execution-frames-and-the-stack">Execution frames and the stack</h3>
<p>To understand why we might want to manually remove an execution frame, let’s think about what happens when we call a function.
The language runtime needs some place to store housekeeping information and any local variables the function may use, so it allocates a new execution frame on the stack.
Then it turns control over to the function.
When the function is done, it executes a <code>return</code> statement.
This statement tells the runtime to remove the execution frame from the stack and to give control (and any result) back to the caller.</p>
<p>But what if the function doesn’t return right away?
What if it makes another function call instead?
In that case, the runtime must create a new execution frame for <em>that</em> call and push it onto the stack, on top of the current frame.
If the function ends up calling itself many times recursively, each call will add another frame to the stack, and pretty soon we will have eaten up a lot of stack space.</p>
<h3 id="eliminating-stack-build-up">Eliminating stack build-up</h3>
<p>To avoid this problem, some programming languages guarantee that they will recycle the current execution frame whenever a function makes a tail call.
That is, if the function calls some other function (or itself recursively) and just returns that function’s result verbatim, that’s a tail call.
In that case, the runtime will recycle the current function’s execution frame before transferring control to the other function, making it so that the other function will return its result directly to the original function’s caller.
This process is called <em>tail-call elimination</em>.</p>
<p>But in languages like Python that don’t offer tail-call elimination, every call, even if it’s a tail call, pushes a new frame onto the stack.
So if we want to prevent stack build-up, we must somehow eliminate the current frame from the stack ourselves, before making a tail call.</p>
<p>But how?
The only obvious way to eliminate the current frame is to <code>return</code> to our caller.
If we’re to make this work, then, the caller must be willing to help us out.
That’s where the trampoline comes in.
It’s our co-conspirator in the plot to eliminate stack build-up.</p>
<h3 id="the-trampoline">The trampoline</h3>
<p>Here’s what the trampoline does:</p>
<ol type="1">
<li>It calls our function <code>f</code>, making itself the current caller.</li>
<li>When <code>f</code> wants to make a recursive tail call to itself, it returns the instruction <code>call(f)(*args, **kwds)</code>.
The language runtime dutifully removes the current execution frame from the stack and returns control to the trampoline, passing it the instruction.</li>
<li>The trampoline interprets the instruction and calls <code>f</code> back, giving it the supplied arguments, and again making itself the caller.</li>
<li>This process repeats until <code>f</code> wants to return a final result <code>z</code>; then it returns the new instruction <code>result(z)</code> instead.
As before, the runtime removes the current execution frame from the stack and returns control to the trampoline.</li>
<li>But now when the trampoline interprets the new instruction it will return <code>z</code> to <em>its</em> caller, ending the trampoline dance.</li>
</ol>
<p>Now you can see how the trampoline got its name.
When our function uses a <code>return</code> statement to remove its own execution frame from the stack, the trampoline bounces control back to it with new arguments.</p>
<p>Here’s a simple implementation.
First, we will encode our instructions to the trampoline as triples.
We’ll let <code>call(f)(*args, **kwds)</code> be the triple <code>(f, args, kwds)</code>,
and <code>result(z)</code> be the triple <code>(None, z, None)</code>:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> call(f):</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>    <span class="co">&quot;&quot;&quot;Instruct trampoline to call f with the args that follow.&quot;&quot;&quot;</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">def</span> g(<span class="op">*</span>args, <span class="op">**</span>kwds):</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> f, args, kwds</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> g</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> result(value):</span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>    <span class="co">&quot;&quot;&quot;Instruct trampoline to stop iterating and return a value.&quot;&quot;&quot;</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> <span class="va">None</span>, value, <span class="va">None</span></span></code></pre></div>
<p>Now we’ll create a decorator to wrap a function with a trampoline that will interpret the instructions that the function returns:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> functools</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> with_trampoline(f):</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>    <span class="co">&quot;&quot;&quot;Wrap a trampoline around a function that expects a trampoline.&quot;&quot;&quot;</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>    <span class="at">@functools.wraps</span>(f)</span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>    <span class="kw">def</span> g(<span class="op">*</span>args, <span class="op">**</span>kwds):</span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>        h <span class="op">=</span> f</span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a>        <span class="co"># the trampoline</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a>        <span class="cf">while</span> h <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>:</span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a>            h, args, kwds <span class="op">=</span> h(<span class="op">*</span>args, <span class="op">**</span>kwds)</span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> args</span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> g</span></code></pre></div>
<p>Note that the trampoline boils down to three lines:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="cf">while</span> h <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>:</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>    h, args, kwds <span class="op">=</span> h(<span class="op">*</span>args, <span class="op">**</span>kwds)</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="cf">return</span> args</span></code></pre></div>
<p>Basically, the trampoline keeps calling whatever function is in <code>h</code> until that function returns a <code>result(z)</code> instruction, at which time the loop exits and <code>z</code> is returned.
The original recursive tail calls have been boiled down to a <code>while</code> loop.
Recursion has become iteration.</p>
<h3 id="example-factorial">Example: factorial</h3>
<p>To see how we might use this implementation, let’s return to the factorial example from <a href="http://blog.moertel.com/posts/2013-05-11-recursive-to-iterative.html">the first article in our series</a>:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> factorial(n):</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> n <span class="op">&lt;</span> <span class="dv">2</span>:</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> <span class="dv">1</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> n <span class="op">*</span> factorial(n <span class="op">-</span> <span class="dv">1</span>)</span></code></pre></div>
<p>Step one, as before, is to tail-convert the lone recursive call:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a> <span class="kw">def</span> factorial(n, acc<span class="op">=</span><span class="dv">1</span>):</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>     <span class="cf">if</span> n <span class="op">&lt;</span> <span class="dv">2</span>:</span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>         <span class="cf">return</span> acc</span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>     <span class="cf">return</span> factorial(n <span class="op">-</span> <span class="dv">1</span>, acc <span class="op">*</span> n)</span></code></pre></div>
<p>Now we can create an equivalent function that uses trampoline idioms:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> trampoline_factorial(n, acc<span class="op">=</span><span class="dv">1</span>):</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> n <span class="op">&lt;</span> <span class="dv">2</span>:</span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> result(acc)</span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> call(trampoline_factorial)(n <span class="op">-</span> <span class="dv">1</span>, n <span class="op">*</span> acc)</span></code></pre></div>
<p>Note how the <code>return</code> statements have been transformed.</p>
<p>Finally, we can wrap this function with a trampoline to get a callable version that we can use just like the original:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>factorial <span class="op">=</span> with_trampoline(trampoline_factorial)</span></code></pre></div>
<p>Let’s take it for a spin:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="op">&gt;&gt;&gt;</span> factorial(<span class="dv">5</span>)</span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="dv">120</span></span></code></pre></div>
<p>To really see what’s going on, be sure to use the Online Python Tutor’s visualizer to step through the original, tail-recursive, and trampoline versions of the function.
Just open this link: <a href="http://www.pythontutor.com/visualize.html#code=%23+our+trampoline+library%0A%0Aimport+functools%0A%0Adef+call(f)%3A%0A++++%22%22%22Instruct+trampoline+to+call+f+with+the+args+that+follow.%22%22%22%0A++++def+g(*args,+**kwds)%3A%0A++++++++return+f,+args,+kwds%0A++++return+g%0A%0Adef+result(value)%3A%0A++++%22%22%22Instruct+trampoline+to+stop+iterating+and+return+a+value.%22%22%22%0A++++return+None,+value,+None%0A%0Adef+with_trampoline(f)%3A%0A++++%22%22%22Wrap+a+trampoline+around+a+function+that+expects+a+trampoline.%22%22%22%0A++++%40functools.wraps(f)%0A++++def+g(*args,+**kwds)%3A%0A++++++++h+%3D+f%0A++++++++%23+the+trampoline%0A++++++++while+h+is+not+None%3A%0A++++++++++++h,+args,+kwds+%3D+h(*args,+**kwds)%0A++++++++return+args%0A++++return+g%0A%0A%0A%23+original+recursive+version+of+factorial+function%0A%0Adef+factorial(n)%3A%0A++++if+n+%3C+2%3A%0A++++++++return+1%0A++++return+n+*+factorial(n+-+1)%0A%0Aprint+factorial(5)%0A%0A%0A%23+tail-call+recursive+version%0A%0Adef+factorial(n,+acc%3D1)%3A%0A+++++if+n+%3C+2%3A%0A+++++++++return+acc%0A+++++return+factorial(n+-+1,+acc+*+n)%0A%0Aprint+factorial(5)%0A%0A%0A%23+trampoline-based+tail-call+version+(%3D+iterative)%0A%0Adef+trampoline_factorial(n,+acc%3D1)%3A%0A++++if+n+%3C+2%3A%0A++++++++return+result(acc)%0A++++return+call(trampoline_factorial)(n+-+1,+n+*+acc)%0A%0Afactorial+%3D+with_trampoline(trampoline_factorial)%0A%0Aprint+factorial(5)%0A&amp;mode=display&amp;cumulative=false&amp;heapPrimitives=false&amp;drawParentPointers=false&amp;textReferences=false&amp;showOnlyOutputs=false&amp;py=2&amp;curInstr=0">Visualize the execution</a>.
(ProTip: use a new tab.)</p>
<h3 id="why-use-the-trampoline">Why use the trampoline?</h3>
<p>As I mentioned at the beginning of this article, if you can convert a function’s recursive calls into tail calls – which you must do to use a trampoline – you can also use the Simple Method to convert the function’s recursion into iteration, eliminating the calls altogether.
For example, here’s what the Simple Method does to our original <code>factorial</code> function:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> factorial(n, acc<span class="op">=</span><span class="dv">1</span>):</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>    <span class="cf">while</span> n <span class="op">&gt;</span> <span class="dv">1</span>:</span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a>        (n, acc) <span class="op">=</span> (n <span class="op">-</span> <span class="dv">1</span>, acc <span class="op">*</span> n)</span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> acc</span></code></pre></div>
<p>This version is simpler and more efficient than the trampoline version.
So why not use the Simple Method always?</p>
<p>The answer is that the Simple Method is tricky to apply to functions that make tail calls from within loops.
Recall that it introduces a loop around a function’s body and replaces recursive tail calls with <code>continue</code> statements.
But if the function already has its own loops, replacing a tail call within one of them with a <code>continue</code> statement will restart that inner loop instead of the whole-body loop, as desired.
In that case, you must add condition flags to make sure the right loop gets restarted, and that gets old fast.
Then, using a trampoline may be a win.</p>
<p>That said, I almost never use trampolines.
Getting a function into tail-call form is nine tenths of the battle.
If I’ve gone that far already, I’ll usually go the rest of the way to get a tight, iterative version.</p>
<p>Why, then, did we make this effort to understand the trampoline?
Two reasons.
First, it’s semi-common in programming lore, so it’s best to know about it.
Second, it’s a stepping stone to a more-general, more-powerful technique: <em>continuation-passing-style expressions</em>.
That’s our subject for next time.</p>
<p>In the meantime, if you want another take on trampolines in Python, Kyle Miller wrote a nice article on the subject: <a href="http://web.mit.edu/kmill/www/programming/tailcall.html">Tail call recursion in Python</a>.</p>
<p>Thanks for reading!
As always, if you have questions or comments, please leave a comment on the blog or hit me at <a href="https://twitter.com/tmoertel">@tmoertel</a>.</p>
</div>
]]></summary>
</entry>
<entry>
    <title>Tricks of the trade: Recursion to Iteration, Part 3: Recursive Data Structures</title>
    <link href="http://blog.moertel.com/posts/2013-06-03-recursion-to-iteration-3.html" />
    <id>http://blog.moertel.com/posts/2013-06-03-recursion-to-iteration-3.html</id>
    <published>2013-06-03T00:00:00Z</published>
    <updated>2013-06-03T00:00:00Z</updated>
    <summary type="html"><![CDATA[<div class="info byline">By
  <span itemprop="author" itemscope itemtype="http://schema.org/Person"><a href="https://blog.moertel.com/about.html" itemprop="url"><span itemprop="name">Tom Moertel</span></a></span></div>
<div class="info">Posted on <time itemprop="datePublished" datetime="2013-06-03">June  3, 2013</time></div>
<div class="tags">Tags: <span itemprop="keywords"><a title="All pages tagged &#39;programming&#39;." href="/tags/programming.html" rel="tag">programming</a>, <a title="All pages tagged &#39;recursion&#39;." href="/tags/recursion.html" rel="tag">recursion</a>, <a title="All pages tagged &#39;iteration&#39;." href="/tags/iteration.html" rel="tag">iteration</a>, <a title="All pages tagged &#39;python&#39;." href="/tags/python.html" rel="tag">python</a>, <a title="All pages tagged &#39;recursion-to-iteration series&#39;." href="/tags/recursion-to-iteration%20series.html" rel="tag">recursion-to-iteration series</a>, <a title="All pages tagged &#39;tail calls&#39;." href="/tags/tail%20calls.html" rel="tag">tail calls</a>, <a title="All pages tagged &#39;data structures&#39;." href="/tags/data%20structures.html" rel="tag">data structures</a></span></div>

<div itemprop="articleBody">
<p>This is the third article in <a href="http://blog.moertel.com/tags/recursion-to-iteration%20series.html">a series on converting recursive algorithms into iterative algorithms</a>.
If any of what follows seems confusing, you may want to read the earlier articles first.</p>
<p>This is an extra article that I hadn’t planned.
I’m writing it because in a comment on the previous article a reader asked me to show a less mathematical example and suggested tree traversal.
So that’s the subject of this article:
We’ll take a binary tree and flatten it into a list, first recursively, then iteratively.</p>
<h3 id="the-challenge">The challenge</h3>
<p>First, let’s define a binary tree to be either empty or given by a node having three parts: (1) a value, (2) a left subtree, and (3) a right subtree, where both of the subtrees are themselves binary trees.
In Haskell, we might define it like so:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">BinaryTree</span> a <span class="ot">=</span> <span class="dt">Empty</span> <span class="op">|</span> <span class="dt">Node</span> a (<span class="dt">BinaryTree</span> a) (<span class="dt">BinaryTree</span> a)</span></code></pre></div>
<p>In Python, which we’ll use for the rest of this article, we’ll say that <code>None</code> represents an empty tree and that the following class represents a node:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> collections</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>Node <span class="op">=</span> collections.namedtuple(<span class="st">&#39;Node&#39;</span>, <span class="st">&#39;val left right&#39;</span>)</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="co"># some sample trees having various node counts</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>tree0 <span class="op">=</span> <span class="va">None</span>  <span class="co"># empty tree</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>tree1 <span class="op">=</span> Node(<span class="dv">5</span>, <span class="va">None</span>, <span class="va">None</span>)</span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>tree2 <span class="op">=</span> Node(<span class="dv">7</span>, tree1, <span class="va">None</span>)</span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a>tree3 <span class="op">=</span> Node(<span class="dv">7</span>, tree1, Node(<span class="dv">9</span>, <span class="va">None</span>, <span class="va">None</span>))</span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a>tree4 <span class="op">=</span> Node(<span class="dv">2</span>, <span class="va">None</span>, tree3)</span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a>tree5 <span class="op">=</span> Node(<span class="dv">2</span>, Node(<span class="dv">1</span>, <span class="va">None</span>, <span class="va">None</span>), tree3)</span></code></pre></div>
<p>Let us now define a function to flatten a tree using an <a href="http://en.wikipedia.org/wiki/Tree_traversal#In-order">in-order traversal</a>.
The recursive definition is absurdly simple, the data type having only two cases to consider:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> flatten(bst):</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>    <span class="co"># empty case</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> bst <span class="kw">is</span> <span class="va">None</span>:</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> []</span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>    <span class="co"># node case</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> flatten(bst.left) <span class="op">+</span> [bst.val] <span class="op">+</span> flatten(bst.right)</span></code></pre></div>
<p>A few tests to check that it does what we expect:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> check_flattener(f):</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>    <span class="cf">assert</span> f(tree0) <span class="op">==</span> []</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>    <span class="cf">assert</span> f(tree1) <span class="op">==</span> [<span class="dv">5</span>]</span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">assert</span> f(tree2) <span class="op">==</span> [<span class="dv">5</span>, <span class="dv">7</span>]</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>    <span class="cf">assert</span> f(tree3) <span class="op">==</span> [<span class="dv">5</span>, <span class="dv">7</span>, <span class="dv">9</span>]</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>    <span class="cf">assert</span> f(tree4) <span class="op">==</span> [<span class="dv">2</span>, <span class="dv">5</span>, <span class="dv">7</span>, <span class="dv">9</span>]</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>    <span class="cf">assert</span> f(tree5) <span class="op">==</span> [<span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">5</span>, <span class="dv">7</span>, <span class="dv">9</span>]</span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a>    <span class="bu">print</span> <span class="st">&#39;ok&#39;</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a>check_flattener(flatten)  <span class="co"># ok</span></span></code></pre></div>
<p>Our challenge for today is to convert <code>flatten</code> into an iterative version.
Other than a new trick – partial evaluation – the transformation is straightforward, so I’ll move quickly.</p>
<p>Let’s do this!</p>
<h3 id="eliminating-the-first-recursive-call">Eliminating the first recursive call</h3>
<p>First, let’s separate the base case from the incremental work:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> step(bst):</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> flatten(bst.left) <span class="op">+</span> [bst.val] <span class="op">+</span> flatten(bst.right)</span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> flatten(bst):</span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> bst <span class="kw">is</span> <span class="va">None</span>:</span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> []</span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> step(bst)</span></code></pre></div>
<p>And let’s break the incremental work into smaller pieces to see what’s going on.</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> step(bst):</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>    left <span class="op">=</span> flatten(bst.left)</span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>    left.append(bst.val)</span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>    right <span class="op">=</span> flatten(bst.right)</span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a>    left.extend(right)</span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> left</span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> flatten(bst):</span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> bst <span class="kw">is</span> <span class="va">None</span>:</span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> []</span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> step(bst)</span></code></pre></div>
<p>Let’s try to get rid of the first recursive call by assuming that somebody has passed us its result via a secret argument <code>left</code>:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> step(bst, left<span class="op">=</span><span class="va">None</span>):</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> left <span class="kw">is</span> <span class="va">None</span>:</span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>        left <span class="op">=</span> flatten(bst.left)</span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>    left.append(bst.val)</span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a>    right <span class="op">=</span> flatten(bst.right)</span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a>    left.extend(right)</span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> left</span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> flatten(bst):</span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> bst <span class="kw">is</span> <span class="va">None</span>:</span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> []</span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> step(bst)</span></code></pre></div>
<p>And now we’ll make <code>step</code> return values that parallel its input arguments:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> step(bst, left<span class="op">=</span><span class="va">None</span>):</span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> left <span class="kw">is</span> <span class="va">None</span>:</span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>        left <span class="op">=</span> flatten(bst.left)</span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a>    left.append(bst.val)</span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a>    right <span class="op">=</span> flatten(bst.right)</span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a>    left.extend(right)</span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> bst, left  <span class="co"># &lt;-- add bst</span></span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> flatten(bst):</span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> bst <span class="kw">is</span> <span class="va">None</span>:</span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> []</span>
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> step(bst)[<span class="op">-</span><span class="dv">1</span>]  <span class="co"># &lt;-- note [-1]</span></span></code></pre></div>
<p>In the first recursive call, the transformation applied to <code>bst</code> is <code>.left</code>, so we want to apply the opposite transformation to <code>bst</code> in the returned values.
And what’s the opposite of descending to a node’s left subtree?
It’s ascending to the node’s parent.
So we want something like this:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="co"># this code does not work!</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> step(bst, left<span class="op">=</span><span class="va">None</span>):</span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> left <span class="kw">is</span> <span class="va">None</span>:</span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a>        left <span class="op">=</span> flatten(bst.left)</span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a>    left.append(bst.val)</span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a>    right <span class="op">=</span> flatten(bst.right)</span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a>    left.extend(right)</span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> get_parent(bst), left  <span class="co"># &lt;-- need get_parent</span></span></code></pre></div>
<p>But we’re stuck.
We can’t define <code>get_parent</code> because our tree data structure doesn’t keep track of parents, only children.</p>
<p>New plan:
Maybe we can assume that someone has <em>passed us</em> the node’s parent and go from there?</p>
<p>But this plan hits the same brick wall:
If we add a new argument to accept the parent, we must for parallelism add a new return value to emit the transformed parent, which is the parent of the parent.
But we can’t compute the parent of the parent because, as before, we have no way of implementing <code>get_parent</code>.</p>
<p>So we do what mathematicians do when their assumptions hit a brick wall: we strengthen our assumption!
Now we assume that someone has passed us <em>all of the parents</em>, right up to the tree’s root.
And that assumption gives us what we need:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> step(bst, parents, left<span class="op">=</span><span class="va">None</span>):</span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> left <span class="kw">is</span> <span class="va">None</span>:</span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a>        left <span class="op">=</span> flatten(bst.left)</span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a>    left.append(bst.val)</span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a>    right <span class="op">=</span> flatten(bst.right)</span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a>    left.extend(right)</span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> parents[<span class="op">-</span><span class="dv">1</span>], parents[:<span class="op">-</span><span class="dv">1</span>], left</span></code></pre></div>
<p>Note that we’re using the Python stack convention for <code>parents</code>; thus the immediate parent of <code>bst</code> is given by the final element <code>parents[-1]</code>.</p>
<p>As a simplification, we can eliminate the <code>bst</code> argument by considering it the final parent pushed onto the stack:</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> step(parents, left<span class="op">=</span><span class="va">None</span>):</span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a>    bst <span class="op">=</span> parents.pop()  <span class="co"># &lt;-- bst = top of parents stack</span></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> left <span class="kw">is</span> <span class="va">None</span>:</span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a>        left <span class="op">=</span> flatten(bst.left)</span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a>    left.append(bst.val)</span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a>    right <span class="op">=</span> flatten(bst.right)</span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a>    left.extend(right)</span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> parents, left</span></code></pre></div>
<p>Now that <code>step</code> requires the <code>parents</code> stack as an argument, the base function must provide it:</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> flatten(bst):</span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> bst <span class="kw">is</span> <span class="va">None</span>:</span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> []</span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a>    parents <span class="op">=</span> [bst]</span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> step(parents)[<span class="op">-</span><span class="dv">1</span>]</span></code></pre></div>
<p>But we still haven’t eliminated the first recursive call.
To do that, we’ll need to pass the <code>step</code> function a value for its <code>left</code> argument, which will cause the recursive call to be skipped.</p>
<p>But we only know what that value should be for one case, the base case, when <code>bst</code> is <code>None</code>; then <code>left</code> must be <code>[]</code>.
To get to that case from the tree’s root, where <code>bst</code> is definitely not <code>None</code>, we must iteratively replicate the normal recursive calls on <code>bst.left</code> until we hit the leftmost leaf node.
And then, to compute the desired result, we must reverse the trip, iterating the <code>step</code> function until we have returned to the tree’s root, where the <code>parents</code> stack must be empty:</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> flatten(bst):</span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a>    <span class="co"># find initial conditions for secret-feature &quot;left&quot;</span></span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a>    left <span class="op">=</span> []</span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a>    parents <span class="op">=</span> []</span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a>    <span class="cf">while</span> bst <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>:</span>
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a>        parents.append(bst)</span>
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a>        bst <span class="op">=</span> bst.left</span>
<span id="cb13-8"><a href="#cb13-8" aria-hidden="true" tabindex="-1"></a>    <span class="co"># iterate to compute the result</span></span>
<span id="cb13-9"><a href="#cb13-9" aria-hidden="true" tabindex="-1"></a>    <span class="cf">while</span> parents:</span>
<span id="cb13-10"><a href="#cb13-10" aria-hidden="true" tabindex="-1"></a>        parents, left <span class="op">=</span> step(parents, left)</span>
<span id="cb13-11"><a href="#cb13-11" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> left</span></code></pre></div>
<p>And just like that, one of the recursive calls has been transformed into iteration.
We’re halfway to the finish line!</p>
<h3 id="eliminating-the-second-recursive-call">Eliminating the second recursive call</h3>
<p>But we still have to eliminate that final recursive call to <code>flatten</code>, now sequestered in <code>step</code>.
Let’s take a closer look at that function after we make its <code>left</code> argument required since it always gets called with a value now:</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> step(parents, left):</span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a>    bst <span class="op">=</span> parents.pop()</span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a>    left.append(bst.val)</span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a>    right <span class="op">=</span> flatten(bst.right)</span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a>    left.extend(right)</span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> parents, left</span></code></pre></div>
<p>To get rid of the recursive call to <code>flatten</code>, we’re going to use a new trick: partial evaluation.
Basically, we’re going to replace the call to <code>flatten</code> with the function body of <code>flatten</code>, after we rename all its variables to prevent conflicts.
So let’s make a copy of <code>flatten</code> and suffix all its variables with <code>1</code>:</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> flatten1(bst1):</span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a>    left1 <span class="op">=</span> []</span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a>    parents1 <span class="op">=</span> []</span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">while</span> bst1 <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>:</span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true" tabindex="-1"></a>        parents1.append(bst1)</span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true" tabindex="-1"></a>        bst1 <span class="op">=</span> bst1.left</span>
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true" tabindex="-1"></a>    <span class="cf">while</span> parents1:</span>
<span id="cb15-8"><a href="#cb15-8" aria-hidden="true" tabindex="-1"></a>        parents1, left1 <span class="op">=</span> step(parents1, left1)</span>
<span id="cb15-9"><a href="#cb15-9" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> left1</span></code></pre></div>
<p>And then let’s make its arguments and return values explicit:</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a>    (bst1, ) <span class="op">=</span> ARGUMENTS</span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a>    left1 <span class="op">=</span> []</span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a>    parents1 <span class="op">=</span> []</span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">while</span> bst1 <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>:</span>
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a>        parents1.append(bst1)</span>
<span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a>        bst1 <span class="op">=</span> bst1.left</span>
<span id="cb16-7"><a href="#cb16-7" aria-hidden="true" tabindex="-1"></a>    <span class="cf">while</span> parents1:</span>
<span id="cb16-8"><a href="#cb16-8" aria-hidden="true" tabindex="-1"></a>        parents1, left1 <span class="op">=</span> step(parents1, left1)</span>
<span id="cb16-9"><a href="#cb16-9" aria-hidden="true" tabindex="-1"></a>    RETURNS <span class="op">=</span> (left1, )</span></code></pre></div>
<p>And then we’ll drop this expansion into <code>step</code>:</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> step(parents, left):</span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a>    bst <span class="op">=</span> parents.pop()</span>
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a>    left.append(bst.val)</span>
<span id="cb17-4"><a href="#cb17-4" aria-hidden="true" tabindex="-1"></a>    <span class="co"># -- begin partial evaluation --</span></span>
<span id="cb17-5"><a href="#cb17-5" aria-hidden="true" tabindex="-1"></a>    (bst1, ) <span class="op">=</span> (bst.right, )</span>
<span id="cb17-6"><a href="#cb17-6" aria-hidden="true" tabindex="-1"></a>    left1 <span class="op">=</span> []</span>
<span id="cb17-7"><a href="#cb17-7" aria-hidden="true" tabindex="-1"></a>    parents1 <span class="op">=</span> []</span>
<span id="cb17-8"><a href="#cb17-8" aria-hidden="true" tabindex="-1"></a>    <span class="cf">while</span> bst1 <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>:</span>
<span id="cb17-9"><a href="#cb17-9" aria-hidden="true" tabindex="-1"></a>        parents1.append(bst1)</span>
<span id="cb17-10"><a href="#cb17-10" aria-hidden="true" tabindex="-1"></a>        bst1 <span class="op">=</span> bst1.left</span>
<span id="cb17-11"><a href="#cb17-11" aria-hidden="true" tabindex="-1"></a>    <span class="cf">while</span> parents1:</span>
<span id="cb17-12"><a href="#cb17-12" aria-hidden="true" tabindex="-1"></a>        parents1, left1 <span class="op">=</span> step(parents1, left1)</span>
<span id="cb17-13"><a href="#cb17-13" aria-hidden="true" tabindex="-1"></a>    (right, ) <span class="op">=</span> (left1, )</span>
<span id="cb17-14"><a href="#cb17-14" aria-hidden="true" tabindex="-1"></a>    <span class="co"># -- end partial evaluation --</span></span>
<span id="cb17-15"><a href="#cb17-15" aria-hidden="true" tabindex="-1"></a>    left.extend(right)</span>
<span id="cb17-16"><a href="#cb17-16" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> parents, left</span></code></pre></div>
<p>Now we can eliminate code by fusion across the partial-evaluation boundary.</p>
<p>First up: <code>left1</code>.
We can now see that this variable accumulates values that, in the end, get appended to <code>left</code> (via the return variable <code>right</code>).
But we can just as well append those values to <code>left</code> directly, eliminating <code>left1</code> within the boundary and the call to <code>left.extend(right)</code> without:</p>
<div class="sourceCode" id="cb18"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> step(parents, left):</span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a>    bst <span class="op">=</span> parents.pop()</span>
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a>    left.append(bst.val)</span>
<span id="cb18-4"><a href="#cb18-4" aria-hidden="true" tabindex="-1"></a>    <span class="co"># -- begin partial evaluation --</span></span>
<span id="cb18-5"><a href="#cb18-5" aria-hidden="true" tabindex="-1"></a>    (bst1, ) <span class="op">=</span> (bst.right, )</span>
<span id="cb18-6"><a href="#cb18-6" aria-hidden="true" tabindex="-1"></a>    <span class="co"># left1 = []  # &lt;-- eliminate and use left instead</span></span>
<span id="cb18-7"><a href="#cb18-7" aria-hidden="true" tabindex="-1"></a>    parents1 <span class="op">=</span> []</span>
<span id="cb18-8"><a href="#cb18-8" aria-hidden="true" tabindex="-1"></a>    <span class="cf">while</span> bst1 <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>:</span>
<span id="cb18-9"><a href="#cb18-9" aria-hidden="true" tabindex="-1"></a>        parents1.append(bst1)</span>
<span id="cb18-10"><a href="#cb18-10" aria-hidden="true" tabindex="-1"></a>        bst1 <span class="op">=</span> bst1.left</span>
<span id="cb18-11"><a href="#cb18-11" aria-hidden="true" tabindex="-1"></a>    <span class="cf">while</span> parents1:</span>
<span id="cb18-12"><a href="#cb18-12" aria-hidden="true" tabindex="-1"></a>        parents1, left <span class="op">=</span> step(parents1, left)</span>
<span id="cb18-13"><a href="#cb18-13" aria-hidden="true" tabindex="-1"></a>    <span class="co"># (right, ) = (left, )  # &lt;-- eliminated</span></span>
<span id="cb18-14"><a href="#cb18-14" aria-hidden="true" tabindex="-1"></a>    <span class="co"># -- end partial evaluation --</span></span>
<span id="cb18-15"><a href="#cb18-15" aria-hidden="true" tabindex="-1"></a>    <span class="co"># left.extend(right)  # &lt;-- eliminated</span></span>
<span id="cb18-16"><a href="#cb18-16" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> parents, left</span></code></pre></div>
<p>For this next fusion, we’re going to need to recall our base function to get the necessary outside scope:</p>
<div class="sourceCode" id="cb19"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> step(parents, left):</span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a>    bst <span class="op">=</span> parents.pop()</span>
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a>    left.append(bst.val)</span>
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true" tabindex="-1"></a>    <span class="co"># -- begin partial evaluation --</span></span>
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true" tabindex="-1"></a>    (bst1, ) <span class="op">=</span> (bst.right, )</span>
<span id="cb19-6"><a href="#cb19-6" aria-hidden="true" tabindex="-1"></a>    parents1 <span class="op">=</span> []</span>
<span id="cb19-7"><a href="#cb19-7" aria-hidden="true" tabindex="-1"></a>    <span class="cf">while</span> bst1 <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>:</span>
<span id="cb19-8"><a href="#cb19-8" aria-hidden="true" tabindex="-1"></a>        parents1.append(bst1)</span>
<span id="cb19-9"><a href="#cb19-9" aria-hidden="true" tabindex="-1"></a>        bst1 <span class="op">=</span> bst1.left</span>
<span id="cb19-10"><a href="#cb19-10" aria-hidden="true" tabindex="-1"></a>    <span class="cf">while</span> parents1:</span>
<span id="cb19-11"><a href="#cb19-11" aria-hidden="true" tabindex="-1"></a>        parents1, left <span class="op">=</span> step(parents1, left)</span>
<span id="cb19-12"><a href="#cb19-12" aria-hidden="true" tabindex="-1"></a>    <span class="co"># -- end partial evaluation --</span></span>
<span id="cb19-13"><a href="#cb19-13" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> parents, left</span>
<span id="cb19-14"><a href="#cb19-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-15"><a href="#cb19-15" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> flatten(bst):</span>
<span id="cb19-16"><a href="#cb19-16" aria-hidden="true" tabindex="-1"></a>    left <span class="op">=</span> []</span>
<span id="cb19-17"><a href="#cb19-17" aria-hidden="true" tabindex="-1"></a>    parents <span class="op">=</span> []</span>
<span id="cb19-18"><a href="#cb19-18" aria-hidden="true" tabindex="-1"></a>    <span class="cf">while</span> bst <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>:</span>
<span id="cb19-19"><a href="#cb19-19" aria-hidden="true" tabindex="-1"></a>        parents.append(bst)</span>
<span id="cb19-20"><a href="#cb19-20" aria-hidden="true" tabindex="-1"></a>        bst <span class="op">=</span> bst.left</span>
<span id="cb19-21"><a href="#cb19-21" aria-hidden="true" tabindex="-1"></a>    <span class="cf">while</span> parents:</span>
<span id="cb19-22"><a href="#cb19-22" aria-hidden="true" tabindex="-1"></a>        parents, left <span class="op">=</span> step(parents, left)</span>
<span id="cb19-23"><a href="#cb19-23" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> left</span></code></pre></div>
<p>When <code>flatten</code> calls <code>step</code> and the code within the partially evaluated region executes, it builds up a stack of nodes <code>parents1</code> and then calls <code>step</code> iteratively to pop values off of that stack and process them.
When it’s finished, control returns to <code>step</code> proper, which then returns to its caller, <code>flatten</code>, with the values (<code>parents</code>, <code>left</code>).
But look at what <code>flatten</code> then does with <code>parents</code>: it calls <code>step</code> iteratively to pop values off of that stack and process them in exactly the same way.</p>
<p>So we can eliminate the <code>while</code> loop in <code>step</code> – and the recursive call! – by returning not <code>parents</code> but <code>parents + parents1</code>, which will make the <code>while</code> loop in <code>flatten</code> do the exact same work.</p>
<div class="sourceCode" id="cb20"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> step(parents, left):</span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a>    bst <span class="op">=</span> parents.pop()</span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a>    left.append(bst.val)</span>
<span id="cb20-4"><a href="#cb20-4" aria-hidden="true" tabindex="-1"></a>    <span class="co"># -- begin partial evaluation --</span></span>
<span id="cb20-5"><a href="#cb20-5" aria-hidden="true" tabindex="-1"></a>    (bst1, ) <span class="op">=</span> (bst.right, )</span>
<span id="cb20-6"><a href="#cb20-6" aria-hidden="true" tabindex="-1"></a>    parents1 <span class="op">=</span> []</span>
<span id="cb20-7"><a href="#cb20-7" aria-hidden="true" tabindex="-1"></a>    <span class="cf">while</span> bst1 <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>:</span>
<span id="cb20-8"><a href="#cb20-8" aria-hidden="true" tabindex="-1"></a>        parents1.append(bst1)</span>
<span id="cb20-9"><a href="#cb20-9" aria-hidden="true" tabindex="-1"></a>        bst1 <span class="op">=</span> bst1.left</span>
<span id="cb20-10"><a href="#cb20-10" aria-hidden="true" tabindex="-1"></a>    <span class="co"># while parents1:                            # &lt;-- eliminated</span></span>
<span id="cb20-11"><a href="#cb20-11" aria-hidden="true" tabindex="-1"></a>    <span class="co">#     parents1, left = step(parents1, left)  #</span></span>
<span id="cb20-12"><a href="#cb20-12" aria-hidden="true" tabindex="-1"></a>    <span class="co"># -- end partial evaluation --</span></span>
<span id="cb20-13"><a href="#cb20-13" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> parents <span class="op">+</span> parents1, left  <span class="co"># parents -&gt; parents + parents1</span></span></code></pre></div>
<p>And then we can eliminate <code>parents1</code> completely by taking the values we would have appended to it and appending them directly to <code>parents</code>:</p>
<div class="sourceCode" id="cb21"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> step(parents, left):</span>
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a>    bst <span class="op">=</span> parents.pop()</span>
<span id="cb21-3"><a href="#cb21-3" aria-hidden="true" tabindex="-1"></a>    left.append(bst.val)</span>
<span id="cb21-4"><a href="#cb21-4" aria-hidden="true" tabindex="-1"></a>    <span class="co"># -- begin partial evaluation --</span></span>
<span id="cb21-5"><a href="#cb21-5" aria-hidden="true" tabindex="-1"></a>    (bst1, ) <span class="op">=</span> (bst.right, )</span>
<span id="cb21-6"><a href="#cb21-6" aria-hidden="true" tabindex="-1"></a>    <span class="co"># parents1 = []  # &lt;-- eliminated</span></span>
<span id="cb21-7"><a href="#cb21-7" aria-hidden="true" tabindex="-1"></a>    <span class="cf">while</span> bst1 <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>:</span>
<span id="cb21-8"><a href="#cb21-8" aria-hidden="true" tabindex="-1"></a>        parents.append(bst1)  <span class="co"># parents1 -&gt; parents</span></span>
<span id="cb21-9"><a href="#cb21-9" aria-hidden="true" tabindex="-1"></a>        bst1 <span class="op">=</span> bst1.left</span>
<span id="cb21-10"><a href="#cb21-10" aria-hidden="true" tabindex="-1"></a>    <span class="co"># -- end partial evaluation --</span></span>
<span id="cb21-11"><a href="#cb21-11" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> parents, left  <span class="co"># parents + parents1 -&gt; parents</span></span></code></pre></div>
<p>And now, once we remove our partial-evaluation scaffolding, our <code>step</code> function is looking simple again:</p>
<div class="sourceCode" id="cb22"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> step(parents, left):</span>
<span id="cb22-2"><a href="#cb22-2" aria-hidden="true" tabindex="-1"></a>    bst <span class="op">=</span> parents.pop()</span>
<span id="cb22-3"><a href="#cb22-3" aria-hidden="true" tabindex="-1"></a>    left.append(bst.val)</span>
<span id="cb22-4"><a href="#cb22-4" aria-hidden="true" tabindex="-1"></a>    bst1 <span class="op">=</span> bst.right</span>
<span id="cb22-5"><a href="#cb22-5" aria-hidden="true" tabindex="-1"></a>    <span class="cf">while</span> bst1 <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>:</span>
<span id="cb22-6"><a href="#cb22-6" aria-hidden="true" tabindex="-1"></a>        parents.append(bst1)</span>
<span id="cb22-7"><a href="#cb22-7" aria-hidden="true" tabindex="-1"></a>        bst1 <span class="op">=</span> bst1.left</span>
<span id="cb22-8"><a href="#cb22-8" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> parents, left</span></code></pre></div>
<p>For the final leg of our journey – simplification – let’s inline the <code>step</code> logic back into the base function:</p>
<div class="sourceCode" id="cb23"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> flatten(bst):</span>
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a>    left <span class="op">=</span> []</span>
<span id="cb23-3"><a href="#cb23-3" aria-hidden="true" tabindex="-1"></a>    parents <span class="op">=</span> []</span>
<span id="cb23-4"><a href="#cb23-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">while</span> bst <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>:</span>
<span id="cb23-5"><a href="#cb23-5" aria-hidden="true" tabindex="-1"></a>        parents.append(bst)</span>
<span id="cb23-6"><a href="#cb23-6" aria-hidden="true" tabindex="-1"></a>        bst <span class="op">=</span> bst.left</span>
<span id="cb23-7"><a href="#cb23-7" aria-hidden="true" tabindex="-1"></a>    <span class="cf">while</span> parents:</span>
<span id="cb23-8"><a href="#cb23-8" aria-hidden="true" tabindex="-1"></a>        parents, left <span class="op">=</span> parents, left</span>
<span id="cb23-9"><a href="#cb23-9" aria-hidden="true" tabindex="-1"></a>        bst <span class="op">=</span> parents.pop()</span>
<span id="cb23-10"><a href="#cb23-10" aria-hidden="true" tabindex="-1"></a>        left.append(bst.val)</span>
<span id="cb23-11"><a href="#cb23-11" aria-hidden="true" tabindex="-1"></a>        bst1 <span class="op">=</span> bst.right</span>
<span id="cb23-12"><a href="#cb23-12" aria-hidden="true" tabindex="-1"></a>        <span class="cf">while</span> bst1 <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>:</span>
<span id="cb23-13"><a href="#cb23-13" aria-hidden="true" tabindex="-1"></a>            parents.append(bst1)</span>
<span id="cb23-14"><a href="#cb23-14" aria-hidden="true" tabindex="-1"></a>            bst1 <span class="op">=</span> bst1.left</span>
<span id="cb23-15"><a href="#cb23-15" aria-hidden="true" tabindex="-1"></a>        parents, left <span class="op">=</span> parents, left</span>
<span id="cb23-16"><a href="#cb23-16" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> left</span></code></pre></div>
<p>Let’s eliminate the trivial argument-binding and return-value assignments:</p>
<div class="sourceCode" id="cb24"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> flatten(bst):</span>
<span id="cb24-2"><a href="#cb24-2" aria-hidden="true" tabindex="-1"></a>    left <span class="op">=</span> []</span>
<span id="cb24-3"><a href="#cb24-3" aria-hidden="true" tabindex="-1"></a>    parents <span class="op">=</span> []</span>
<span id="cb24-4"><a href="#cb24-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">while</span> bst <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>:</span>
<span id="cb24-5"><a href="#cb24-5" aria-hidden="true" tabindex="-1"></a>        parents.append(bst)</span>
<span id="cb24-6"><a href="#cb24-6" aria-hidden="true" tabindex="-1"></a>        bst <span class="op">=</span> bst.left</span>
<span id="cb24-7"><a href="#cb24-7" aria-hidden="true" tabindex="-1"></a>    <span class="cf">while</span> parents:</span>
<span id="cb24-8"><a href="#cb24-8" aria-hidden="true" tabindex="-1"></a>        <span class="co"># parents, left = parents, left  # = no-op</span></span>
<span id="cb24-9"><a href="#cb24-9" aria-hidden="true" tabindex="-1"></a>        bst <span class="op">=</span> parents.pop()</span>
<span id="cb24-10"><a href="#cb24-10" aria-hidden="true" tabindex="-1"></a>        left.append(bst.val)</span>
<span id="cb24-11"><a href="#cb24-11" aria-hidden="true" tabindex="-1"></a>        bst1 <span class="op">=</span> bst.right</span>
<span id="cb24-12"><a href="#cb24-12" aria-hidden="true" tabindex="-1"></a>        <span class="cf">while</span> bst1 <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>:</span>
<span id="cb24-13"><a href="#cb24-13" aria-hidden="true" tabindex="-1"></a>            parents.append(bst1)</span>
<span id="cb24-14"><a href="#cb24-14" aria-hidden="true" tabindex="-1"></a>            bst1 <span class="op">=</span> bst1.left</span>
<span id="cb24-15"><a href="#cb24-15" aria-hidden="true" tabindex="-1"></a>        <span class="co"># parents, left = parents, left  # = no-op</span></span>
<span id="cb24-16"><a href="#cb24-16" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> left</span></code></pre></div>
<p>And, finally, factor out the duplicated <code>while</code> loop into a local function:</p>
<div class="sourceCode" id="cb25"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> flatten(bst):</span>
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true" tabindex="-1"></a>    left <span class="op">=</span> []</span>
<span id="cb25-3"><a href="#cb25-3" aria-hidden="true" tabindex="-1"></a>    parents <span class="op">=</span> []</span>
<span id="cb25-4"><a href="#cb25-4" aria-hidden="true" tabindex="-1"></a>    <span class="kw">def</span> descend_left(bst):</span>
<span id="cb25-5"><a href="#cb25-5" aria-hidden="true" tabindex="-1"></a>        <span class="cf">while</span> bst <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>:</span>
<span id="cb25-6"><a href="#cb25-6" aria-hidden="true" tabindex="-1"></a>            parents.append(bst)</span>
<span id="cb25-7"><a href="#cb25-7" aria-hidden="true" tabindex="-1"></a>            bst <span class="op">=</span> bst.left</span>
<span id="cb25-8"><a href="#cb25-8" aria-hidden="true" tabindex="-1"></a>    descend_left(bst)</span>
<span id="cb25-9"><a href="#cb25-9" aria-hidden="true" tabindex="-1"></a>    <span class="cf">while</span> parents:</span>
<span id="cb25-10"><a href="#cb25-10" aria-hidden="true" tabindex="-1"></a>        bst <span class="op">=</span> parents.pop()</span>
<span id="cb25-11"><a href="#cb25-11" aria-hidden="true" tabindex="-1"></a>        left.append(bst.val)</span>
<span id="cb25-12"><a href="#cb25-12" aria-hidden="true" tabindex="-1"></a>        descend_left(bst.right)</span>
<span id="cb25-13"><a href="#cb25-13" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> left</span></code></pre></div>
<p>And that’s it!
We now have a tight, efficient, and iterative version of our original function.
Further, the code is close to idiomatic.</p>
<p>That’s it for this time.
If you have any questions or comments, just hit me at <a href="https://twitter.com/tmoertel">@tmoertel</a> or use the comment form below.</p>
<p>Thanks for reading!</p>
</div>
]]></summary>
</entry>

</feed>
