<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-18950714</id><updated>2011-11-25T13:51:54.474+11:00</updated><title type='text'>dmnd.blog</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://dmnd.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/18950714/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://dmnd.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>dmnd</name><uri>http://www.blogger.com/profile/14616860526835565749</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>1</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-18950714.post-113248059627556775</id><published>2005-11-23T23:06:00.000+11:00</published><updated>2005-11-25T11:34:25.463+11:00</updated><title type='text'>Going with the theme</title><content type='html'>&lt;span style="font-family:trebuchet ms;"&gt;I've rewritten the database from &lt;a href="http://www.gigamonkeys.com/book/practical-a-simple-database.html"&gt;Chapter 3&lt;/a&gt; (&lt;a href="http://dmnd.blogspot.com/2005/11/lesson-one-do-not-develop-exclusively.html"&gt;this time&lt;/a&gt; I even managed to save it to a file!), and I especially liked the final section on macros and duplication. Generalising &lt;span style="font-family:courier new;"&gt;where&lt;/span&gt; was great, but why stop with that? I tend to learn things much more thoroughly if I have to work them out for myself, so as an exercise for the reader (i.e. &lt;span style="font-style: italic;"&gt;myself&lt;/span&gt;) I decided perform some similar modifications to &lt;span style="font-family:courier new;"&gt;update&lt;/span&gt;... read on for my own extension to Chapter 3 of &lt;a href="http://www.gigamonkeys.com/book/"&gt;Practical Common Lisp&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;You should probably keep in mind that as this is my first foray into Lisp I quite possibly have no idea what I'm talking about. That said, here we go:&lt;br /&gt;&lt;br /&gt;So far, our &lt;span style="font-family:courier new;"&gt;update&lt;/span&gt; function looks like this:&lt;br /&gt;&lt;pre style="font-family: courier new;"&gt;&lt;br /&gt;(defun update (selector-fn &amp;key title artist rating (ripped nil ripped-p))&lt;br /&gt;(setf *db*&lt;br /&gt;    (mapcar&lt;br /&gt;    #'(lambda (row)&lt;br /&gt;       (when (funcall selector-fn row)&lt;br /&gt;         (if title    (setf (getf row :title) title))&lt;br /&gt;         (if artist   (setf (getf row :artist) artist))&lt;br /&gt;         (if rating   (setf (getf row :rating) rating))&lt;br /&gt;         (if ripped-p (setf (getf row :ripped) ripped)))&lt;br /&gt;       row) *db*)))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We're going to remove that messy duplication, and we'll use the same pattern that Siebel taught us when rewriting &lt;span style="font-family:courier new;"&gt;where&lt;/span&gt;. This involved the use of two helper-functions, &lt;span style="font-family:courier new;"&gt;make-comparison-expr&lt;/span&gt; and &lt;span style="font-family:courier new;"&gt;make-comparison-list&lt;/span&gt;&lt;span style="font-family:trebuchet ms;"&gt;, that fill in only the expressions that are actually used. So, start by defining a function to generate each of the test-assignment lines above in a function called &lt;span style="font-family:courier new;"&gt;make-update-expr&lt;/span&gt;:&lt;br /&gt;&lt;pre style="font-family:courier new;"&gt;&lt;br /&gt;(defun make-update-expr (field value)&lt;br /&gt;  `(setf (getf row ,field) ,value))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If you noticed something is different, you'd be correct: there is now no need for an &lt;span style="font-family:courier new;"&gt;if&lt;/span&gt; function. Because we are generating assignment expressions based on what is given in the arguments, there's no need to test for any conditions in this function. Remember also that the backquote (&lt;span style="font-family:courier new;"&gt;`&lt;/span&gt;) stops an entire form from being evaluated, and allows you to put a comma in front anything you do actually want evaluated. Testing this function at the REPL gives:&lt;br /&gt;&lt;pre style="font-family:courier new;"&gt;&lt;br /&gt;CL-USER&gt; (make-update-expr :artist "Rise Against")&lt;br /&gt;(SETF (GETF ROW :ARTIST) "Rise Against")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That works. Now we need to make a counterpart to &lt;span style="font-family:courier new;"&gt;make-comparison-list&lt;/span&gt; that instead creates a list of update expressions. Basing our new function on what we have learned of &lt;span style="font-family:courier new;"&gt;loop&lt;/span&gt; so far, this should do the trick:&lt;br /&gt;&lt;pre style="font-family:courier new;"&gt;&lt;br /&gt;(defun make-update-list (fields)&lt;br /&gt; (loop while fields&lt;br /&gt;       collecting (make-update-expr (pop fields) (pop fields))))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This is basically the same as &lt;span style="font-family:courier new;"&gt;make-comparison-expr&lt;/span&gt;, and should allow us to generate the interior part of &lt;span style="font-family:courier new;"&gt;update&lt;/span&gt; that will do all the work. Now we just need to wrap it up in a new macro. Basing the new macro on Siebel's earlier &lt;span style="font-family:courier new;"&gt;where&lt;/span&gt; macro allows us to get something similar to the following:&lt;br /&gt;&lt;pre style="font-family:courier new;"&gt;&lt;br /&gt;(defmacro update (selector-fn &amp;amp;rest clauses)&lt;br /&gt; `(setf *db*&lt;br /&gt;        (mapcar&lt;br /&gt;         #'(lambda (row)&lt;br /&gt;             (when (funcall ,selector-fn row)&lt;br /&gt;               ,@(make-update-list clauses))&lt;br /&gt;              row) *db*)))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It is worth noting the following:&lt;br /&gt;&lt;ul  style="font-family:trebuchet ms;"&gt;&lt;li&gt;&lt;span style="font-family:courier new;"&gt;update&lt;/span&gt; is now a macro, based on the earlier version of the function. This means it will generate only the necessary code each time it is called;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The use of &lt;span style="font-family:courier new;"&gt;&amp;rest&lt;/span&gt; means we are now decoupled from the specific fields of this database;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;We've backqouted the entire expression (so that our macro returns valid Lisp code) but use &lt;span style="font-family:courier new;"&gt;,@&lt;/span&gt; to ensure our call to &lt;span style="font-family:courier new;"&gt;make-update-list&lt;/span&gt; is evaluated (and "spliced" into a flat list).&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;These are all concepts that were used by Siebel to construct the &lt;span style="font-family:courier new;"&gt;where&lt;/span&gt; macro, but I think reiterating over them again really helps to develop a full understanding (at least it does in my own case).&lt;br /&gt;&lt;br /&gt;But I'm not finished quite just yet! There is yet one more piece of duplication in the code that I have decided to eliminate. Did you notice that &lt;span style="font-family:courier new;"&gt;make-comparison-list&lt;/span&gt; and &lt;span style="font-family:courier new;"&gt;make-update-list&lt;/span&gt; were almost identical? Here they are again:&lt;br /&gt;&lt;pre style="font-family:courier new;"&gt;&lt;br /&gt;(defun make-comparisons-list (fields)&lt;br /&gt;  (loop while fields&lt;br /&gt;        collecting (make-comparison-expr (pop fields) (pop fields))))&lt;br /&gt;&lt;br /&gt;(defun make-update-list (fields)&lt;br /&gt;  (loop while fields&lt;/span&gt;&lt;br /&gt;        collecting (make-update-expr (pop fields) (pop fields))))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;While there's only two of them, who actually &lt;span style="font-style: italic;"&gt;wants&lt;/span&gt; to write a new &lt;span style="font-family:courier new;"&gt;make-foo-list&lt;/span&gt; function every time they make a new expression generator? Using &lt;span style="font-family:courier new;"&gt;funcall&lt;/span&gt;, which was introduced earlier by Siebel, we can generalise these list-making functions pretty easily:&lt;br /&gt;&lt;pre style="font-family:courier new;"&gt;&lt;br /&gt;(defun make-expr-list (expr-maker fields)&lt;br /&gt;  (loop while fields&lt;br /&gt;        collecting (funcall expr-maker (pop fields) (pop fields))))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The &lt;span style="font-family:courier new;"&gt;expr-maker&lt;/span&gt; in the argument list is a reference to &lt;span style="font-family:courier new;"&gt;make-comparison-expr&lt;/span&gt; or &lt;span style="font-family:courier new;"&gt;make-update-expr&lt;/span&gt; or any other (bi-argumented) expression-making function that we end up needing. Having this function allows us to delete the duplication in &lt;span style="font-family:courier new;"&gt;make-comparisons-list&lt;/span&gt; and &lt;span style="font-family:courier new;"&gt;make-update-list&lt;/span&gt; and means we have to change our &lt;span style="font-family:courier new;"&gt;where&lt;/span&gt; and &lt;span style="font-family:courier new;"&gt;update&lt;/span&gt; macros to the following:&lt;br /&gt;&lt;pre style="font-family:courier new;"&gt;&lt;br /&gt;(defmacro where (&amp;rest clauses)&lt;br /&gt;  `#'(lambda (cd)&lt;br /&gt;       (and ,@(make-expr-list 'make-comparison-expr clauses))))&lt;br /&gt;&lt;br /&gt;(defmacro update (selector-fn &amp;amp;rest clauses)&lt;br /&gt;  `(setf *db*&lt;br /&gt;    (mapcar&lt;br /&gt;     #'(lambda (row)&lt;br /&gt;         (when (funcall ,selector-fn row)&lt;br /&gt;           ,@(make-expr-list 'make-update-expr clauses))&lt;br /&gt;         row) *db*)))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;All we have to remember is to put a quote in front of the expression generating function we want &lt;span style="font-family:courier new;"&gt;make-update-expr&lt;/span&gt; to use (if it's not there, Lisp tries to evaluate it as a variable), and we're done. &lt;span style="font-family:courier new;"&gt;update&lt;/span&gt; has been generalised, duplication has been removed and we also have a little expression list making function that would potentially be useful were we to continue adding features to this database.&lt;br /&gt;&lt;br /&gt;That's it for this chapter, but as I progress through the book I'll continue to post articles like this whenever the opportunity presents itself. I think that attempting to teach another person something requires a complete understanding of the content, so I plan to keep doing these little extensions (even if nobody ends up actually reading them).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/18950714-113248059627556775?l=dmnd.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dmnd.blogspot.com/feeds/113248059627556775/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=18950714&amp;postID=113248059627556775' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/18950714/posts/default/113248059627556775'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/18950714/posts/default/113248059627556775'/><link rel='alternate' type='text/html' href='http://dmnd.blogspot.com/2005/11/going-with-theme.html' title='Going with the theme'/><author><name>dmnd</name><uri>http://www.blogger.com/profile/14616860526835565749</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry></feed>
