134 lines
11 KiB
HTML
134 lines
11 KiB
HTML
|
<!DOCTYPE html>
|
||
|
<html lang='en'><head><meta charset='utf-8' /><meta name='pinterest' content='nopin' /><link href='../../../../static/css/style.css' rel='stylesheet' type='text/css' /><link href='../../../../static/css/print.css' rel='stylesheet' type='text/css' media='print' /><title>Customizing Common Lisp's Iterate: Averaging / Steve Losh</title></head><body><header><a id='logo' href='https://stevelosh.com/'>Steve Losh</a><nav><a href='../../../index.html'>Blog</a> - <a href='https://stevelosh.com/projects/'>Projects</a> - <a href='https://stevelosh.com/photography/'>Photography</a> - <a href='https://stevelosh.com/links/'>Links</a> - <a href='https://stevelosh.com/rss.xml'>Feed</a></nav></header><hr class='main-separator' /><main id='page-blog-entry'><article><h1><a href='index.html'>Customizing Common Lisp's Iterate: Averaging</a></h1><p class='date'>Posted on September 20th, 2016.</p><p>When I first started learning Common Lisp, one of the things I learned was the
|
||
|
<a href="http://www.gigamonkeys.com/book/loop-for-black-belts.html">loop macro</a>. <code>loop</code> is powerful, but it's not extensible and <a href="https://stevelosh.com/static/images/blog/2016/09/loop-macro.jpg">some
|
||
|
people find it ugly</a>. The <a href="https://common-lisp.net/project/iterate/">iterate</a> library was made to solve both of
|
||
|
these problems.</p>
|
||
|
|
||
|
<p>Unfortunately I haven't found many guides or resources on how to extend
|
||
|
<code>iterate</code>. The <code>iterate</code> manual describes the macros you need to use, but only
|
||
|
gives a few sparse examples. Sometimes it's helpful to see things in action.</p>
|
||
|
|
||
|
<p>I've made a few handy extensions myself in the past couple of months, so
|
||
|
I figured I'd post about them in case someone else is looking for examples of
|
||
|
how to write their own <code>iterate</code> clauses and drivers.</p>
|
||
|
|
||
|
<p>This entry is the first in a series:</p>
|
||
|
|
||
|
<ul>
|
||
|
<li><a href="index.html">Averaging</a></li>
|
||
|
<li><a href="../../10/iterate-timing/index.html">Timing</a></li>
|
||
|
</ul>
|
||
|
|
||
|
<p>This first post will show how to make a <code>averaging</code> clause that keeps a running
|
||
|
average of a given expression during the loop. I've found it handy in a couple
|
||
|
of places.</p>
|
||
|
|
||
|
<ol class="table-of-contents"><li><a href="index.html#s1-end-result">End Result</a></li><li><a href="index.html#s2-code">Code</a></li><li><a href="index.html#s3-debugging">Debugging</a></li></ol>
|
||
|
|
||
|
<h2 id="s1-end-result"><a href="index.html#s1-end-result">End Result</a></h2>
|
||
|
|
||
|
<p>Before we look at the code, let's look at what we're aiming for:</p>
|
||
|
|
||
|
<pre><code><span class="code"><span class="paren1">(<span class="code">iterate <span class="paren2">(<span class="code">for i <span class="keyword">:in</span> <span class="paren3">(<span class="code">list 20 10 10 20</span>)</span></span>)</span>
|
||
|
<span class="paren2">(<span class="code">averaging i</span>)</span></span>)</span>
|
||
|
<span class="comment">; =>
|
||
|
</span>15
|
||
|
|
||
|
<span class="paren1">(<span class="code">iterate <span class="paren2">(<span class="code">for l <span class="keyword">:in</span> '<span class="paren3">(<span class="code"><span class="paren4">(<span class="code">10 <span class="keyword">:foo</span></span>)</span> <span class="paren4">(<span class="code">20 <span class="keyword">:bar</span></span>)</span> <span class="paren4">(<span class="code">0 <span class="keyword">:baz</span></span>)</span></span>)</span></span>)</span>
|
||
|
<span class="paren2">(<span class="code">averaging <span class="paren3">(<span class="code">car l</span>)</span> <span class="keyword">:into</span> running-average</span>)</span>
|
||
|
<span class="paren2">(<span class="code">collect running-average</span>)</span></span>)</span>
|
||
|
<span class="comment">; =>
|
||
|
</span><span class="paren1">(<span class="code">10 15 10</span>)</span></span></code></pre>
|
||
|
|
||
|
<p>Simple enough. The <code>averaging</code> clause takes an expression (and optionally
|
||
|
a variable name) and averages its value over each iteration of the loop.</p>
|
||
|
|
||
|
<h2 id="s2-code"><a href="index.html#s2-code">Code</a></h2>
|
||
|
|
||
|
<p>There's not much code to <code>averaging</code>, but it does contain a few ideas that crop
|
||
|
up often when writing <code>iterate</code> extensions:</p>
|
||
|
|
||
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defmacro-clause</span></i> <span class="paren2">(<span class="code">AVERAGING expr &optional INTO var</span>)</span>
|
||
|
<span class="string">"Maintain a running average of `expr` in `var`.
|
||
|
|
||
|
If `var` is omitted the final average will be
|
||
|
returned instead.
|
||
|
|
||
|
Examples:
|
||
|
|
||
|
(iterate (for x :in '(0 10 0 10))
|
||
|
(averaging x))
|
||
|
=>
|
||
|
5
|
||
|
|
||
|
(iterate (for x :in '(1.0 1 2 3 4))
|
||
|
(averaging (/ x 10) :into avg)
|
||
|
(collect avg))
|
||
|
=>
|
||
|
(0.1 0.1 0.13333334 0.17500001 0.22)
|
||
|
|
||
|
"</span>
|
||
|
<span class="paren2">(<span class="code"><i><span class="symbol">with-gensyms</span></i> <span class="paren3">(<span class="code">count total</span>)</span>
|
||
|
<span class="paren3">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren4">(<span class="code"><span class="paren5">(<span class="code">average <span class="paren6">(<span class="code">or var <span class="special">iterate::*result-var*</span></span>)</span></span>)</span></span>)</span>
|
||
|
`<span class="paren4">(<span class="code"><i><span class="symbol">progn</span></i>
|
||
|
<span class="paren5">(<span class="code">for ,count <span class="keyword">:from</span> 1</span>)</span>
|
||
|
<span class="paren5">(<span class="code">sum ,expr <span class="keyword">:into</span> ,total</span>)</span>
|
||
|
<span class="paren5">(<span class="code">for ,average = <span class="paren6">(<span class="code">/ ,total ,count</span>)</span></span>)</span></span>)</span></span>)</span></span>)</span></span>)</span></span></code></pre>
|
||
|
|
||
|
<p>We use <code>defmacro-clause</code> to define the clause. Check <a href="https://common-lisp.net/project/iterate/doc/Rolling-Your-Own.html#Rolling-Your-Own">the iterate manual</a>
|
||
|
to learn more about the basics of that.</p>
|
||
|
|
||
|
<p>The first thing to note is the big docstring, which describes how to use the
|
||
|
clause and gives some examples. I prefer to err on the side of providing <em>more</em>
|
||
|
information in documentation rather than less. People who don't need the
|
||
|
hand-holding can quickly skim over it, but if you omit information it can leave
|
||
|
people confused. <a href="http://www.bash.org/?105201">Your monitor isn't going to run out of ink</a> and <a href="https://steve-yegge.blogspot.is/2008/09/programmings-dirtiest-little-secret.html">you
|
||
|
type fast (right?)</a> so be nice and just write the damn docs.</p>
|
||
|
|
||
|
<p>Next up is selecting the name of the variable for the average. The <code>(or var
|
||
|
iterate::*result-var*)</code> pattern is one I use often when writing <code>iterate</code>
|
||
|
clauses. It's kind of weird that <code>*result-var*</code> isn't external in the <code>iterate</code>
|
||
|
package, but this idiom is explicitly mentioned in the manual so I suppose it's
|
||
|
fine to use.</p>
|
||
|
|
||
|
<p>Finally, we <em>could</em> have written a simpler version of <code>averaging</code> that just
|
||
|
returned the result from the loop:</p>
|
||
|
|
||
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defmacro-clause</span></i> <span class="paren2">(<span class="code">AVERAGING expr</span>)</span>
|
||
|
<span class="paren2">(<span class="code"><i><span class="symbol">with-gensyms</span></i> <span class="paren3">(<span class="code">count total</span>)</span>
|
||
|
`<span class="paren3">(<span class="code"><i><span class="symbol">progn</span></i>
|
||
|
<span class="paren4">(<span class="code">for ,count <span class="keyword">:from</span> 1</span>)</span>
|
||
|
<span class="paren4">(<span class="code">sum ,expr <span class="keyword">:into</span> ,total</span>)</span>
|
||
|
<span class="paren4">(<span class="code">finally <span class="paren5">(<span class="code">return <span class="paren6">(<span class="code">/ ,total ,count</span>)</span></span>)</span></span>)</span></span>)</span></span>)</span></span>)</span></span></code></pre>
|
||
|
|
||
|
<p>This would work, but doesn't let us see the running average during the course of
|
||
|
the loop. <code>iterate</code>'s built-in clauses like <code>collect</code> and <code>sum</code> usually allow
|
||
|
you to access the "in-progress" value, so it's good for our extensions to
|
||
|
support it too.</p>
|
||
|
|
||
|
<h2 id="s3-debugging"><a href="index.html#s3-debugging">Debugging</a></h2>
|
||
|
|
||
|
<p>This clause is pretty simple, but more complicated ones can get a bit tricky.
|
||
|
When writing vanilla Lisp macros I usually end up writing the macro and then
|
||
|
<code>macroexpand-1</code>'ing a sample of it to make sure it's expanding to what I think
|
||
|
it should.</p>
|
||
|
|
||
|
<p>As far as I can tell there's no simple way to macroexpand an <code>iterate</code> clause on
|
||
|
its own. This is really a pain in the ass when you're trying to debug them, so
|
||
|
I <a href="https://github.com/sjl/cl-losh/blob/55de0419a9b97a35943ce4884b598dbd99cc5670/losh.lisp#L1105-L1151">hacked together</a> a <code>macroexpand-iterate</code> function for my own sanity.
|
||
|
It's not pretty, but it gets the job done:</p>
|
||
|
|
||
|
<pre><code><span class="code"><span class="paren1">(<span class="code">macroexpand-iterate '<span class="paren2">(<span class="code">averaging <span class="paren3">(<span class="code">* 2 x</span>)</span></span>)</span></span>)</span>
|
||
|
<span class="comment">; =>
|
||
|
</span><span class="paren1">(<span class="code"><i><span class="symbol">PROGN</span></i>
|
||
|
<span class="paren2">(<span class="code">FOR <span class="keyword">#:COUNT518</span> <span class="keyword">:FROM</span> 1</span>)</span>
|
||
|
<span class="paren2">(<span class="code">SUM <span class="paren3">(<span class="code">* 2 X</span>)</span> <span class="keyword">:INTO</span> <span class="keyword">#:TOTAL519</span></span>)</span>
|
||
|
<span class="paren2">(<span class="code">FOR <span class="special">ITERATE::*RESULT-VAR*</span> = <span class="paren3">(<span class="code">/ <span class="keyword">#:TOTAL519</span> <span class="keyword">#:COUNT518</span></span>)</span></span>)</span></span>)</span>
|
||
|
|
||
|
<span class="paren1">(<span class="code">macroexpand-iterate '<span class="paren2">(<span class="code">averaging <span class="paren3">(<span class="code">* 2 x</span>)</span> <span class="keyword">:into</span> foo</span>)</span></span>)</span>
|
||
|
<span class="comment">; =>
|
||
|
</span><span class="paren1">(<span class="code"><i><span class="symbol">PROGN</span></i>
|
||
|
<span class="paren2">(<span class="code">FOR <span class="keyword">#:COUNT520</span> <span class="keyword">:FROM</span> 1</span>)</span>
|
||
|
<span class="paren2">(<span class="code">SUM <span class="paren3">(<span class="code">* 2 X</span>)</span> <span class="keyword">:INTO</span> <span class="keyword">#:TOTAL521</span></span>)</span>
|
||
|
<span class="paren2">(<span class="code">FOR FOO = <span class="paren3">(<span class="code">/ <span class="keyword">#:TOTAL521</span> <span class="keyword">#:COUNT520</span></span>)</span></span>)</span></span>)</span></span></code></pre>
|
||
|
</article></main><hr class='main-separator' /><footer><nav><a href='https://github.com/sjl/'>GitHub</a> ・ <a href='https://twitter.com/stevelosh/'>Twitter</a> ・ <a href='https://instagram.com/thirtytwobirds/'>Instagram</a> ・ <a href='https://hg.stevelosh.com/.plan/'>.plan</a></nav></footer></body></html>
|