<?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-8366702</id><updated>2011-04-22T00:05:58.643+02:00</updated><category term='Python'/><category term='Knowledge representation'/><category term='Contest'/><category term='CLIPS'/><category term='Ruleby'/><category term='Family'/><category term='Cookbook'/><category term='Regular Expressions'/><category term='Statistics'/><category term='Free Software'/><category term='Fun Stuff'/><category term='benchmark'/><category term='GothPy'/><category term='Prolog'/><category term='Lisp'/><category term='Programming'/><category term='Testing'/><category term='Gothenburg'/><category term='Rete Algorithm'/><category term='Code Generation'/><category term='F/OSS'/><category term='PatternMatching'/><category term='Games'/><category term='Gingerbread house'/><category term='Pentago'/><category term='Mathematics'/><category term='AI'/><category term='Kōan'/><category term='Puzzles'/><category term='Debugging'/><category term='MPS'/><category term='Work'/><category term='Book'/><category term='Video'/><category term='Scheme'/><category term='Gnu'/><category term='CodeKata'/><category term='Neural Networks'/><category term='MonteCarlo'/><category term='Business Rules'/><category term='CSP'/><category term='Go'/><category term='LISA'/><category term='pyRete'/><category term='Markov Algorithm'/><category term='Toys'/><category term='pyClips'/><category term='JBoss Rules'/><category term='Backward Chaining'/><category term='Rule Engine'/><category term='Rools'/><category term='Humour'/><category term='Vacation'/><category term='Java'/><category term='OSX'/><category term='Expert system'/><category term='JBoss Drools'/><category term='Theory'/><category term='wxPython'/><category term='CLOS'/><category term='DataMining'/><category term='AquaMacs'/><category term='Ruby'/><category term='Peace'/><category term='Eowyn'/><category term='OLPC'/><category term='Emacs'/><category term='AI Player'/><category term='Erlang'/><category term='Rule compiler'/><title type='text'>// comments are lies!</title><subtitle type='html'>This blog is no longer updated!</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default?start-index=101&amp;max-results=100'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>202</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-8366702.post-303855609351942957</id><published>2008-11-15T21:05:00.002+01:00</published><updated>2008-11-15T21:11:50.982+01:00</updated><title type='text'>Time to say bye bye!</title><content type='html'>I've been thinking for quite some time now that maybe I should stop posting on this blog.&lt;br /&gt;&lt;br /&gt;There are many reasons behind this decision and maybe I'll start another blog somewhere down the line. But I simply don't believe that this is the right format anymore (maybe it never was, I don't know) for whatever it is that I'm trying to achieve with &lt;b&gt;// comments are lies!&lt;/b&gt; This will therefore be my last post here.&lt;br /&gt;&lt;br /&gt;I want to thank everyone who participated in the discussions we've had here. I've learned a LOT. Thank you.&lt;br /&gt;&lt;br /&gt;See you @ &lt;a href="http://twitter.com/johanlindberg"&gt;Twitter&lt;/a&gt;, &lt;a href="http://sourceforge.net/users/johanlindberg"&gt;SourceForge&lt;/a&gt;, &lt;a href="http://delicious.com/johanlindberg"&gt;Delicious&lt;/a&gt;, &lt;a href="http://github.com/johanlindberg"&gt;Github&lt;/a&gt;, &lt;a href="http://groups.google.com/groups/profile?enc_user=fCLFvhoAAAA9IZrT2QbN7VFW9n00vjsPfVkDoaoMBC1ZX5YCLbSZfw"&gt;Google Groups&lt;/a&gt;, &lt;a href="http://sv-se.facebook.com/people/Johan_Lindberg/576419422"&gt;Facebook&lt;/a&gt;, &lt;a href="http://www.linkedin.com/profile?viewProfile=&amp;amp;key=7486247&amp;amp;trk=tab_pro"&gt;LinkedIn&lt;/a&gt;, ...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-303855609351942957?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/303855609351942957/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=303855609351942957' title='1 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/303855609351942957'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/303855609351942957'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/11/time-to-say-bye-bye.html' title='Time to say bye bye!'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-3703927192140414870</id><published>2008-11-12T21:29:00.000+01:00</published><updated>2008-11-12T21:39:19.680+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DataMining'/><category scheme='http://www.blogger.com/atom/ns#' term='Statistics'/><title type='text'>Google Flutrends and predictive data mining</title><content type='html'>&lt;a href="http://www.google.org/"&gt;Google.org&lt;/a&gt; recently &lt;a href="http://blog.google.org/2008/11/tracking-flu-trends.html"&gt;announced&lt;/a&gt; &lt;a href="http://www.google.org/flutrends/"&gt;Flutrends&lt;/a&gt;, a web site that provides estimates of flu activity in the US based on search queries.&lt;br /&gt;&lt;br /&gt;I'm not sure whether this is a good thing or just plain scary and I'm not surprised that they've found such a strong correlation between their data and the &lt;a href="http://www.cdc.gov/flu/"&gt;government's&lt;/a&gt; either. I am, however, quite interested in knowing &lt;span style="font-style: italic;"&gt;what else&lt;/span&gt; is in there.&lt;br /&gt;&lt;br /&gt;In Ian Ayres' book &lt;a href="http://www.amazon.com/Super-Crunchers-Thinking-Numbers-Smart/dp/0553805401/ref=pd_bbs_sr_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1226517838&amp;amp;sr=8-1"&gt;Super Crunchers: Why Thinking-by-Numbers Is the New Way to Be Smart&lt;/a&gt; it is mentioned that &lt;a href="http://www.walmart.com/"&gt;Wal-Mart&lt;/a&gt; use data mining techniques in order to match shelf space against predicted customer demand. Pretty cool. And they're not alone. But they use closed data sets. Their own, of course, and data that they buy from companies like Terabyte.&lt;br /&gt;&lt;br /&gt;I hope that more companies and governments follow Google's example and open up (at least parts of) their data stores for public use. There are &lt;a href="http://www.programmableweb.com/mashup/bidnearby"&gt;lots&lt;/a&gt; and &lt;a href="http://www.programmableweb.com/mashup/bbc-news-map"&gt;lots&lt;/a&gt; of &lt;a href="http://www.programmableweb.com/popular/"&gt;mash-ups&lt;/a&gt; available that show the &lt;span style="font-style: italic;"&gt;potential&lt;/span&gt; of combining data sets and creating useful services to the public. Of course, for most companies they still have to find out that they actually &lt;span style="font-weight: bold;"&gt;have&lt;/span&gt; interesting data in their systems.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-3703927192140414870?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/3703927192140414870/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=3703927192140414870' title='2 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/3703927192140414870'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/3703927192140414870'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/11/google-flutrends-and-predictive-data.html' title='Google Flutrends and predictive data mining'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-8153080751102402201</id><published>2008-10-29T11:43:00.001+01:00</published><updated>2008-10-29T11:48:24.297+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='MonteCarlo'/><category scheme='http://www.blogger.com/atom/ns#' term='AI Player'/><category scheme='http://www.blogger.com/atom/ns#' term='Pentago'/><title type='text'>If the only tool you have...</title><content type='html'>"If the only tool you have is a hammer, you will see every problem as a nail." - &lt;a href="http://en.wikipedia.org/wiki/Abraham_Maslow"&gt;Abraham Maslow&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This week, I've been experimenting with &lt;a href="http://en.wikipedia.org/wiki/Monte_Carlo_method"&gt;Monte Carlo methods&lt;/a&gt; for a &lt;a href="http://www.pentago.com/"&gt;Pentago&lt;/a&gt; AI player. &lt;a href="http://commentsarelies.blogspot.com/2007/04/constructing-ai-player-for-pentago.html"&gt;I've written pattern-based AI players for Pentago before&lt;/a&gt; and (somewhere) I've got an unfinished implementation that uses &lt;a href="http://en.wikipedia.org/wiki/Minimax"&gt;Minimax&lt;/a&gt; as well.&lt;br /&gt;&lt;br /&gt;I didn't really expect much from this little experimental player (it's only about 100 lines of Python code) but it has turned out to be "not too bad". Despite the fact that it only considers one game state at a time... maybe that says more about my other players though ;-)&lt;br /&gt;&lt;br /&gt;I'll try to add &lt;a href="http://senseis.xmp.net/?MonteCarloTreeSearch"&gt;MCTS&lt;/a&gt; (Monte Carlo Tree Search) during this week, maybe even &lt;a href="http://senseis.xmp.net/?UCT"&gt;UCT&lt;/a&gt; (Upper Confidence bounds applied to Trees) which ought to make the player quite a bit stronger.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-8153080751102402201?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/8153080751102402201/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=8153080751102402201' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8153080751102402201'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8153080751102402201'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/10/if-only-tool-you-have.html' title='If the only tool you have...'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-5837078510197567964</id><published>2008-10-22T21:39:00.001+02:00</published><updated>2008-10-22T21:49:05.541+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Games'/><category scheme='http://www.blogger.com/atom/ns#' term='Go'/><category scheme='http://www.blogger.com/atom/ns#' term='MonteCarlo'/><category scheme='http://www.blogger.com/atom/ns#' term='AI Player'/><title type='text'>Computer Go reference bots</title><content type='html'>Interested in Computer Go? Then you probably already know about Don Dailey's excellent work on providing a number of &lt;a href="http://senseis.xmp.net/?MonteCarlo"&gt;Monte Carlo&lt;/a&gt; reference implementations. So far, he has managed to implement it in &lt;strike&gt;two&lt;/strike&gt; three different languages (Java, C and some language called &lt;a href="http://live.gnome.org/Vala"&gt;Vala&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;The announcement is &lt;a href="http://computer-go.org/pipermail/computer-go/2008-October/016624.html"&gt;here&lt;/a&gt; and the Java bot is available &lt;a href="http://cgos.boardspace.net/public/javabot.zip"&gt;here&lt;/a&gt;. It's not the prettiest Java code out there, but Don is a C programmer (I think) so he is excused. It was very interesting and educating to study the code. I've been reading a lot of the &lt;a href="http://www.computer-go.info/resources/papers.html"&gt;Monte Carlo Computer Go papers&lt;/a&gt; but I've always felt as if I've missed something fundamental since my understanding of how it works felt too... simple.&lt;br /&gt;&lt;br /&gt;Now that I've seen the code I can say, with confidence, that it *is* quite simple. Implementing a Monte Carlo method, that is. Getting a decent (let alone, good) AI-player for 19x19 Go seems quite far from simple.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-5837078510197567964?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/5837078510197567964/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=5837078510197567964' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/5837078510197567964'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/5837078510197567964'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/10/computer-go-reference-bots.html' title='Computer Go reference bots'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-3601571410928419168</id><published>2008-10-19T12:44:00.000+02:00</published><updated>2008-10-19T12:45:36.941+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='PatternMatching'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Pattern matching function declarations in Python part II</title><content type='html'>Ok, so the &lt;a href="http://commentsarelies.blogspot.com/2008/10/pattern-matching-function-declarations.html"&gt;pattern matching idea&lt;/a&gt; kind of backfired.&lt;br /&gt;&lt;br /&gt;I was under the impression that a function's default arguments wouldn't be evaluated until the function was called. Which would have meant that I could've changed them, into valid Python code, in the decorator function. But... that's not the way it works.&lt;br /&gt;&lt;br /&gt;My first attempt at something more interesting than matching a constant failed miserably. I wanted to use a variable to constrain that matching process. For example:&lt;pre&gt;&gt;&gt;&gt; @patternmatched&lt;br /&gt;... def foo(lst = [__, var, var, __]):&lt;br /&gt;...   pass&lt;/pre&gt;should match any &lt;span style="font-family: courier new;font-size:85%;" &gt;lst&lt;/span&gt; which is a list and where there is, at least one, repeated element somewhere in it. Both &lt;span style="font-family: courier new;font-size:85%;" &gt;[1,1,2,3,4]&lt;/span&gt; and &lt;span style="font-family: courier new;font-size:85%;" &gt;[3,2,1,1,2]&lt;/span&gt; should match, whilst &lt;span style="font-family: courier new;font-size:85%;" &gt;[1,2,1,3,1]&lt;/span&gt; shouldn't.&lt;br /&gt;&lt;br /&gt;Unfortunately. Trying the above results in a &lt;span style="font-family: courier new;font-size:85%;" &gt;NameError&lt;/span&gt; since both &lt;span style="font-family: courier new;font-size:85%;" &gt;__&lt;/span&gt; and &lt;span style="font-family: courier new;font-size:85%;" &gt;var&lt;/span&gt; are unbound. And, seeing that, I remembered that this is *exactly* what happened about three years ago when I was trying out various syntaxes for PyRete. It's the reason I decided to put all of the matching in the if-statement instead of in the function arguments.&lt;br /&gt;&lt;br /&gt;But, it's not quite time yet to give up. There are several possible ways to go about this. The "best" choice is probably to use Andrew's &lt;a href="http://www.dalkescientific.com/Python/python4ply.html"&gt;python4ply&lt;/a&gt;, or maybe &lt;a href="http://pypy.org/"&gt;PyPy&lt;/a&gt;. Either way, I'll get a chance to compare and contrast the work of implementing a DSL in Python versus Common Lisp.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-3601571410928419168?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/3601571410928419168/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=3601571410928419168' title='1 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/3601571410928419168'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/3601571410928419168'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/10/pattern-matching-function-declarations_19.html' title='Pattern matching function declarations in Python part II'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-1974621732750948575</id><published>2008-10-16T09:15:00.005+02:00</published><updated>2008-10-16T10:32:04.076+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Rule Engine'/><category scheme='http://www.blogger.com/atom/ns#' term='MPS'/><category scheme='http://www.blogger.com/atom/ns#' term='Lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><title type='text'>Handling defrules in MPS, executing the RHS</title><content type='html'>I've been rewriting &lt;a href="http://commentsarelies.blogspot.com/2008/08/handling-deftemplates-in-mps.html"&gt;the &lt;span style="font-family:courier new;font-size:85%;"&gt;deftemplate&lt;/span&gt; code&lt;/a&gt; for MPS more or less from scratch... but I'll have to talk about that some other time. It's high time to focus on the &lt;span style="font-family:courier new;font-size:85%;"&gt;defrule&lt;/span&gt; macro(s). I'll break this up into several posts and I'll start with the function for executing the RHS.&lt;br /&gt;&lt;br /&gt;The MPS syntax (a subset of CLIPS') for &lt;span style="font-family:courier new;font-size:85%;"&gt;defrule&lt;/span&gt; looks like this:&lt;br /&gt;&lt;pre&gt;  defrule-construct&lt;br /&gt;    ::= (&lt;span style="FONT-WEIGHT: bold"&gt;defrule&lt;/span&gt; rule-name&lt;br /&gt;          conditional-element*&lt;br /&gt;          &lt;span style="FONT-WEIGHT: bold"&gt;=&gt;&lt;/span&gt;&lt;br /&gt;          expression*)&lt;br /&gt;&lt;br /&gt;  conditional-element&lt;br /&gt;    ::= template-pattern-CE ¦ assigned-pattern-CE ¦&lt;br /&gt;        not-CE ¦ and-CE ¦ or-CE ¦ test-CE ¦&lt;br /&gt;        exists-CE ¦ forall-CE&lt;br /&gt;&lt;br /&gt;  assigned-pattern-CE&lt;br /&gt;    ::= single-field-variable &lt;span style="FONT-WEIGHT: bold"&gt;&lt;-&lt;/span&gt; template-pattern-CE&lt;br /&gt;&lt;br /&gt;  not-CE     ::= (&lt;span style="FONT-WEIGHT: bold"&gt;not&lt;/span&gt; conditional-element)&lt;br /&gt;  and-CE     ::= (&lt;span style="FONT-WEIGHT: bold"&gt;and&lt;/span&gt; conditional-element+)&lt;br /&gt;  or-CE      ::= (&lt;span style="FONT-WEIGHT: bold"&gt;or&lt;/span&gt; conditional-element+)&lt;br /&gt;  test-CE    ::= (&lt;span style="FONT-WEIGHT: bold"&gt;test&lt;/span&gt; function-call)&lt;br /&gt;  exists-CE  ::= (&lt;span style="FONT-WEIGHT: bold"&gt;exists&lt;/span&gt; conditional-element+)&lt;br /&gt;  forall-CE  ::= (&lt;span style="FONT-WEIGHT: bold"&gt;forall&lt;/span&gt; conditional-element&lt;br /&gt;                         conditional-element+)&lt;br /&gt;&lt;br /&gt;  template-pattern-CE&lt;br /&gt;    ::= (deftemplate-name single-field-LHS-slot*)&lt;br /&gt;&lt;br /&gt;  single-field-LHS-slot&lt;br /&gt;    ::= (slot-name constraint)&lt;br /&gt;&lt;br /&gt;  constraint ::= ?  connected-constraint&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  connected-constraint&lt;br /&gt;    ::= single-constraint ¦&lt;br /&gt;        single-constraint &lt;span style="FONT-WEIGHT: bold"&gt;&amp;amp;&lt;/span&gt; connected-constraint&lt;br /&gt;        single-constraint &lt;strong&gt;&lt;span style="FONT-WEIGHT: bold"&gt;¦&lt;/span&gt;&lt;/strong&gt; connected-constraint&lt;br /&gt;&lt;br /&gt;  single-constraint&lt;br /&gt;    ::= term ¦ ~term&lt;br /&gt;&lt;br /&gt;  term       ::= constant ¦ single-field-variable ¦&lt;br /&gt;                 &lt;span style="FONT-WEIGHT: bold"&gt;:&lt;/span&gt;function-call ¦ &lt;span style="FONT-WEIGHT: bold"&gt;=&lt;/span&gt;function-call&lt;br /&gt;&lt;br /&gt;  single-field-variable&lt;br /&gt;    ::= ?variable-symbol&lt;/pre&gt;I'm quite far from having all of the above supported, but I've got enough to show how the function that executes the RHS of a rule works.&lt;br /&gt;&lt;br /&gt;The &lt;span style="font-family:courier new;font-size:85%;"&gt;defrule&lt;/span&gt; macro works by expanding into a series of macro calls which handle more and more specific cases in the processing. The macro itself is quite simple&lt;br /&gt;&lt;pre&gt;(defmacro defrule (name &amp;amp;body body)&lt;br /&gt;  (let ((rhs (cdr (member '=&gt; body)))&lt;br /&gt;        (lhs (ldiff body (member '=&gt; body))))&lt;br /&gt;    `(progn&lt;br /&gt;       (compile-lhs ,name ,@lhs)&lt;br /&gt;       (compile-rhs ,name ,@rhs))))&lt;/pre&gt;As you can see, it expands into two other macro-calls: &lt;span style="font-family:courier new;font-size:85%;"&gt;compile-lhs&lt;/span&gt; and &lt;span style="font-family:courier new;font-size:85%;"&gt;compile-rhs&lt;/span&gt;, where the first parses the conditional-elements and, in the process, creates two symbol-tables (fact-bindings and variable-bindings) with all of the variables it can find.&lt;br /&gt;&lt;br /&gt;The second macro handles the RHS and looks like this:&lt;br /&gt;&lt;pre&gt;(defmacro compile-rhs (name &amp;amp;body rhs)&lt;br /&gt;  (when (null rhs)&lt;br /&gt;    (setf rhs '(t)))&lt;br /&gt;  `(defun ,(make-sym "RHS/" name) (activation)&lt;br /&gt;     (let* ((token (activation-token activation))&lt;br /&gt;            ,@(mapcar #'make-fact-binding (reverse fact-bindings))&lt;br /&gt;            ,@(mapcar #'make-variable-binding (reverse variable-bindings)))&lt;br /&gt;       ,@rhs)))&lt;/pre&gt;So, if we evaluate this:&lt;br /&gt;&lt;pre&gt;MPS&gt; (defrule foobar&lt;br /&gt;       ?foo &lt;- (foo (bar ?bar) (baz 1))&lt;br /&gt;        =&gt;&lt;br /&gt;       (format t "~%~A ~A" ?foo ?bar))&lt;/pre&gt;it expands into this (among other things):&lt;br /&gt;&lt;pre&gt;(DEFUN RHS/FOOBAR (ACTIVATION)&lt;br /&gt;  (LET* ((TOKEN (ACTIVATION-TOKEN ACTIVATION))&lt;br /&gt;         (?FOO (NTH 0 TOKEN))&lt;br /&gt;         (?BAR (DEFTEMPLATE/FOO-BAR ?FOO)))&lt;br /&gt;    (FORMAT T "~%~A ~A" ?FOO ?BAR)))&lt;/pre&gt;Most of the work is with figuring out how to assign values to each of the variables. I'm using a simple list as the structure for the tokens. WMEs (fact objects) are added in the order they appear in the list of conditional elements. Once all of the WMEs are bound, all of the variable-bindings are bound by using the automatically constructed accessor methods for those structs.&lt;br /&gt;&lt;br /&gt;The actions in the RHS are simply spliced into the &lt;span style="font-family:courier new;font-size:85%;"&gt;let*&lt;/span&gt; form at macro-expansion time which completes the function definition. It is then evaluated and stored with the rule's production node. Later, when that production node is left-activated, it creates an activation (with the WMEs and some additional meta-data like timestamps and such) and places it in the conflict-set. If that activation ever triggers a rule, it is passed as an argument to the RHS function.&lt;br /&gt;&lt;br /&gt;Since I haven't got enough of "the rest" of MPS in place. We're going to have to mock an activation and call the function directly to see whether or not it works.&lt;br /&gt;&lt;pre&gt;MPS&gt; (rhs/foobar (make-activation :token (list (foo (bar 1) (baz 1)))))&lt;br /&gt;&lt;span style="COLOR: rgb(255,153,0)"&gt;#S(DEFTEMPLATE/FOO :BAR 1 :BAZ 1) 1&lt;/span&gt;&lt;br /&gt;NIL&lt;br /&gt;MPS&gt;&lt;/pre&gt;There's really not much more to say about the RHS so I'll stop here. The LHS is a bit more complicated and I hope that I can show some of that code soon.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-1974621732750948575?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/1974621732750948575/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=1974621732750948575' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/1974621732750948575'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/1974621732750948575'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/10/handling-defrules-in-mps-executing-rhs.html' title='Handling defrules in MPS, executing the RHS'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-1647576063136489303</id><published>2008-10-13T15:32:00.004+02:00</published><updated>2008-10-19T12:45:52.356+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Rule Engine'/><category scheme='http://www.blogger.com/atom/ns#' term='PatternMatching'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Pattern matching function declarations in Python</title><content type='html'>All those who have tried to write a rules-based program know that there are, at least, two differences between "regular" programming and rules-based programming. The first is that you have no direct control over the flow of execution and the second is that, instead of specifying input parameters, you specify patterns that should be matched for a certain "function" (or rule) to be invoked.&lt;br /&gt;&lt;br /&gt;The bit about giving up control is quite difficult for most programmers (at least the ones I know) and it doesn't translate well to other types of programming anyway. But it would be interesting to try and add pattern matching functions to a language such as Python and see whether it could be useful (or at least fun to experiment with).&lt;br /&gt;&lt;br /&gt;It's not a new idea. The first language to support pattern matching this way was &lt;a href="http://en.wikipedia.org/wiki/Snobol"&gt;Snobol&lt;/a&gt; (version 4 I think) which was introduced in the late 60s/early 70s. Both &lt;a href="http://haskell.org/"&gt;Haskell&lt;/a&gt; and &lt;a href="http://www.erlang.org/"&gt;Erlang&lt;/a&gt; has it and there are a bunch of others as well, &lt;a href="http://www.lambdassociates.org/aboutqi.htm"&gt;Qi&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Prolog_%28programming_language%29"&gt;Prolog&lt;/a&gt; to mention a few.&lt;br /&gt;&lt;br /&gt;I'm thinking about something along the lines of:&lt;br /&gt;&lt;pre&gt;&gt;&gt;&gt; from patternmatching import *&lt;br /&gt;&gt;&gt;&gt; @patternmatched&lt;br /&gt;... def fac(n = 0):&lt;br /&gt;...   return 1&lt;br /&gt;...&lt;br /&gt;&gt;&gt;&gt; @patternmatched&lt;br /&gt;... def fac(n = int):&lt;br /&gt;...   return n* fac(n = n-1)&lt;br /&gt;...&lt;br /&gt;&gt;&gt;&gt; fac(5)&lt;br /&gt;120&lt;br /&gt;&gt;&gt;&gt;&lt;/pre&gt;The idea is that the first function would handle any calls to the &lt;span style="font-family:courier new;"&gt;fac&lt;/span&gt; function where the parameter &lt;span style="font-family:courier new;"&gt;n&lt;/span&gt; is &lt;span style="font-family:courier new;"&gt;0&lt;/span&gt; and the second function would handle any calls where the parameter &lt;span style="font-family:courier new;"&gt;n&lt;/span&gt; is an &lt;span style="font-family:courier new;"&gt;int&lt;/span&gt;  (but not 0). And, yes. I've got the above working already. The tricky bits are providing more elaborate forms of pattern matching with the available syntax.&lt;br /&gt;&lt;br /&gt;I'll be back shortly with some code...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-1647576063136489303?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/1647576063136489303/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=1647576063136489303' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/1647576063136489303'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/1647576063136489303'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/10/pattern-matching-function-declarations.html' title='Pattern matching function declarations in Python'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-1639873711164954250</id><published>2008-10-09T19:53:00.000+02:00</published><updated>2008-10-09T19:55:01.176+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Rule Engine'/><title type='text'>When should I use a rule engine?</title><content type='html'>This is an interesting question that I think about quite a lot. I consider myself somewhat of a rule engine evangelist and I've always felt a bit annoyed that it's so difficult to explain the benefits of a rules based approach to others.&lt;br /&gt;&lt;br /&gt;The textbook answer to the question is that a rule engine should be used when &lt;span style="font-style: italic;"&gt;the problem is ill-structured and the solution is difficult or impractical to describe with an algorithm&lt;/span&gt; or &lt;span style="font-style: italic;"&gt;when the knowledge required to formulate a solution changes frequently&lt;/span&gt;. But that's just words.&lt;br /&gt;&lt;br /&gt;I've tried to find an ill-structured problem that is small enough to use as an example but I'm not really sure one exists. The best I've got so far is the problem of translating a number to text, where 0 &lt;= number &lt;= 99. It's an ill-structured problem that is easy to grasp but still translates to an ugly implementation with a lot of special-cases-handling in most programming languages. Not that it translates to a very pretty CLIPS application either... but I guess that comes with the territory.&lt;br /&gt;&lt;br /&gt;Anyone got a better idea?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-1639873711164954250?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/1639873711164954250/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=1639873711164954250' title='10 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/1639873711164954250'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/1639873711164954250'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/10/when-should-i-use-rule-engine.html' title='When should I use a rule engine?'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-8940856140621918106</id><published>2008-09-28T17:48:00.003+02:00</published><updated>2008-10-01T10:09:30.668+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><title type='text'>CLIPS is not a Lisp</title><content type='html'>In a &lt;a href="http://www.mail-archive.com/rules-users@lists.jboss.org/msg06525.html"&gt;recent thread&lt;/a&gt; on &lt;a href="http://www.mail-archive.com/rules-users@lists.jboss.org/"&gt;JBoss Drools' Rules Users mailing list&lt;/a&gt;, Mark Proctor writes:&lt;br /&gt;&lt;blockquote&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:verdana;"&gt;... The Drools DRL language itself was designed as a more intuitive and less verbose language, this becomes increasinly important as you start to add more complex syntax which becomes harder to read with a lisp approach. I think most people in here would agree that the Drools DRL approach is an improvement over the lisp approach of clips/jess - apart from the die hard lisp fans.&lt;/span&gt;&lt;/span&gt;&lt;/blockquote&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:verdana;"&gt;&lt;/span&gt;&lt;/span&gt;I might be a die hard Lisp fan, but if you ask me, the problem is not that CLIPS (and Jess) are &lt;span style="FONT-STYLE: italic"&gt;Lisp&lt;/span&gt;-like it's that they are Lisp-&lt;span style="FONT-STYLE: italic"&gt;like&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;CLIPS doesn't use a Lisp approach. It might &lt;span style="FONT-STYLE: italic"&gt;look&lt;/span&gt; that way, but the similarities are only skin-deep. CLIPS lack one of the most fundamental requirements for being Lispy, namely, code as data. It lacks other things as well, but given &lt;span style="FONT-STYLE: italic"&gt;that&lt;/span&gt; capacity (or in other words to be able to provide a higher level syntactical abstraction on top of CLIPS) there would be nothing wrong with CLIPS syntax that couldn't be fixed with CLIPS syntax. Yes. I know about &lt;span style="font-family:courier new;font-size:85%;"&gt;build&lt;/span&gt; and &lt;span style="font-family:courier new;font-size:85%;"&gt;eval&lt;/span&gt; but they'll only take you so far. It's not that I'm unhappy with CLIPS, but I honestly believe a Lisp-based CLIPS would be so much better.&lt;br /&gt;&lt;br /&gt;My apologies to Mark. This rant has nothing to do with JBoss Drools. It's just that his post had the magic words in it ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-8940856140621918106?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/8940856140621918106/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=8940856140621918106' title='14 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8940856140621918106'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8940856140621918106'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/09/clips-is-not-lisp.html' title='CLIPS is not a Lisp'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>14</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-2348425498475254083</id><published>2008-09-23T20:23:00.000+02:00</published><updated>2008-09-23T20:29:34.176+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OSX'/><category scheme='http://www.blogger.com/atom/ns#' term='AquaMacs'/><category scheme='http://www.blogger.com/atom/ns#' term='Lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='Emacs'/><title type='text'>Updating my Common Lisp environment</title><content type='html'>I've been using &lt;a href="http://gigamonkeys.com/book/lispbox/"&gt;LispBox&lt;/a&gt; since &lt;a href="http://gigamonkeys.com/blog/"&gt;Peter&lt;/a&gt; first announced it. At home I've got both a Mac OS X version (using &lt;a href="http://clozure.com/clozurecl.html"&gt;OpenMCL&lt;/a&gt;) as well as a Windows version (using &lt;a href="http://www.clisp.org/"&gt;CLISP&lt;/a&gt;). I've also tried &lt;a href="http://common-lisp.net/project/lispbox/"&gt;Lisp In A Box&lt;/a&gt; and &lt;a href="http://www.newartisans.com/software/readylisp.html"&gt;Ready Lisp&lt;/a&gt; (using &lt;a href="http://www.sbcl.org/"&gt;SBCL&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;LispBox has worked &lt;span style="font-weight: bold;"&gt;great&lt;/span&gt; for me but it's time to take off the training wheels. So I've uninstalled all of my ready-to-go Lisp kits, downloaded a fresh snapshot of &lt;a href="http://common-lisp.net/project/slime/"&gt;SLIME&lt;/a&gt; and installed a copy of &lt;a href="http://aquamacs.org/"&gt;Aquamacs&lt;/a&gt;. One day, maybe, I'll be enough hacker to compile Emacs from source but that's going to have to wait a while...and Aquamacs boasts about being so much better &lt;a href="http://aquamacs.org/feature-matrix.shtml"&gt;in a direct comparison&lt;/a&gt; so I've decided to give it a try. I've yet to become familiar with Emacs' way of handling the clipboard anyway so this seems a perfect fit at the moment (Aquamacs allows you to use the "usual" Mac shortcuts).&lt;br /&gt;&lt;br /&gt;Installations were a lot easier than I thought. The only problem I ran into was actually that it took some time to get Finder to show the &lt;span style="font-style: italic;"&gt;.emacs.d&lt;/span&gt; folder (I had to use the menu option &lt;span style="font-style: italic;"&gt;Go to folder...&lt;/span&gt;). I've already got several CL implementations running (&lt;a href="http://ecls.sourceforge.net/"&gt;ECL&lt;/a&gt;, &lt;a href="http://clozure.com/clozurecl.html"&gt;Clozure&lt;/a&gt; and SBCL) on my Mac so now I just have to figure out either how to get SLIME to connect to several Lisps or how to customize Aquamacs so that I can do &lt;span style="font-family: courier new;font-size:85%;" &gt;M-x ECL&lt;/span&gt; to start SLIME with ECL.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-2348425498475254083?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/2348425498475254083/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=2348425498475254083' title='4 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/2348425498475254083'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/2348425498475254083'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/09/updating-my-common-lisp-environment.html' title='Updating my Common Lisp environment'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-9141416653644829185</id><published>2008-09-16T11:53:00.003+02:00</published><updated>2008-09-16T21:04:35.585+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Theory'/><category scheme='http://www.blogger.com/atom/ns#' term='Lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='CLOS'/><title type='text'>Structuring data via behavioural synthesis</title><content type='html'>I am one of the those who have been (and some still are, I guess) waiting for Geoff Wozniak to post a link to his thesis, &lt;em&gt;Structuring data via behavioural synthesis&lt;/em&gt;, on his blog &lt;a href="http://exploring-lisp.blogspot.com/"&gt;Exploring Lisp&lt;/a&gt;. To my surprise, it turns out that someone at the &lt;a href="http://www.csd.uwo.ca/"&gt;University of Western Ontario&lt;/a&gt; has &lt;a href="http://www.csd.uwo.ca/%7Ewatt/home/students/theses/GWozniak2008-phd.pdf"&gt;published it already&lt;/a&gt;, only "bad" thing with that is of course that now I want to see the code even more.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-9141416653644829185?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/9141416653644829185/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=9141416653644829185' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/9141416653644829185'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/9141416653644829185'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/09/structuring-data-via-behavioural.html' title='Structuring data via behavioural synthesis'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-558517104654094622</id><published>2008-09-13T21:30:00.000+02:00</published><updated>2008-09-13T21:31:44.375+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Lisp'/><title type='text'>Working out some kinks</title><content type='html'>I've been a student of Lisp(s) for roughly five years now. Unfortunately, sometimes, pretty simple bugs show up and bite me. I'm a bit embarrassed to say this but... I haven't written anything about MPS for a while because I've had a &lt;span style="font-style: italic;"&gt;problem&lt;/span&gt; with the proof-of-concept code. Or, so I thought. It turned out that the &lt;span style=";font-family:courier new;font-size:85%;"  &gt;agenda&lt;/span&gt; function used &lt;span style="font-size:85%;"&gt;&lt;a style="font-family: courier new;" href="http://www.lispworks.com/documentation/HyperSpec/Body/f_mapc_.htm#mapcan"&gt;mapcan&lt;/a&gt;&lt;/span&gt; instead of &lt;span style=";font-family:courier new;font-size:85%;"  &gt;&lt;a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_mapc_.htm#mapcar"&gt;mapcar&lt;/a&gt;&lt;/span&gt;. Funny (well...) thing is that since I &lt;b&gt;thought&lt;/b&gt; it used &lt;span style=";font-family:courier new;font-size:85%;"  &gt;mapcar&lt;/span&gt; it took me quite a while to find it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-558517104654094622?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/558517104654094622/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=558517104654094622' title='3 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/558517104654094622'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/558517104654094622'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/09/working-out-some-kinks.html' title='Working out some kinks'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-126343965470151210</id><published>2008-09-04T21:04:00.001+02:00</published><updated>2008-09-04T21:04:00.955+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AI'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Go'/><title type='text'>CrazyStone vs Aoba Kaori</title><content type='html'>Yet another Computer Go program wins a handicap game (8 stones) against a professional Go player. Earlier today, &lt;a href="http://remi.coulom.free.fr/CrazyStone/"&gt;CrazyStone&lt;/a&gt; (running on a PC with 8 processors) played &lt;a href="http://www.nihonkiin.or.jp/player/htm/ki000343.htm"&gt;Aoba Kaori (4P)&lt;/a&gt; at the &lt;a href="http://www.ipsj.or.jp/10jigyo/fit/fit2008/index.html"&gt;FIT2008&lt;/a&gt; conference (here are some  &lt;a href="http://www.yss-aya.com/photo/20080904fit/index01.html"&gt;photos&lt;/a&gt;) and won by resignation.&lt;br /&gt;&lt;br /&gt;In a couple of weeks, &lt;a href="http://www.lri.fr/%7Egelly/MoGo.htm"&gt;MoGo&lt;/a&gt; plays Myungwan Kim (&lt;a href="http://commentsarelies.blogspot.com/2008/08/mogo-beats-myungwan-kim-8p-at-us-go.html"&gt;again&lt;/a&gt;) at the &lt;a href="http://www.cotsengotournament.com/"&gt;Cotsen Go Tournament&lt;/a&gt;. It will be interesting to see whether or not they can repeat the success from the US Go Congress. I have a feeling that Kim will have adapted to MoGo's playing style.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-126343965470151210?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/126343965470151210/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=126343965470151210' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/126343965470151210'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/126343965470151210'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/09/crazystone-vs-aoba-kaori.html' title='CrazyStone vs Aoba Kaori'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-8785321435790775593</id><published>2008-09-02T14:19:00.001+02:00</published><updated>2008-09-02T17:23:42.281+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='GothPy'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Roman numerals</title><content type='html'>Yesterday we had another &lt;a href="http://groups.google.se/group/gothpy?hl=sv"&gt;GothPy&lt;/a&gt; meeting. Apart from getting some formalities out of the way, Emily showed us how to do the &lt;a href="http://codingdojo.org/cgi-bin/wiki.pl?KataFizzBuzz"&gt;FizzBuzz kata&lt;/a&gt; in just over 4 minutes. Quite impressive. This was one of the things &lt;a href="http://emilybache.blogspot.com/2008/08/onwards-with-stars.html"&gt;she and Michael Feathers had done for the &lt;em&gt;Programming with the Stars&lt;/em&gt; competition&lt;/a&gt; at &lt;a href="http://agile2008.org/"&gt;Agile 2008&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;We were also supposed to tackle a "new" kata, converting numbers to roman numerals, but a debate about what rules should be used to guide the conversion (for example, should 1999 be 'IMM' or 'MCMXCIX'?) got us into doing an english translation instead (converting numbers to english text). And as the &lt;a href="http://c2.com/cgi/wiki?SmugLispWeenie"&gt;smug Lisp weenie&lt;/a&gt; I have turned into I couldn't help but to inform everyone that the built-in format directives &lt;span&gt;~@R&lt;/span&gt; and ~R solves those problems quite neatly. The hard part is of course to write the Lisp implementation ;-)&lt;br /&gt;&lt;br /&gt;Anyway, as usual, it turned out we thought about the problem a bit differently but we quickly converged around a "good enough" strategy even though it wasn't particularly pretty and we ended up with lots of code duplication. I learned a lot though. It put &lt;a href="http://commentsarelies.blogspot.com/2007/06/simple-re-write-system-using-markov.html"&gt;my previous solution&lt;/a&gt; in a different light and it's always interesting to see and hear how others tackle awkward problems like this.&lt;br /&gt;&lt;br /&gt;And BTW. Here is my attempt at the Roman numerals kata (using doctest):&lt;br /&gt;&lt;pre&gt;def as_roman_numeral(num):&lt;br /&gt;   """&lt;br /&gt;   Returns a string containing the roman numeral representation of num or&lt;br /&gt;   None if num is outside of range(1,4000).&lt;br /&gt; &lt;br /&gt;   &gt;&gt;&gt; as_roman_numeral(4000) == None&lt;br /&gt;   True&lt;br /&gt;   &gt;&gt;&gt; as_roman_numeral(1)&lt;br /&gt;   'I'&lt;br /&gt;   &gt;&gt;&gt; as_roman_numeral(99)&lt;br /&gt;   'XCIX'&lt;br /&gt;   &gt;&gt;&gt; as_roman_numeral(888)&lt;br /&gt;   'DCCCLXXXVIII'&lt;br /&gt;   &gt;&gt;&gt; as_roman_numeral(1999)&lt;br /&gt;   'MCMXCIX'&lt;br /&gt;   &gt;&gt;&gt; as_roman_numeral(2001)&lt;br /&gt;   'MMI'&lt;br /&gt;   """&lt;br /&gt; &lt;br /&gt;   units       = [0, 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX']&lt;br /&gt;   tens        = [0, 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC']&lt;br /&gt;   hundreds    = [0, 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM']&lt;br /&gt;   thousands   = [0, 'M', 'MM', 'MMM']&lt;br /&gt; &lt;br /&gt;   if num in xrange(1,4000):&lt;br /&gt;       th, mils = num//1000, num%1000&lt;br /&gt;       h, cents = mils//100, mils%100&lt;br /&gt;       te, u    = cents//10, cents%10&lt;br /&gt;       return "".join(numerals[digit] for digit, numerals in [(th, thousands),&lt;br /&gt;                                                              (h, hundreds),&lt;br /&gt;                                                              (te, tens),&lt;br /&gt;                                                              (u, units)] if digit &gt; 0)&lt;br /&gt;   else:&lt;br /&gt;       return None&lt;/pre&gt;It works with numbers in range(1,4000) and it implements &lt;a href="http://en.wikipedia.org/wiki/Roman_numeral#XCIX_vs._IC"&gt;the rules as described on Wikipedia&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-8785321435790775593?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/8785321435790775593/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=8785321435790775593' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8785321435790775593'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8785321435790775593'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/09/roman-numerals.html' title='Roman numerals'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-4462121861340297190</id><published>2008-08-23T10:09:00.000+02:00</published><updated>2008-08-23T10:11:23.279+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Rule Engine'/><category scheme='http://www.blogger.com/atom/ns#' term='MPS'/><category scheme='http://www.blogger.com/atom/ns#' term='Lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><title type='text'>MPS inference engine, the rest of the implementation</title><content type='html'>In the &lt;a href="http://commentsarelies.blogspot.com/2008/08/mps-inference-engine-rete-network.html"&gt;last post&lt;/a&gt; I mentioned a few of the design choices I've made for the Rete Network implementation. The rest of the engine is quite straight forward and simple. There's really not much to talk about but I'll show some code anyway ;-).&lt;br /&gt;&lt;br /&gt;Another thing though, and a bit more interesting really, is that I have been thinking about whether or not I should try to stay true to CLIPS' behaviour and functionality in the misc engine functions as well.&lt;br /&gt;&lt;br /&gt;For example, the &lt;span style=";font-family:courier new;font-size:85%;"  &gt;agenda&lt;/span&gt; function currently &lt;em&gt;returns&lt;/em&gt; a list of all activations on the agenda and CLIPS &lt;em&gt;prints&lt;/em&gt; them but returns no value. Similarly there's &lt;span style=";font-family:courier new;font-size:85%;"  &gt;facts&lt;/span&gt; which behaves like &lt;span style=";font-family:courier new;font-size:85%;"  &gt;get-fact-list&lt;/span&gt; instead. Used at the REPL there's little difference in what the user sees but the reason I've made them different is because then I can write other functions on top of them.&lt;br /&gt;&lt;br /&gt;Ok. As promised, some code. Here is the &lt;span style=";font-family:courier new;font-size:85%;"  &gt;run&lt;/span&gt; function (which uses &lt;span style=";font-family:courier new;font-size:85%;"  &gt;agenda&lt;/span&gt;):&lt;pre&gt;|(defun run (&amp;amp;optional (limit -1))&lt;br /&gt;|  (do* ((curr-agenda (agenda) (agenda))&lt;br /&gt;|        (execution-count 0 (+ execution-count 1))&lt;br /&gt;|        (limit limit (- limit 1)))&lt;br /&gt;|       ((or (eq limit 0)&lt;br /&gt;|            (= (length curr-agenda) 0)) execution-count)&lt;br /&gt;|    (let* ((activation (first curr-agenda))&lt;br /&gt;|           (rhs-func (make-sym "RHS-" (string (activation-rule activation))))&lt;br /&gt;|           (prod-mem (make-sym "PRODUCTION-" (string (activation-rule activation)) "-MEMORY")))&lt;br /&gt;|      (funcall rhs-func activation)&lt;br /&gt;|      (store '- activation prod-mem))))&lt;/pre&gt;and here is the &lt;span style="font-family: courier new;font-size:85%;" &gt;assert&lt;/span&gt; function (I'm shadowing the built-in &lt;span style="font-family: courier new;font-size:85%;" &gt;assert&lt;/span&gt; function, still accessible as &lt;span style="font-family: courier new;font-size:85%;" &gt;cl:assert&lt;/span&gt; though):&lt;pre&gt;|(defun assert (&amp;amp;rest facts)&lt;br /&gt;|  (incf timestamp)&lt;br /&gt;|  (dolist (fact facts)&lt;br /&gt;|    (store '+ fact 'working-memory)&lt;br /&gt;|    (mapcar #'(lambda (node) (funcall node '+ fact timestamp))&lt;br /&gt;|            (gethash (type-of fact) (gethash 'root rete-network)))))&lt;/pre&gt;I hope that they are at least somewhat readable to others.&lt;br /&gt;&lt;br /&gt;I've now finished the functions that make up the inference engine implementation. And it &lt;span style="font-style: italic;"&gt;works&lt;/span&gt;... as long as you manually divide your program into alpha, beta and production nodes and connect them properly in the Rete Network ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-4462121861340297190?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/4462121861340297190/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=4462121861340297190' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/4462121861340297190'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/4462121861340297190'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/08/mps-inference-engine-rest-of.html' title='MPS inference engine, the rest of the implementation'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-2175415398148736762</id><published>2008-08-18T20:49:00.004+02:00</published><updated>2008-08-18T21:13:25.580+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Family'/><category scheme='http://www.blogger.com/atom/ns#' term='Eowyn'/><category scheme='http://www.blogger.com/atom/ns#' term='Work'/><title type='text'>Back at work</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_3hqf70Lx0Mk/SKnGodDS5_I/AAAAAAAAAEw/VpQSL94EtTQ/s1600-h/IMG_4885.JPG"&gt;&lt;img style="cursor: pointer;" src="http://3.bp.blogspot.com/_3hqf70Lx0Mk/SKnGodDS5_I/AAAAAAAAAEw/VpQSL94EtTQ/s320/IMG_4885.JPG" alt="" id="BLOGGER_PHOTO_ID_5235934440090429426" border="0" /&gt;&lt;/a&gt; &lt;span style="font-size:85%;"&gt;&lt;span style="font-style: italic;"&gt;The future is that way Mom (and it does &lt;a href="http://www.lambdassociates.org/lC21.htm"&gt;pattern matching&lt;/a&gt;)!&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Today was my first day back at &lt;a href="http://www.nordea.com/"&gt;work&lt;/a&gt;. The past eight months have been great! Eo and I have had a blast, but it's time to go on - facing new and exciting challenges and that means tackling a new business area for me and &lt;a href="http://en.wikipedia.org/wiki/Day_care"&gt;day care&lt;/a&gt; for Eo.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-2175415398148736762?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/2175415398148736762/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=2175415398148736762' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/2175415398148736762'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/2175415398148736762'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/08/back-at-work.html' title='Back at work'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_3hqf70Lx0Mk/SKnGodDS5_I/AAAAAAAAAEw/VpQSL94EtTQ/s72-c/IMG_4885.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-817809973812210305</id><published>2008-08-15T14:28:00.000+02:00</published><updated>2008-08-15T14:28:25.298+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Rule Engine'/><category scheme='http://www.blogger.com/atom/ns#' term='MPS'/><category scheme='http://www.blogger.com/atom/ns#' term='Lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><title type='text'>MPS inference engine, the Rete Network implementation</title><content type='html'>It's difficult to talk about the MPS &lt;span style=";font-family:courier new;font-size:85%;"  &gt;defrule&lt;/span&gt; macro and the code it expands to without first explaining the environment in which it is meant to run. So I'll try to explain and describe my  current thoughts and ideas that make up the design of the MPS inference engine in this and following posts.&lt;br /&gt;&lt;br /&gt;The engine is made up of some I/O functions (&lt;span style="font-family: courier new;font-size:85%;" &gt;assert&lt;/span&gt; and &lt;span style="font-family: courier new;font-size:85%;" &gt;retract&lt;/span&gt;), the &lt;a href="http://en.wikipedia.org/wiki/Rete_algorithm"&gt;Rete Network&lt;/a&gt;, a Conflict Resolver and an Execution Engine. The Rete Network is the most central part  of the whole thing so I'll start there.&lt;br /&gt;&lt;br /&gt;It is basically implemented within a hash-table. This is the simplest possible representation of a Rete Network that I can think of. There are, of course, a few helper functions to abstract away some of the book keeping details of this design choice. During rule compilation there are functions for adding a node/memory and connecting nodes/memories with each other and at run time there are functions to propagate facts/tokens, access contents of and store facts/tokens in memory.&lt;br /&gt;&lt;br /&gt;A processing node (for example an alpha node) is implemented as a function and is stored in the hash-table as well. A very simple LHS construct like:&lt;pre&gt;|(defrule foo&lt;br /&gt;|  (bar (baz ?baz&amp;amp;:(&gt; ?baz 10)))&lt;br /&gt;|  =&gt;)&lt;/pre&gt;would be represented in the Rete Network by the following function:&lt;pre&gt;|(defun bar-baz-&gt;-10 (key fact timestamp)&lt;br /&gt;|  (when (&gt; (deftemplate/bar-baz fact) 10)&lt;br /&gt;|    (store key fact 'bar-baz-&gt;-10-memory)&lt;br /&gt;|    (propagate key fact timestamp 'bar-baz-&gt;-10)))&lt;/pre&gt;A join node is represented by two functions (one for left and one for right activation) and is slightly more complex (but not much). If we change the LHS to:&lt;pre&gt;|(defrule foo&lt;br /&gt;|  (bar-1 (baz ?baz))&lt;br /&gt;|  (bar-2 (baz ?baz))&lt;br /&gt;|  =&gt;)&lt;/pre&gt;we would have to create a join node for joining facts on the &lt;span style=";font-family:courier new;font-size:85%;"  &gt;?baz&lt;/span&gt; variable. It would look something like this:&lt;pre&gt;|(let ((left-memory  'bind-bar-1-memory)&lt;br /&gt;|      (right-memory 'bind-bar-2-memory))&lt;br /&gt;|  (defun join-bar-1-baz-and-bar-2-baz-left (key token timestamp)&lt;br /&gt;|    (dolist (fact (contents-of right-memory))&lt;br /&gt;|      (when (eq (bar-1-baz (nth 0 token))&lt;br /&gt;|                (bar-2-baz fact))&lt;br /&gt;|        (store key (append token (list fact)) 'join-bar-1-baz-and-bar-2-baz-memory)&lt;br /&gt;|        (propagate key (append token (list fact)) timestamp 'join-bar-1-baz-and-bar-2-baz))))&lt;br /&gt;|&lt;br /&gt;|  (defun join-bar-1-baz-and-bar-2-baz-right (key fact timestamp)&lt;br /&gt;|    (dolist (token (contents-of left-memory))&lt;br /&gt;|      (when (eq (bar-1-baz (nth 0 token))&lt;br /&gt;|                (bar-2-baz fact))&lt;br /&gt;|        (store key (append token (list fact)) 'join-bar-1-baz-and-bar-2-baz-memory)&lt;br /&gt;|        (propagate key (append token (list fact)) timestamp 'join-bar-1-baz-and-bar-2-baz)))))&lt;/pre&gt;As you can see variables are expanded into "positions" everywhere in the functions. Variables are, however, available and bound in the execution context of the production node's RHS function.&lt;br /&gt;&lt;br /&gt;There are some more bits and pieces that are good to know. The root node, for example, is a hash-table. Each deftemplate-type is a key and the value contains a list of alpha nodes.&lt;br /&gt;&lt;br /&gt;Compilation consists of (apart from expanding the rule's LHS into a number of functions) a series of calls to &lt;span style="font-family: courier new;font-size:85%;" &gt;add-to-root&lt;/span&gt; and &lt;span style="font-family: courier new;font-size:85%;" &gt;connect-nodes&lt;/span&gt;. Since each node is responsible for propagating as well as storing facts/tokens in memory we can connect the processing nodes to each other directly. The example above would be compiled with:&lt;pre&gt;|(add-to-root 'bar-1 #'bind-bar-1) &lt;span style="color: rgb(255, 102, 102);"&gt;; This might not be necessary! We should&lt;/span&gt;&lt;br /&gt;|(add-to-root 'bar-2 #'bind-bar-2) &lt;span style="color: rgb(255, 102, 102);"&gt;; be able to connect directly to the join.&lt;/span&gt;&lt;br /&gt;|(connect-nodes 'bind-bar-1 #'join-bar-1-baz-and-bar-2-baz-left)&lt;br /&gt;|(connect-nodes 'bind-bar-2 #'join-bar-1-baz-and-bar-2-baz-right)&lt;br /&gt;|(connect-nodes 'join-bar-1-baz-and-bar-2-baz #'production-foo)&lt;/pre&gt;This is all theory, though. I don't actually have all of the &lt;span style=";font-family:courier new;font-size:85%;"  &gt;defrule&lt;/span&gt; macro in place so the final version might very well expand into something different. But I hope this conveys the general idea of the Rete Network implementation.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-817809973812210305?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/817809973812210305/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=817809973812210305' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/817809973812210305'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/817809973812210305'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/08/mps-inference-engine-rete-network.html' title='MPS inference engine, the Rete Network implementation'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-7543230109130035689</id><published>2008-08-11T13:24:00.002+02:00</published><updated>2008-08-11T13:47:51.509+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Rule Engine'/><category scheme='http://www.blogger.com/atom/ns#' term='MPS'/><category scheme='http://www.blogger.com/atom/ns#' term='Lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><title type='text'>Handling deftemplates in MPS</title><content type='html'>There are three  parts to my MPS (Minimal Production System) experiment, the "engine" itself and the &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;deftemplate&lt;/span&gt;&lt;/span&gt; and &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;defrule&lt;/span&gt;&lt;/span&gt; macros. So far, I've spent most of my time on the &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;deftemplate&lt;/span&gt;&lt;/span&gt; macro. It turned out to be a bit trickier than I thought to implement. Mostly because I was stuck thinking about too complex macro expansions (which I never got to work).&lt;br /&gt;&lt;br /&gt;Here is the syntax as BNF (well, sort of[1]):&lt;pre&gt;|  deftemplate-construct&lt;br /&gt;|    ::= (&lt;span style="font-weight: bold;"&gt;deftemplate&lt;/span&gt; deftemplate-name&lt;br /&gt;|                     [comment]&lt;br /&gt;|                     single-slot-definition*)&lt;br /&gt;|&lt;br /&gt;|  single-slot-definition&lt;br /&gt;|    ::= (&lt;span style="font-weight: bold;"&gt;slot&lt;/span&gt; slot-name [default-attribute])&lt;br /&gt;|&lt;br /&gt;|  default-attribute&lt;br /&gt;|    ::= (&lt;span style="font-weight: bold;"&gt;default&lt;/span&gt; ?NONE | expression) |&lt;br /&gt;|        (&lt;span style="font-weight: bold;"&gt;default-dynamic&lt;/span&gt; expression)&lt;/pre&gt;It is a subset of CLIPS' deftemplate syntax. There are no multislots and no constraint-attributes for slots (types, ranges and such). But, apart from that, it is more or less the same[2]. And here's how it works:&lt;pre&gt;|; SLIME 2007-03-14&lt;br /&gt;|;;;; Compile file / [...] /mps. ...&lt;br /&gt;|&lt;span style="color: rgb(153, 153, 255);"&gt;CL-USER&gt;&lt;/span&gt; (in-package :mps)&lt;br /&gt;|&lt;span style="color: rgb(255, 102, 102);"&gt;#[Package "MPS"]&lt;/span&gt;&lt;br /&gt;|&lt;span style="color: rgb(153, 153, 255);"&gt;MPS&gt;&lt;/span&gt; (deftemplate foo&lt;br /&gt;|       (slot a-slot)&lt;br /&gt;|       (slot a-default-slot (default 1))&lt;br /&gt;|       (slot required-slot (default ?NONE)))&lt;br /&gt;|&lt;span style="color: rgb(255, 102, 102);"&gt;FOO&lt;/span&gt;&lt;br /&gt;|&lt;span style="color: rgb(153, 153, 255);"&gt;MPS&gt;&lt;/span&gt; (foo)&lt;br /&gt;|&lt;br /&gt;|The slot: REQUIRED-SLOT in deftemplate: FOO requires an explicit value.&lt;br /&gt;|   [Condition of type SIMPLE-ERROR]&lt;br /&gt;|&lt;br /&gt;|Restarts:&lt;br /&gt;| 0: [ABORT] Return to SLIME's top level.&lt;br /&gt;| 1: [ABORT-BREAK] Reset this process&lt;br /&gt;| 2: [ABORT] Kill this process&lt;br /&gt;|&lt;br /&gt;|&lt;span style="color: rgb(255, 204, 153);"&gt;Invoking restart: Return to SLIME's top level.&lt;/span&gt;&lt;br /&gt;|; Evaluation aborted&lt;br /&gt;|&lt;span style="color: rgb(153, 153, 255);"&gt;MPS&gt;&lt;/span&gt; (foo (required-slot 1))&lt;br /&gt;|&lt;span style="color: rgb(255, 102, 102);"&gt;#S(DEFTEMPLATE/FOO :A-SLOT NIL :A-DEFAULT-SLOT 1 :REQUIRED-SLOT 1)&lt;/span&gt;&lt;br /&gt;|&lt;span style="color: rgb(153, 153, 255);"&gt;MPS&gt;&lt;/span&gt; (deftemplate bar&lt;br /&gt;|       (slot default-gensym (default (gensym)))&lt;br /&gt;|       (slot dynamic-gensym (default-dynamic (gensym))))&lt;br /&gt;|&lt;span style="color: rgb(255, 102, 102);"&gt;BAR&lt;/span&gt;&lt;br /&gt;|&lt;span style="color: rgb(153, 153, 255);"&gt;MPS&gt;&lt;/span&gt; (bar)&lt;br /&gt;|&lt;span style="color: rgb(255, 102, 102);"&gt;#S(DEFTEMPLATE/BAR :DEFAULT-GENSYM #:G31 :DYNAMIC-GENSYM #:G40)&lt;/span&gt;&lt;br /&gt;|&lt;span style="color: rgb(153, 153, 255);"&gt;MPS&gt;&lt;/span&gt; (bar)&lt;br /&gt;|&lt;span style="color: rgb(255, 102, 102);"&gt;#S(DEFTEMPLATE/BAR :DEFAULT-GENSYM #:G31 :DYNAMIC-GENSYM #:G41)&lt;/span&gt;&lt;br /&gt;|&lt;span style="color: rgb(153, 153, 255);"&gt;MPS&gt;&lt;/span&gt;&lt;/pre&gt;The macro expands into a &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;defstruct&lt;/span&gt;&lt;/span&gt; (deftemplate/foo) and another &lt;span style=";font-family:courier new;font-size:85%;"  &gt;defmacro&lt;/span&gt; (foo) that is used as a constructor for the template. The reason it is a macro and not a regular function is because a function's arguments are evaluated whilst a macro's is not. And since there's no function named  &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;a-slot&lt;/span&gt;&lt;/span&gt; or &lt;span style=";font-family:courier new;font-size:85%;"  &gt;a-default-slot&lt;/span&gt; etc. So we'd be thrown into the debugger if we tried to evaluate something like &lt;span style="font-family: courier new;font-size:85%;" &gt;(foo (a-slot 1))&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Here's the macroexpansion of a simple template:&lt;br /&gt;&lt;pre&gt;|&lt;span style="color: rgb(153, 153, 255);"&gt;MPS&gt;&lt;/span&gt; (pprint (macroexpand-1 '(deftemplate foo&lt;br /&gt;|                               (slot a)&lt;br /&gt;|                               (slot b (default 1)))))&lt;br /&gt;|&lt;br /&gt;|&lt;span style="color: rgb(255, 204, 153);"&gt;(PROGN (DEFSTRUCT DEFTEMPLATE/FOO "" (A NIL) (B 1))&lt;/span&gt;&lt;br /&gt;|       &lt;span style="color: rgb(255, 204, 153);"&gt;(DEFMACRO FOO (&amp;amp;REST SLOTS)&lt;/span&gt;&lt;br /&gt;|         &lt;span style="color: rgb(255, 204, 153);"&gt;""&lt;/span&gt;&lt;br /&gt;|         &lt;span style="color: rgb(255, 204, 153);"&gt;(CALL-DEFSTRUCT-CONSTRUCTOR 'DEFTEMPLATE/FOO SLOTS)))&lt;/span&gt;&lt;br /&gt;|; No value&lt;br /&gt;|&lt;span style="color: rgb(153, 153, 255);"&gt;MPS&gt;&lt;/span&gt;&lt;/pre&gt;Most of the code is spent checking that the template follows the syntax described in the BNF. The expansion itself is rather simple, almost trivial (it is the last &lt;span style=";font-family:courier new;font-size:85%;"  &gt;progn&lt;/span&gt; below).&lt;pre&gt;|(defmacro deftemplate (deftemplate-name &amp;amp;body body)&lt;br /&gt;|  "&lt;br /&gt;|  The deftemplate construct is used to create a template which can then&lt;br /&gt;|  be used by non-ordered facts to access fields of the fact by name.&lt;br /&gt;|&lt;br /&gt;|  Examples:&lt;br /&gt;|  (deftemplate object&lt;br /&gt;|    (slot id (default-dynamic (gensym)))&lt;br /&gt;|    (slot name (default ?NONE))           ; Makes name a required field&lt;br /&gt;|    (slot age))&lt;br /&gt;|  "&lt;br /&gt;|&lt;br /&gt;|  (macrolet ((signal-deftemplate-error (msg &amp;amp;rest args)&lt;br /&gt;|               `(error (concatenate 'string "~&amp;amp;The deftemplate ~A contains at least one invalid slot-definition: ~S." ,msg)&lt;br /&gt;|                       ,@args)))&lt;br /&gt;|    (let ((comment "")&lt;br /&gt;|          (defstruct-name (intern (concatenate 'string "DEFTEMPLATE/" (string deftemplate-name))))&lt;br /&gt;|          (defstruct-slots '()))&lt;br /&gt;|&lt;br /&gt;|      ;; Extract the documentation string&lt;br /&gt;|      (when (stringp (car body))&lt;br /&gt;|        (setf comment (car body))&lt;br /&gt;|        (setf body (cdr body)))&lt;br /&gt;|&lt;br /&gt;|      ;; Check syntax and extract slot-specifiers&lt;br /&gt;|      (dolist (slot body)&lt;br /&gt;|        (let* ((slot-name (cadr slot))&lt;br /&gt;|               (curr-defstruct-slot `(,slot-name nil)))&lt;br /&gt;|          (unless (eq (car slot) 'slot)&lt;br /&gt;|            (signal-deftemplate-error "~&amp;amp;Expected SLOT instead of ~A."&lt;br /&gt;|                                      deftemplate-name slot (car slot)))&lt;br /&gt;|&lt;br /&gt;|          (when (&gt; (length slot) 2)&lt;br /&gt;|            (dolist (default-attribute (cddr slot))&lt;br /&gt;|              (unless (consp default-attribute)&lt;br /&gt;|                (signal-deftemplate-error "~&amp;amp;Expected (default ?NONE|expression) or (default-dynamic expression) instead of ~A."&lt;br /&gt;|                                          deftemplate-name slot default-attribute))&lt;br /&gt;|              (unless (or (eq (car default-attribute) 'default)&lt;br /&gt;|                          (eq (car default-attribute) 'default-dynamic))&lt;br /&gt;|                (signal-deftemplate-error "~&amp;amp;Expected DEFAULT or DEFAULT-DYNAMIC instead of: ~A."&lt;br /&gt;|                                          deftemplate-name slot (car default-attribute)))&lt;br /&gt;|              (if (eq (car default-attribute) 'default)&lt;br /&gt;|                  (if (eq (cadr default-attribute) '?NONE)&lt;br /&gt;|                      (setf curr-defstruct-slot `(,slot-name (required ',slot-name ',deftemplate-name)))&lt;br /&gt;|                      (setf curr-defstruct-slot `(,slot-name ',(eval (cadr default-attribute)))))&lt;br /&gt;|                  (setf curr-defstruct-slot `(,slot-name ,(cadr default-attribute))))))&lt;br /&gt;|&lt;br /&gt;|          (setf defstruct-slots (append defstruct-slots (list curr-defstruct-slot)))))&lt;br /&gt;|&lt;br /&gt;|      `(progn&lt;br /&gt;|         (defstruct ,defstruct-name&lt;br /&gt;|           ,comment&lt;br /&gt;|           ,@defstruct-slots)&lt;br /&gt;|&lt;br /&gt;|         (defmacro ,deftemplate-name (&amp;amp;rest slots)&lt;br /&gt;|           ,comment&lt;br /&gt;|           (call-defstruct-constructor ',defstruct-name slots))))))&lt;/pre&gt;and here are the functions used as helpers:&lt;pre&gt;|(defun required (slot-name deftemplate-name)&lt;br /&gt;|  (error "~&amp;amp;The slot: ~A in deftemplate: ~A requires an explicit value." slot-name deftemplate-name))&lt;br /&gt;|&lt;br /&gt;|(defun as-keyword (sym)&lt;br /&gt;|  (intern (string-upcase sym) :keyword))&lt;br /&gt;|&lt;br /&gt;|(defun call-defstruct-constructor (defstruct-name &amp;amp;rest slots)&lt;br /&gt;|  (let ((constructor (intern (concatenate 'string "MAKE-" (string defstruct-name)))))&lt;br /&gt;|    (apply constructor (mapcan #'(lambda (slot)&lt;br /&gt;|                                   `(,(as-keyword (car slot)) ,(cadr slot)))&lt;br /&gt;|                               (car slots)))))&lt;/pre&gt;I should probably try to write a macro to abstract away all those &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;(intern (concatenate 'string ...))&lt;/span&gt;&lt;/span&gt; calls but otherwise, that's about it for &lt;span style=";font-family:courier new;font-size:85%;"  &gt;deftemplate&lt;/span&gt;. Next up is getting all of the &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;defrule&lt;/span&gt;&lt;/span&gt; construct working (which feels like a much tougher task).&lt;br /&gt;&lt;br /&gt;[1] I hate that I still haven't found a good way of sharing code using Blogger. Tips and pointers are very welcome!&lt;br /&gt;&lt;br /&gt;[2] The &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;default&lt;/span&gt;&lt;/span&gt; and &lt;span style=";font-family:courier new;font-size:85%;"  &gt;default-dynamic&lt;/span&gt; attributes in CLIPS take a variable number of expressions (at least according to the BNF found in the CLIPS Basic Programming Guide, Appendix H). I assume it assigns the value of the last as the default but I haven't tried. Anyway, if you want to evaluate several expressions in that place you're going to have to wrap it explicitly in a &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;progn&lt;/span&gt;&lt;/span&gt; (effectively making it one expression).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-7543230109130035689?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/7543230109130035689/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=7543230109130035689' title='2 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7543230109130035689'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7543230109130035689'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/08/handling-deftemplates-in-mps.html' title='Handling deftemplates in MPS'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-4930314340688686028</id><published>2008-08-08T15:00:00.000+02:00</published><updated>2008-08-08T17:07:47.428+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Family'/><title type='text'>Elvis R.I.P.</title><content type='html'>Elvis, September 1, 2001 - August 8, 2008. He will be missed.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_3hqf70Lx0Mk/SJxgpqOMu8I/AAAAAAAAAEk/QGzjeknPPbA/s1600-h/Bild+025.jpg"&gt;&lt;img style="cursor: pointer;" src="http://1.bp.blogspot.com/_3hqf70Lx0Mk/SJxgpqOMu8I/AAAAAAAAAEk/QGzjeknPPbA/s320/Bild+025.jpg" alt="" id="BLOGGER_PHOTO_ID_5232163135922420674" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-4930314340688686028?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/4930314340688686028'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/4930314340688686028'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/08/elvis-rip.html' title='Elvis R.I.P.'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_3hqf70Lx0Mk/SJxgpqOMu8I/AAAAAAAAAEk/QGzjeknPPbA/s72-c/Bild+025.jpg' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-1692050664719408238</id><published>2008-08-08T09:10:00.008+02:00</published><updated>2008-09-04T21:05:20.283+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AI'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Go'/><title type='text'>MoGo beats Myungwan Kim (8P) at US Go Congress</title><content type='html'>From the AGA newsletter (via the &lt;a href="http://computer-go.org/pipermail/computer-go/2008-August/015623.html"&gt;ComputerGo mailing list&lt;/a&gt;):&lt;br /&gt;&lt;blockquote&gt; COMPUTER BEATS PRO AT U.S. GO CONGRESS:&lt;br /&gt;In a historic achievement, the &lt;a href="http://www.lri.fr/%7Egelly/MoGo.htm"&gt;MoGo computer program&lt;/a&gt; defeated Myungwan Kim 8P (l) Thursday afternoon by 1.5 points in a &lt;a href="http://files.gokgs.com/games/2008/8/7/MyungWan-MoGoTiTan-4.sgf"&gt;9-stone game&lt;/a&gt; billed as “Humanity’s Last Stand?” “It played really well,” said Kim, who estimated MoGo’s current strength at “two or maybe three dan,” though he noted that the program – which used 800 processors, at 4.7 Ghz, 15 Teraflops on a borrowed European supercomputer – “made some 5-dan moves,” like those in the lower right-hand corner, where Moyogo took advantage of a mistake by Kim to get an early lead. “I can’t tell you how amazing this is,” David Doshay -- the SlugGo programmer who suggested the match -- told the E-Journal after the game.&lt;br /&gt;&lt;br /&gt;“I’m shocked at the result. I really didn’t expect the computer to win in a one-hour game.” Kim easily won two blitz games with 9 stones and 11 stones and minutes and lost one with 12 stones and 15 minutes by 3.5 points. The games were played live at the U.S. Go Congress, with over 500 watching online on &lt;a href="http://www.gokgs.com/"&gt;KGS&lt;/a&gt;. “I think there’s no chance on nine stones,” Kim told the EJ after the game. “It would even be difficult with eight stones. MoGo played really well; after getting a lead, every time I played aggressively, it just played safely, even when it meant sacrificing some stones. It didn’t try to maximize the win and just played the most sure way to win. It’s like a machine.”&lt;br /&gt;&lt;br /&gt;The game generated a lot of interest and discussion about the game’s tactics and&lt;br /&gt;philosophical implications. “Congratulations on making history today,” game organizer Peter Drake told both Kim and Olivier Teytaud, one of MoGo’s programmers, who participated in a brief online chat after the game. At a rare loss for words in a brief interview with the EJ after the game, Doshay wondered “How much time do we have left? We’ve improved nine stones in just a year and I suspect the next nine will fall quickly now.”&lt;br /&gt;&lt;br /&gt;- reported by Chris Garlock&lt;/blockquote&gt;Amazing! Though, the last quote (by David Doshay) should probably be taken with a grain of salt.&lt;br /&gt;&lt;br /&gt;[Update 2008-08-09] &lt;a href="http://computer-go.org/pipermail/computer-go/2008-August/015721.html"&gt;Apparently, David was misquoted&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-1692050664719408238?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/1692050664719408238/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=1692050664719408238' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/1692050664719408238'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/1692050664719408238'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/08/mogo-beats-myungwan-kim-8p-at-us-go.html' title='MoGo beats Myungwan Kim (8P) at US Go Congress'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-3017850062792750754</id><published>2008-08-02T18:58:00.004+02:00</published><updated>2008-08-10T20:34:32.200+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Rule Engine'/><category scheme='http://www.blogger.com/atom/ns#' term='MPS'/><category scheme='http://www.blogger.com/atom/ns#' term='Lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><title type='text'>A Minimal Production System</title><content type='html'>I've been experimenting a bit with bits and pieces of a production system in Common Lisp for a few days now. It all started as a fun exercise in &lt;a href="http://en.wikipedia.org/wiki/Macro_%28computer_science%29#Lisp_macros"&gt;Common Lisp macrology&lt;/a&gt; (the idea was to convert CLIPS syntax into executable Common Lisp code) but since things have fallen into place so neatly. I thought maybe it would be worth implementing parts of CLIPS syntax and functionality. So now, the only question is: which parts to implement?&lt;br /&gt;&lt;br /&gt;I'm not going to do anything about data types and built-in functions. I'll add support for &lt;span style="font-size:85%;"&gt;&lt;span style="font-family: courier new;"&gt;?&lt;/span&gt;&lt;/span&gt;&lt;span style="font-style: italic; font-family: courier new;font-size:85%;" &gt;variables&lt;/span&gt; but that's about it (no multifields and no globals) and as far as constructs go I'll manage with &lt;span style="font-family: courier new;font-size:85%;" &gt;defrule&lt;/span&gt; and &lt;span style="font-family: courier new;font-size:85%;" &gt;deftemplate&lt;/span&gt; (no implied/ordered facts). If it doesn't turn out to be too difficult I'll try to include connected constraints in &lt;span style="font-family: courier new;font-size:85%;" &gt;defrule&lt;/span&gt; but I won't bother with slot constraints in &lt;span style="font-family: courier new;font-size:85%;" &gt;deftemplates&lt;/span&gt; (types and such). I'm hoping to include all CEs but it depends on how hairy it gets.&lt;br /&gt;&lt;br /&gt;So. What have I missed? Is there something unnecessary on the list?&lt;br /&gt;&lt;br /&gt;[Update 2008-08-03]: Hm. I guess I forgot about a bunch of engine commands. So here goes. &lt;span style="font-family: courier new;font-size:85%;" &gt;Assert&lt;/span&gt; and &lt;span style="font-size:85%;"&gt;&lt;span style="font-family: courier new;"&gt;retract&lt;/span&gt;&lt;/span&gt; seem stupid to leave out, as does &lt;span style="font-family: courier new;font-size:85%;" &gt;run&lt;/span&gt;. I'll probably be annoyed not to have &lt;span style="font-family: courier new;font-size:85%;" &gt;facts&lt;/span&gt; so I guess that's in as well. &lt;span style="font-family: courier new;font-size:85%;" &gt;Load&lt;/span&gt;, &lt;span style="font-family: courier new;font-size:85%;" &gt;clear&lt;/span&gt;, &lt;span style="font-family: courier new;font-size:85%;" &gt;reset&lt;/span&gt; and &lt;span style="font-family: courier new;font-size:85%;" &gt;agenda&lt;/span&gt; feel necessary but I think I'll only work with one conflict resolution strategy (depth) though.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-3017850062792750754?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/3017850062792750754/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=3017850062792750754' title='2 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/3017850062792750754'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/3017850062792750754'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/08/minimal-production-system.html' title='A Minimal Production System'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-8909897404333360813</id><published>2008-07-26T11:39:00.001+02:00</published><updated>2008-07-26T11:39:00.592+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Games'/><category scheme='http://www.blogger.com/atom/ns#' term='Mathematics'/><title type='text'>1, 2, 4, 8, 16, ...</title><content type='html'>How far can you calculate the expansion of the series 1, 2, 4, 8, 16, 32, 64, ... in your head? And, no. You can't use pen and paper or anything.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-8909897404333360813?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/8909897404333360813/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=8909897404333360813' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8909897404333360813'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8909897404333360813'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/07/1-2-4-8-16.html' title='1, 2, 4, 8, 16, ...'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-8068044274736303348</id><published>2008-07-23T07:43:00.001+02:00</published><updated>2008-07-23T07:48:32.102+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AI'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Go'/><title type='text'>Human-Computer  19x19 Go Showdown</title><content type='html'>From the &lt;a href="http://computer-go.org/pipermail/computer-go/2008-July/015369.html"&gt;Computer-Go mailing list&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;On Thursday, August 7, at 1:00 PM (Pacific time), Kim MyungWan 8p will take on &lt;a href="http://www.lri.fr/%7Egelly/MoGo.htm"&gt;MoGo&lt;/a&gt;, the world’s strongest computer Go program. MoGo will connect remotely from France, where it will be running on a supercomputer boasting over 3,000 processor cores. The game will be broadcast on &lt;a href="http://www.gokgs.com/"&gt;KGS&lt;/a&gt;.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Sweet! I guess I'll get an answer &lt;a href="http://commentsarelies.blogspot.com/2008/07/what-would-you-do-with-thousand-cores.html"&gt;to some questions&lt;/a&gt; quite a bit sooner than I expected.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-8068044274736303348?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/8068044274736303348/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=8068044274736303348' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8068044274736303348'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8068044274736303348'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/07/human-computer-19x19-go-showdown.html' title='Human-Computer  19x19 Go Showdown'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-3128084939087143763</id><published>2008-07-22T09:40:00.000+02:00</published><updated>2008-07-22T09:41:13.978+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Expert system'/><category scheme='http://www.blogger.com/atom/ns#' term='AI'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Video'/><title type='text'>AI -TV from 1984</title><content type='html'>José of &lt;a href="http://jaortega.wordpress.com/"&gt;Programming Musings&lt;/a&gt; &lt;a href="http://jaortega.wordpress.com/2008/07/21/vintage-clos/"&gt;found a video with Daniel Bobrow explaining CLOS&lt;/a&gt; from 1987. Being somewhat of a history-geek, I really love this type of thing and I've managed to collect a few interesting links myself.&lt;br /&gt;&lt;br /&gt;One that I can particularly recommend is &lt;a href="http://video.google.com/videoplay?docid=-71655435732711746&amp;amp;q=Computer+Chronicles&amp;amp;total=55&amp;amp;start=0&amp;amp;num=10&amp;amp;so=1&amp;amp;type=search&amp;amp;plindex=2"&gt;Computer Chronicles: Artifical Intelligence (1984)&lt;/a&gt; which is a twenty minute TV show featuring, among others, &lt;a href="http://www-formal.stanford.edu/jmc/"&gt;John McCarthy&lt;/a&gt; and discusses &lt;a href="http://en.wikipedia.org/wiki/AI"&gt;AI in general&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/Expert_system"&gt;Expert Systems&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Lisp_%28programming_language%29"&gt;Lisp&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;embed id="VideoPlayback" style="width: 400px; height: 326px;" allowfullscreen="true" src="http://video.google.com/googleplayer.swf?docid=-71655435732711746&amp;amp;hl=en&amp;amp;fs=true" type="application/x-shockwave-flash"&gt;&lt;/embed&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-3128084939087143763?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/3128084939087143763/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=3128084939087143763' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/3128084939087143763'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/3128084939087143763'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/07/ai-tv-from-1984.html' title='AI -TV from 1984'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-7613343862694682310</id><published>2008-07-18T12:37:00.004+02:00</published><updated>2008-07-20T20:45:49.145+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='Kōan'/><category scheme='http://www.blogger.com/atom/ns#' term='Puzzles'/><title type='text'>Sling Blade Runner IV</title><content type='html'>I've been doing some more work on my &lt;a href="http://en.wikipedia.org/wiki/Koan"&gt;kōan&lt;/a&gt;, &lt;a href="http://commentsarelies.blogspot.com/search?q=sling+blade+runner"&gt;Sling Blade Runner&lt;/a&gt;. I've given up on the beam-stack search approach. I doubt it will improve results much. If any at all.&lt;br /&gt;&lt;br /&gt;Instead I have spent some time on improving the &lt;span style=";font-family:courier new;font-size:85%;"  &gt;extend&lt;/span&gt; method so that it can find more extensions. And it is becoming quite efficient in exploring the neighbourhood of any given pair of titles. Previously it was only looking for extensions between two adjacent titles in the chain but now it searches for extensions between end-points in a whole sequence of titles instead. Consider the chain [A,B,C,D,...]. Now, if there's an extension between A and C, other than, and that is longer than [B], the chain is extended with it.&lt;br /&gt;&lt;br /&gt;This, simple, tweak makes the search slower (of course) but it also improves the lengths of almost all chains that are explored. It rarely ends up with a chain that is shorter than 235 titles and most end up longer than 250 (&lt;a href="http://commentsarelies.blogspot.com/2008/07/sling-blade-runner-iii.html"&gt;246 was the previous best&lt;/a&gt;) titles.&lt;br /&gt;&lt;br /&gt;The longest chain found is now 265 titles:&lt;pre&gt;1 THE GOSPEL&lt;br /&gt;2 THE GOSPEL OF JOHN&lt;br /&gt;3 JOHN Q&lt;br /&gt;4 Q AND A&lt;br /&gt;5 A RIVER RUNS THROUGH IT&lt;br /&gt;6 IT HAPPENED AT THE WORLDS FAIR&lt;br /&gt;7 FAIR GAME&lt;br /&gt;8 GAME OF DEATH&lt;br /&gt;9 DEATH BECOMES HER&lt;br /&gt;10 HER MAJESTY MRS BROWN&lt;br /&gt;11 BROWN SUGAR&lt;br /&gt;12 SUGAR AND SPICE&lt;br /&gt;13 SPICE WORLD&lt;br /&gt;14 WORLD TRADE CENTER&lt;br /&gt;15 CENTER STAGE&lt;br /&gt;16 STAGE FRIGHT&lt;br /&gt;17 FRIGHT NIGHT&lt;br /&gt;18 NIGHT OF THE LIVING DEAD&lt;br /&gt;19 DEAD BANG&lt;br /&gt;20 BANG BANG YOURE DEAD&lt;br /&gt;21 DEAD MAN WALKING&lt;br /&gt;22 WALKING AND TALKING&lt;br /&gt;23 TALKING ABOUT SEX&lt;br /&gt;24 SEX AND THE OTHER MAN&lt;br /&gt;25 MAN OF THE HOUSE&lt;br /&gt;26 HOUSE OF DRACULA&lt;br /&gt;27 DRACULA DEAD AND LOVING IT&lt;br /&gt;28 IT HAPPENED ONE NIGHT&lt;br /&gt;29 ONE NIGHT STAND&lt;br /&gt;30 STAND IN&lt;br /&gt;31 IN GODS HANDS&lt;br /&gt;32 HANDS ON A HARD BODY&lt;br /&gt;33 BODY AND SOUL&lt;br /&gt;34 SOUL FOOD&lt;br /&gt;35 FOOD OF LOVE&lt;br /&gt;36 LOVE IS THE DEVIL&lt;br /&gt;37 THE DEVIL RIDES OUT&lt;br /&gt;38 OUT COLD&lt;br /&gt;39 COLD FEVER&lt;br /&gt;40 FEVER PITCH&lt;br /&gt;41 PITCH BLACK&lt;br /&gt;42 BLACK LIKE ME&lt;br /&gt;43 ME WITHOUT YOU&lt;br /&gt;44 YOU LIGHT UP MY LIFE&lt;br /&gt;45 MY LIFE SO FAR&lt;br /&gt;46 FAR FROM HOME THE ADVENTURES OF YELLOW DOG&lt;br /&gt;47 DOG RUN&lt;br /&gt;48 RUN SILENT RUN DEEP&lt;br /&gt;49 DEEP BLUE&lt;br /&gt;50 DEEP BLUE SEA&lt;br /&gt;51 SEA OF LOVE&lt;br /&gt;52 LOVE WALKED IN&lt;br /&gt;53 IN OLD CALIFORNIA&lt;br /&gt;54 CALIFORNIA SPLIT&lt;br /&gt;55 SPLIT SECOND&lt;br /&gt;56 SECOND BEST&lt;br /&gt;57 BEST MEN&lt;br /&gt;58 MEN WITH GUNS&lt;br /&gt;59 GUNS OF THE MAGNIFICENT SEVEN&lt;br /&gt;60 THE MAGNIFICENT SEVEN&lt;br /&gt;61 SEVEN YEARS IN TIBET&lt;br /&gt;62 TIBET CRY OF THE SNOW LION&lt;br /&gt;63 LION OF THE DESERT&lt;br /&gt;64 DESERT HEARTS&lt;br /&gt;65 HEARTS OF DARKNESS A FILMMAKERS APOCALYPSE&lt;br /&gt;66 APOCALYPSE NOW&lt;br /&gt;67 NOW YOU SEE HIM NOW YOU DONT&lt;br /&gt;68 DONT BOTHER TO KNOCK&lt;br /&gt;69 KNOCK OFF&lt;br /&gt;70 OFF THE BLACK&lt;br /&gt;71 THE BLACK ANGEL&lt;br /&gt;72 ANGEL EYES&lt;br /&gt;73 EYES OF AN ANGEL&lt;br /&gt;74 ANGEL BABY&lt;br /&gt;75 BABY SECRET OF THE LOST LEGEND&lt;br /&gt;76 LEGEND OF THE LOST&lt;br /&gt;77 THE LOST BOYS&lt;br /&gt;78 BOYS LIFE&lt;br /&gt;79 LIFE OR SOMETHING LIKE IT&lt;br /&gt;80 IT TAKES TWO&lt;br /&gt;81 TWO MEN WENT TO WAR&lt;br /&gt;82 WAR OF THE WORLDS&lt;br /&gt;83 THE WORLDS FASTEST INDIAN&lt;br /&gt;84 INDIAN SUMMER&lt;br /&gt;85 SUMMER CATCH&lt;br /&gt;86 CATCH ME IF YOU CAN&lt;br /&gt;87 YOU CAN COUNT ON ME&lt;br /&gt;88 ME MYSELF I&lt;br /&gt;89 I WANT TO LIVE&lt;br /&gt;90 LIVE FOREVER&lt;br /&gt;91 FOREVER YOUNG&lt;br /&gt;92 YOUNG SHERLOCK HOLMES&lt;br /&gt;93 SHERLOCK HOLMES IN WASHINGTON&lt;br /&gt;94 WASHINGTON SQUARE&lt;br /&gt;95 SQUARE DANCE&lt;br /&gt;96 DANCE WITH A STRANGER&lt;br /&gt;97 STRANGER IN THE HOUSE&lt;br /&gt;98 THE HOUSE OF THE DEAD&lt;br /&gt;99 DEAD OF NIGHT&lt;br /&gt;100 NIGHT AND THE CITY&lt;br /&gt;101 THE CITY&lt;br /&gt;102 CITY OF ANGELS&lt;br /&gt;103 ANGELS WITH DIRTY FACES&lt;br /&gt;104 FACES OF DEATH&lt;br /&gt;105 DEATH WISH&lt;br /&gt;106 WISH UPON A STAR&lt;br /&gt;107 A STAR IS BORN&lt;br /&gt;108 BORN TO BE WILD&lt;br /&gt;109 WILD THINGS&lt;br /&gt;110 THINGS TO COME&lt;br /&gt;111 COME AND GET IT&lt;br /&gt;112 IT RUNS IN THE FAMILY&lt;br /&gt;113 THE FAMILY MAN&lt;br /&gt;114 MAN ON FIRE&lt;br /&gt;115 FIRE ON THE MOUNTAIN&lt;br /&gt;116 THE MOUNTAIN MEN&lt;br /&gt;117 MEN IN BLACK&lt;br /&gt;118 BLACK HAWK DOWN&lt;br /&gt;119 DOWN WITH LOVE&lt;br /&gt;120 LOVE AND DEATH&lt;br /&gt;121 DEATH WISH V THE FACE OF DEATH&lt;br /&gt;122 DEATH SHIP&lt;br /&gt;123 SHIP OF FOOLS&lt;br /&gt;124 FOOLS RUSH IN&lt;br /&gt;125 IN THE WINTER DARK&lt;br /&gt;126 DARK STAR&lt;br /&gt;127 STAR TREK IV THE VOYAGE HOME&lt;br /&gt;128 HOME ALONE&lt;br /&gt;129 ALONE IN THE DARK&lt;br /&gt;130 DARK BLUE&lt;br /&gt;131 BLUE CAR&lt;br /&gt;132 CAR 54 WHERE ARE YOU&lt;br /&gt;133 YOU CANT TAKE IT WITH YOU&lt;br /&gt;134 YOU ONLY LIVE ONCE&lt;br /&gt;135 ONCE IN THE LIFE&lt;br /&gt;136 LIFE AS A HOUSE&lt;br /&gt;137 HOUSE PARTY&lt;br /&gt;138 HOUSE PARTY 3&lt;br /&gt;139 3 NINJAS&lt;br /&gt;140 3 NINJAS KNUCKLE UP&lt;br /&gt;141 UP CLOSE AND PERSONAL&lt;br /&gt;142 PERSONAL BEST&lt;br /&gt;143 BEST OF THE BEST&lt;br /&gt;144 BEST OF THE BEST 3 NO TURNING BACK&lt;br /&gt;145 NO TURNING BACK&lt;br /&gt;146 BACK TO SCHOOL&lt;br /&gt;147 SCHOOL OF ROCK&lt;br /&gt;148 ROCK STAR&lt;br /&gt;149 STAR TREK THE MOTION PICTURE&lt;br /&gt;150 PICTURE BRIDE&lt;br /&gt;151 BRIDE OF THE MONSTER&lt;br /&gt;152 MONSTER HOUSE&lt;br /&gt;153 HOUSE OF FRANKENSTEIN&lt;br /&gt;154 FRANKENSTEIN AND THE MONSTER FROM HELL&lt;br /&gt;155 FROM HELL&lt;br /&gt;156 HELL UP IN HARLEM&lt;br /&gt;157 HARLEM RIVER DRIVE&lt;br /&gt;158 DRIVE ME CRAZY&lt;br /&gt;159 CRAZY AS HELL&lt;br /&gt;160 HELL NIGHT&lt;br /&gt;161 NIGHT FALLS ON MANHATTAN&lt;br /&gt;162 MANHATTAN MURDER MYSTERY&lt;br /&gt;163 MYSTERY ALASKA&lt;br /&gt;164 ALASKA SPIRIT OF THE WILD&lt;br /&gt;165 THE WILD&lt;br /&gt;166 THE WILD ONE&lt;br /&gt;167 ONE NIGHT WITH THE KING&lt;br /&gt;168 THE KING AND I&lt;br /&gt;169 I SPY&lt;br /&gt;170 SPY HARD&lt;br /&gt;171 HARD EIGHT&lt;br /&gt;172 EIGHT MEN OUT&lt;br /&gt;173 OUT OF THE BLUE&lt;br /&gt;174 BLUE SKY&lt;br /&gt;175 SKY HIGH&lt;br /&gt;176 HIGH CRIMES&lt;br /&gt;177 CRIMES OF PASSION&lt;br /&gt;178 PASSION IN THE DESERT&lt;br /&gt;179 DESERT BLUE&lt;br /&gt;180 BLUE STEEL&lt;br /&gt;181 STEEL DAWN&lt;br /&gt;182 DAWN OF THE DEAD&lt;br /&gt;183 THE DEAD&lt;br /&gt;184 DEAD MAN ON CAMPUS&lt;br /&gt;185 CAMPUS MAN&lt;br /&gt;186 MAN TROUBLE&lt;br /&gt;187 TROUBLE EVERY DAY&lt;br /&gt;188 DAY OF THE WOMAN&lt;br /&gt;189 THE WOMAN IN RED&lt;br /&gt;190 RED EYE&lt;br /&gt;191 EYE FOR AN EYE&lt;br /&gt;192 EYE OF GOD&lt;br /&gt;193 GOD TOLD ME TO&lt;br /&gt;194 TO DIE FOR&lt;br /&gt;195 FOR THE BOYS&lt;br /&gt;196 THE BOYS&lt;br /&gt;197 BOYS AND GIRLS&lt;br /&gt;198 GIRLS WILL BE GIRLS&lt;br /&gt;199 GIRLS GIRLS GIRLS&lt;br /&gt;200 GIRLS OF SUMMER&lt;br /&gt;201 SUMMER LOVERS&lt;br /&gt;202 LOVERS AND OTHER STRANGERS&lt;br /&gt;203 STRANGERS WHEN WE MEET&lt;br /&gt;204 MEET JOE BLACK&lt;br /&gt;205 BLACK AND WHITE&lt;br /&gt;206 WHITE HUNTER BLACK HEART&lt;br /&gt;207 HEART CONDITION&lt;br /&gt;208 CONDITION RED&lt;br /&gt;209 RED RIVER&lt;br /&gt;210 RIVER OF NO RETURN&lt;br /&gt;211 RETURN OF THE FLY&lt;br /&gt;212 FLY AWAY HOME&lt;br /&gt;213 HOME ALONE 2 LOST IN NEW YORK&lt;br /&gt;214 NEW YORK NEW YORK&lt;br /&gt;215 NEW YORK COP&lt;br /&gt;216 COP LAND&lt;br /&gt;217 LAND OF THE DEAD&lt;br /&gt;218 DEAD END&lt;br /&gt;219 END OF DAYS&lt;br /&gt;220 DAYS OF HEAVEN&lt;br /&gt;221 HEAVEN CAN WAIT&lt;br /&gt;222 WAIT UNTIL DARK&lt;br /&gt;223 DARK CITY&lt;br /&gt;224 CITY OF JOY&lt;br /&gt;225 JOY RIDE&lt;br /&gt;226 RIDE THE HIGH COUNTRY&lt;br /&gt;227 COUNTRY LIFE&lt;br /&gt;228 LIFE WITH FATHER&lt;br /&gt;229 FATHER OF THE BRIDE&lt;br /&gt;230 BRIDE OF THE WIND&lt;br /&gt;231 THE WIND AND THE LION&lt;br /&gt;232 THE LION KING&lt;br /&gt;233 KING OF THE JUNGLE&lt;br /&gt;234 THE JUNGLE BOOK&lt;br /&gt;235 JUNGLE BOOK&lt;br /&gt;236 BOOK OF LOVE&lt;br /&gt;237 LOVE LIFE&lt;br /&gt;238 LIFE IS BEAUTIFUL&lt;br /&gt;239 BEAUTIFUL PEOPLE&lt;br /&gt;240 PEOPLE I KNOW&lt;br /&gt;241 I KNOW WHERE IM GOING&lt;br /&gt;242 IM GOING HOME&lt;br /&gt;243 HOME ALONE 3&lt;br /&gt;244 3 NINJAS KICK BACK&lt;br /&gt;245 BACK TO THE BEACH&lt;br /&gt;246 BEACH PARTY&lt;br /&gt;247 PARTY MONSTER&lt;br /&gt;248 MONSTER IN A BOX&lt;br /&gt;249 BOX OF MOON LIGHT&lt;br /&gt;250 LIGHT OF DAY&lt;br /&gt;251 DAY FOR NIGHT&lt;br /&gt;252 NIGHT MOTHER&lt;br /&gt;253 MOTHER NIGHT&lt;br /&gt;254 NIGHT ON EARTH&lt;br /&gt;255 EARTH GIRLS ARE EASY&lt;br /&gt;256 EASY MONEY&lt;br /&gt;257 MONEY FOR NOTHING&lt;br /&gt;258 NOTHING BUT TROUBLE&lt;br /&gt;259 TROUBLE IN PARADISE&lt;br /&gt;260 PARADISE ROAD&lt;br /&gt;261 ROAD HOUSE&lt;br /&gt;262 HOUSE PARTY 2&lt;br /&gt;263 2 DAYS IN THE VALLEY&lt;br /&gt;264 VALLEY GIRL&lt;br /&gt;265 GIRL WITH A PEARL EARRING&lt;/pre&gt;The results vary a bit depending on search depth (not that surprising really) so there may still be some possible improvements by just modifying parameters.&lt;br /&gt;&lt;br /&gt;[Update 2008-07-20] The longest chain found is now 268 titles long.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-7613343862694682310?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/7613343862694682310/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=7613343862694682310' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7613343862694682310'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7613343862694682310'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/07/sling-blade-runner-iv.html' title='Sling Blade Runner IV'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-7745046498482104673</id><published>2008-07-14T09:00:00.004+02:00</published><updated>2008-07-14T18:08:18.448+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='pyClips'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><title type='text'>Using printout and readline in PyCLIPS applications</title><content type='html'>I am trying to help a &lt;a href="http://clipsrules.sourceforge.net/"&gt;CLIPS&lt;/a&gt; user compile his application into a Windows executable (.exe) by following the description in &lt;a href="http://commentsarelies.blogspot.com/2008/06/creating-exe-for-clips-application.html"&gt;one of my previous blog posts&lt;/a&gt;. But his application relies on the built in functions &lt;span style=";font-family:courier new;font-size:85%;"  &gt;read&lt;/span&gt; and &lt;span style=";font-family:courier new;font-size:85%;"  &gt;printout&lt;/span&gt;. And my blog post says nothing about that type of situation since all of the times I've done this before have been with applications that have GUIs and I've never had to bother with command line interaction.&lt;br /&gt;&lt;br /&gt;The problem is, more specifically, that PyCLIPS consumes all calls to &lt;span class="Apple-style-span" style=";font-family:'courier new';font-size:85%;"  &gt;&lt;span class="Apple-style-span"&gt;printout&lt;/span&gt;&lt;/span&gt; and &lt;span class="Apple-style-span"  style="font-size:85%;"&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;read&lt;/span&gt;&lt;/span&gt; &lt;span style="font-weight: bold;"&gt;without&lt;/span&gt; printing to screen or requiring any user input. This may seem like a stupid design decision at first, but if you consider the many situations and environments that PyCLIPS can run in. It starts to feel like a rather sensible approach since it's obviously impossible to know what the right thing to do is. If you need user interaction via command line, you can provide Python functions that perform I/O for you.&lt;br /&gt;&lt;br /&gt;So. Just to be clear. PyCLIPS does not constrain the possibilities for interaction with CLIPS in any way. It just doesn't make any assumptions about how it should best be done.&lt;br /&gt;&lt;br /&gt;Ok. Fair enough. But, what to do?&lt;br /&gt;&lt;br /&gt;First off. I don't want to have to maintain different versions of the application just to perform I/O. I need a way to dynamically dispatch I/O to either the built in functions (if it's run in CLIPS Dialog) or to my Python functions (if it's run in PyCLIPS).&lt;br /&gt;&lt;br /&gt;Here is a simple application that uses &lt;span style=";font-family:courier new;font-size:85%;"  &gt;printout&lt;/span&gt; and &lt;span style=";font-family:courier new;font-size:85%;"  &gt;read&lt;/span&gt;:&lt;pre&gt;|(deffacts start&lt;br /&gt;|  (sum 0))&lt;br /&gt;|&lt;br /&gt;|(defrule calc-sum&lt;br /&gt;|  ?sum &lt;- (sum ?s)&lt;br /&gt;|  =&gt;&lt;br /&gt;|  (retract ?sum)&lt;br /&gt;|  (printout t "Enter a number or Q to quit: ")&lt;br /&gt;|  (bind ?input (read))&lt;br /&gt;|  (if (numberp ?input)&lt;br /&gt;|   then (bind ?s (+ ?s ?input))&lt;br /&gt;|        (assert (sum ?s))&lt;br /&gt;|   else (if (or (eq ?input q)&lt;br /&gt;|                (eq ?input Q))&lt;br /&gt;|         then (printout t "Sum = " ?s crlf)&lt;br /&gt;|         else (printout t "Invalid input: " ?input crlf)&lt;br /&gt;|              (assert (sum ?s)))))&lt;/pre&gt;It works like this:&lt;pre&gt;CLIPS&gt; (load "sum.clp")&lt;br /&gt;!!!$*&lt;br /&gt;TRUE&lt;br /&gt;CLIPS&gt; (reset)&lt;br /&gt;CLIPS&gt; (run)&lt;br /&gt;Enter a number or Q to quit: one&lt;br /&gt;Invalid input: one&lt;br /&gt;Enter a number or Q to quit: 9&lt;br /&gt;Enter a number or Q to quit: 8&lt;br /&gt;Enter a number or Q to quit: 5&lt;br /&gt;Enter a number or Q to quit: Q&lt;br /&gt;Sum = 22&lt;/pre&gt;When I first thought about how to make this program run (with as little effort as possible) in PyCLIPS, I didn't think that it would be very difficult. I was actually quite certain I'd be able to &lt;a href="http://en.wikipedia.org/wiki/Monkeypatch"&gt;monkey-patch&lt;/a&gt; PyCLIPS with my I/O functions. CLIPS, however, was far from impressed with my attempt. So, a slap on the wrist later, I settled on (manually) replacing all function calls to &lt;span style=";font-family:courier new;font-size:85%;"  &gt;printout&lt;/span&gt;, &lt;span style=";font-family:courier new;font-size:85%;"  &gt;readline&lt;/span&gt; and &lt;span style=";font-family:courier new;font-size:85%;"  &gt;read&lt;/span&gt; to deffunction wrappers (&lt;span class="Apple-style-span"  style="font-size:85%;"&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;printout1&lt;/span&gt;&lt;/span&gt;, &lt;span style=";font-family:courier new;font-size:85%;"  &gt;readline1&lt;/span&gt; and &lt;span class="Apple-style-span"  style="font-size:85%;"&gt;&lt;span class="Apple-style-span"  style="font-family:'courier new';"&gt;read1&lt;/span&gt;&lt;/span&gt;) instead.&lt;br /&gt;&lt;br /&gt;Here are the CLIPS deffunctions:&lt;br /&gt;&lt;pre&gt;|(deffunction printout1 (?logical-name $?args)&lt;br /&gt;|  (if (member$ python-call (get-function-list))&lt;br /&gt;|   then (funcall python-call pyprintout ?logical-name $?args)&lt;br /&gt;|   else (progn$ (?arg $?args)&lt;br /&gt;|          (printout ?logical-name ?arg))))&lt;br /&gt;|&lt;br /&gt;|(deffunction readline1 ($?logical-name)&lt;br /&gt;|  (if (&gt; (length$ $?logical-name) 0)&lt;br /&gt;|   then (bind ?logical-name (first$ $?logical-name))&lt;br /&gt;|   else (bind ?logical-name t))&lt;br /&gt;|&lt;br /&gt;|  (if (member$ python-call (get-function-list))&lt;br /&gt;|   then (funcall python-call pyreadline ?logical-name)&lt;br /&gt;|   else (readline ?logical-name)))&lt;br /&gt;|&lt;br /&gt;|(deffunction read1 ($?logical-name)&lt;br /&gt;|  (if (&gt; (length$ $?logical-name) 0)&lt;br /&gt;|   then (bind ?logical-name (first$ $?logical-name))&lt;br /&gt;|   else (bind ?logical-name t))&lt;br /&gt;|&lt;br /&gt;|  (if (member$ python-call (get-function-list))&lt;br /&gt;|   then (eval (funcall python-call pyread ?logical-name))&lt;br /&gt;|   else (read ?logical-name)))&lt;/pre&gt;They look quite hairy. I know. But they're generic so it's not something you'd have to write and modify for each application that you want to be able to run in both CLIPS and PyCLIPS. There's also some Python boiler plate. It looks like this:&lt;pre&gt;|import clips&lt;br /&gt;|&lt;br /&gt;|def pyprintout(*args):&lt;br /&gt;|  for arg in args[1]:&lt;br /&gt;|    if arg.cltypename().upper() == "SYMBOL":&lt;br /&gt;|      if arg.upper() == "CRLF":&lt;br /&gt;|        print&lt;br /&gt;|      elif arg.upper() == "TAB":&lt;br /&gt;|        print "\t",&lt;br /&gt;|&lt;span style="color: rgb(255, 0, 0);"&gt;      else:&lt;/span&gt;&lt;br /&gt;|&lt;span style="color: rgb(255, 0, 0);"&gt;        print arg,&lt;/span&gt;&lt;br /&gt;|&lt;br /&gt;|    else:&lt;br /&gt;|      print arg,&lt;br /&gt;|&lt;br /&gt;|def pyreadline(*args):&lt;br /&gt;|  return raw_input()&lt;br /&gt;|&lt;br /&gt;|def pyread(*args):&lt;br /&gt;|  return raw_input()&lt;br /&gt;|&lt;br /&gt;|clips.RegisterPythonFunction(pyprintout)&lt;br /&gt;|clips.RegisterPythonFunction(pyreadline)&lt;br /&gt;|clips.RegisterPythonFunction(pyread)&lt;/pre&gt;Once we've got all of the above in place. We can load, reset and run from within a PyCLIPS script and have the application work directly in the Windows command line.&lt;pre&gt;C:\...&gt;python sum.py&lt;br /&gt;Enter a number or Q to quit:  one&lt;br /&gt;Invalid input:  &lt;span style="color: rgb(255, 0, 0);"&gt;one&lt;/span&gt;&lt;br /&gt;Enter a number or Q to quit:  9&lt;br /&gt;Enter a number or Q to quit:  8&lt;br /&gt;Enter a number or Q to quit:  5&lt;br /&gt;Enter a number or Q to quit:  Q&lt;br /&gt;Sum =  22&lt;br /&gt;&lt;br /&gt;C:\...&gt;&lt;/pre&gt;The files &lt;a href="http://www.pulp.se/clips/pyclips/sum.py"&gt;sum.py&lt;/a&gt; and &lt;a href="http://www.pulp.se/clips/pyclips/sum.clp"&gt;sum.clp&lt;/a&gt; contain the whole application if you're interested. The point of all this is of course to be able to compile the PyCLIPS application into an executable. Step for step instructions on how to do that can be found &lt;a href="http://commentsarelies.blogspot.com/2008/06/creating-exe-for-clips-application.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;[Update 2008-07-14] I just fixed the pyprintout function (in sum.py), so that it also prints symbols. The code I added is marked with &lt;span style="color: rgb(255, 0, 0);"&gt;red&lt;/span&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-7745046498482104673?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/7745046498482104673/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=7745046498482104673' title='1 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7745046498482104673'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7745046498482104673'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/07/using-printout-and-readline-in-pyclips.html' title='Using printout and readline in PyCLIPS applications'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-8069214458860471883</id><published>2008-07-11T08:23:00.000+02:00</published><updated>2008-07-11T08:23:00.420+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AI'/><category scheme='http://www.blogger.com/atom/ns#' term='Lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='CLOS'/><title type='text'>Common Lisp Reasoner</title><content type='html'>I just found the SourceForge project &lt;a href="http://reasoner.sourceforge.net/"&gt;Common Lisp Reasoner&lt;/a&gt;. It's a CLOS extension for AI applications like scheduling and diagnosis. I haven't had time to check it out, but it looks interesting. I wonder if it plays nicely with other CLOS extensions, like the production system &lt;a href="http://lisa.sourceforge.net/"&gt;Lisa&lt;/a&gt; for example.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-8069214458860471883?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/8069214458860471883/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=8069214458860471883' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8069214458860471883'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8069214458860471883'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/07/common-lisp-reasoner.html' title='Common Lisp Reasoner'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-8979433102526672476</id><published>2008-07-10T08:00:00.001+02:00</published><updated>2008-07-10T12:29:39.636+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Games'/><category scheme='http://www.blogger.com/atom/ns#' term='Go'/><title type='text'>What would *you* do with a thousand cores?</title><content type='html'>&lt;a href="http://blogs.intel.com/research/authors#anwar_ghuloum_"&gt;Anwar Ghuloum's&lt;/a&gt; post &lt;a href="http://blogs.intel.com/research/2008/06/unwelcome_advice.php"&gt;Unwelcome advice&lt;/a&gt; has caused a bit of a stir recently. Probably not in the way he wanted though. The comments are full of questions and doubts about whether they (&lt;a href="http://www.intel.com/"&gt;Intel&lt;/a&gt;) can back up their claims of soon-to-come-thousands-of-cores-processors. And, more importantly, if they *can* - programming in itself will be very different from how we do it today so it's meaningless to think about it in terms of how it's done now.&lt;br /&gt;&lt;br /&gt;But, regardless of all that... More than a thousand cores in a single processor. What would *you* do with that? One area that I think looks particularly promising is &lt;a href="http://en.wikipedia.org/wiki/Computer_Go"&gt;Computer Go&lt;/a&gt;. That many cores may well be the tipping point of AI players in Go. Will they finally be able to move up into the (professional) Dan levels?&lt;br /&gt;&lt;br /&gt;In the upcoming &lt;a href="http://www.computer-go.info/egc2008/"&gt;tournament&lt;/a&gt; at the &lt;a href="http://egc2008.eu/en/congress/index.php"&gt;European Go Congress&lt;/a&gt; (in Leksand, Sweden) they're provding hardware with 2 cores. If we ignore practical scaling problems for a minute, what would the effect of an additional 998 cores be? Many of the top players today (like &lt;a href="http://www.lri.fr/~gelly/MoGo.htm"&gt;MoGo&lt;/a&gt; and &lt;a href="http://remi.coulom.free.fr/CrazyStone/"&gt;CrazyStone&lt;/a&gt;) rely on "easily" parallellizable statistical methods (see &lt;a href="http://senseis.xmp.net/?UCT"&gt;UCT&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Monte-Carlo_method"&gt;Monte Carlo methods&lt;/a&gt; for more info). And the more playouts you can perform within a given time the better the resulting play. At least in theory.&lt;br /&gt;&lt;br /&gt;I may well be &lt;a href="http://commentsarelies.blogspot.com/2007/10/cracking-go.html"&gt;underestimating the problem&lt;/a&gt;. I know. But it would be very interesting to see how much of an improvement can be made by just adding more raw power.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-8979433102526672476?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/8979433102526672476/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=8979433102526672476' title='2 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8979433102526672476'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8979433102526672476'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/07/what-would-you-do-with-thousand-cores.html' title='What would *you* do with a thousand cores?'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-7007259147818196391</id><published>2008-07-06T22:22:00.001+02:00</published><updated>2008-07-06T22:22:00.047+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Family'/><title type='text'>My birthday</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_3hqf70Lx0Mk/SHEhqPHQJ1I/AAAAAAAAAEU/I2awBRjBb8o/s1600-h/Birthday_boy.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_3hqf70Lx0Mk/SHEhqPHQJ1I/AAAAAAAAAEU/I2awBRjBb8o/s320/Birthday_boy.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5219990452594288466" /&gt;&lt;/a&gt;&lt;br /&gt;If you're geeky and you know it, clap your hands.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-7007259147818196391?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/7007259147818196391/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=7007259147818196391' title='3 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7007259147818196391'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7007259147818196391'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/07/my-birthday.html' title='My birthday'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp3.blogger.com/_3hqf70Lx0Mk/SHEhqPHQJ1I/AAAAAAAAAEU/I2awBRjBb8o/s72-c/Birthday_boy.jpg' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-1789560196289325862</id><published>2008-07-02T11:35:00.008+02:00</published><updated>2008-07-02T19:53:29.123+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='Kōan'/><category scheme='http://www.blogger.com/atom/ns#' term='Puzzles'/><title type='text'>Sling Blade Runner III</title><content type='html'>Continuing work on my  &lt;a href="http://commentsarelies.blogspot.com/2008/05/sling-blade-runner.html"&gt;Sling Blade Runner&lt;/a&gt; solution has led me to improve the search algorithm a bit further. Instead of looking for good starting-points for a search, it now looks for good mid-points and searches both forwards and backwards. This should, in theory, be a better way to do it and it does improve the average length of chains. But it turns out that it doesn't make *that* big a difference on the longest one (roughly 10 titles). The longest chain found is now 246 titles long.&lt;pre&gt;1 THE PRICE OF MILK&lt;br /&gt;2 MILK AND HONEY&lt;br /&gt;3 HONEY I BLEW UP THE KID&lt;br /&gt;4 THE KID&lt;br /&gt;5 THE KID AND I&lt;br /&gt;6 I WENT DOWN&lt;br /&gt;7 DOWN TO YOU&lt;br /&gt;8 YOU ONLY LIVE ONCE&lt;br /&gt;9 ONCE AROUND&lt;br /&gt;10 AROUND THE BEND&lt;br /&gt;11 BEND OF THE RIVER&lt;br /&gt;12 THE RIVER&lt;br /&gt;13 THE RIVER WILD&lt;br /&gt;14 WILD THINGS&lt;br /&gt;15 THINGS TO COME&lt;br /&gt;16 COME AND GET IT&lt;br /&gt;17 IT TAKES TWO&lt;br /&gt;18 TWO EVIL EYES&lt;br /&gt;19 EYES OF AN ANGEL&lt;br /&gt;20 ANGEL HEART&lt;br /&gt;21 HEART CONDITION&lt;br /&gt;22 CONDITION RED&lt;br /&gt;23 RED RIVER&lt;br /&gt;24 RIVER OF NO RETURN&lt;br /&gt;25 RETURN TO HORROR HIGH&lt;br /&gt;26 HIGH SCHOOL HIGH&lt;br /&gt;27 HIGH CRIMES&lt;br /&gt;28 CRIMES OF PASSION&lt;br /&gt;29 PASSION IN THE DESERT&lt;br /&gt;30 DESERT HEARTS&lt;br /&gt;31 HEARTS OF DARKNESS A FILMMAKERS APOCALYPSE&lt;br /&gt;32 APOCALYPSE NOW&lt;br /&gt;33 NOW YOU SEE HIM NOW YOU DONT&lt;br /&gt;34 DONT BOTHER TO KNOCK&lt;br /&gt;35 KNOCK OFF&lt;br /&gt;36 OFF THE BLACK&lt;br /&gt;37 THE BLACK ANGEL&lt;br /&gt;38 ANGEL BABY&lt;br /&gt;39 BABY SECRET OF THE LOST LEGEND&lt;br /&gt;40 LEGEND OF THE LOST&lt;br /&gt;41 THE LOST BOYS&lt;br /&gt;42 BOYS AND GIRLS&lt;br /&gt;43 GIRLS JUST WANT TO HAVE FUN&lt;br /&gt;44 FUN AND FANCY FREE&lt;br /&gt;45 FREE WILLY&lt;br /&gt;46 FREE WILLY 2 THE ADVENTURE HOME&lt;br /&gt;47 HOME ALONE 3&lt;br /&gt;48 3 NINJAS&lt;br /&gt;49 3 NINJAS KNUCKLE UP&lt;br /&gt;50 UP CLOSE AND PERSONAL&lt;br /&gt;51 PERSONAL BEST&lt;br /&gt;52 BEST OF THE BEST&lt;br /&gt;53 BEST OF THE BEST 3 NO TURNING BACK&lt;br /&gt;54 NO TURNING BACK&lt;br /&gt;55 BACK STAGE&lt;br /&gt;56 STAGE FRIGHT&lt;br /&gt;57 FRIGHT NIGHT&lt;br /&gt;58 NIGHT MOTHER&lt;br /&gt;59 MOTHER NIGHT&lt;br /&gt;60 NIGHT FALLS ON MANHATTAN&lt;br /&gt;61 MANHATTAN MURDER MYSTERY&lt;br /&gt;62 MYSTERY ALASKA&lt;br /&gt;63 ALASKA SPIRIT OF THE WILD&lt;br /&gt;64 THE WILD&lt;br /&gt;65 THE WILD ONE&lt;br /&gt;66 ONE NIGHT WITH THE KING&lt;br /&gt;67 THE KING AND I&lt;br /&gt;68 I NEVER PROMISED YOU A ROSE GARDEN&lt;br /&gt;69 GARDEN STATE&lt;br /&gt;70 STATE FAIR&lt;br /&gt;71 FAIR GAME&lt;br /&gt;72 GAME OF DEATH&lt;br /&gt;73 DEATH WISH V THE FACE OF DEATH&lt;br /&gt;74 DEATH SHIP&lt;br /&gt;75 SHIP OF FOOLS&lt;br /&gt;76 FOOLS RUSH IN&lt;br /&gt;77 IN PRAISE OF OLDER WOMEN&lt;br /&gt;78 WOMEN IN LOVE&lt;br /&gt;79 IN LOVE AND WAR&lt;br /&gt;80 WAR OF THE WORLDS&lt;br /&gt;81 THE WORLDS FASTEST INDIAN&lt;br /&gt;82 INDIAN SUMMER&lt;br /&gt;83 SUMMER CATCH&lt;br /&gt;84 CATCH A FIRE&lt;br /&gt;85 FIRE ON THE MOUNTAIN&lt;br /&gt;86 THE MOUNTAIN MEN&lt;br /&gt;87 MEN WITH GUNS&lt;br /&gt;88 GUNS OF THE MAGNIFICENT SEVEN&lt;br /&gt;89 THE MAGNIFICENT SEVEN RIDE&lt;br /&gt;90 RIDE WITH THE DEVIL&lt;br /&gt;91 THE DEVIL RIDES OUT&lt;br /&gt;92 OUT COLD&lt;br /&gt;93 COLD FEVER&lt;br /&gt;94 FEVER PITCH&lt;br /&gt;95 PITCH BLACK&lt;br /&gt;96 BLACK AND WHITE&lt;br /&gt;97 WHITE WATER SUMMER&lt;br /&gt;98 SUMMER LOVERS&lt;br /&gt;99 LOVERS AND OTHER STRANGERS&lt;br /&gt;100 STRANGERS WHEN WE MEET&lt;br /&gt;101 MEET JOE BLACK&lt;br /&gt;102 BLACK HAWK DOWN&lt;br /&gt;103 DOWN WITH LOVE&lt;br /&gt;104 LOVE LIFE&lt;br /&gt;105 LIFE IS BEAUTIFUL&lt;br /&gt;106 BEAUTIFUL GIRLS&lt;br /&gt;107 GIRLS GIRLS GIRLS&lt;br /&gt;108 GIRLS WILL BE GIRLS&lt;br /&gt;109 GIRLS TOWN&lt;br /&gt;110 TOWN AND COUNTRY&lt;br /&gt;111 COUNTRY LIFE&lt;br /&gt;112 LIFE WITH FATHER&lt;br /&gt;113 FATHER OF THE BRIDE&lt;br /&gt;114 BRIDE OF THE WIND&lt;br /&gt;115 THE WIND AND THE LION&lt;br /&gt;116 THE LION KING&lt;br /&gt;117 KING OF THE JUNGLE&lt;br /&gt;118 THE JUNGLE BOOK&lt;br /&gt;119 JUNGLE BOOK&lt;br /&gt;120 BOOK OF LOVE&lt;br /&gt;121 LOVE CRAZY&lt;br /&gt;122 CRAZY AS HELL&lt;br /&gt;123 HELL NIGHT&lt;br /&gt;124 NIGHT ON EARTH&lt;br /&gt;125 EARTH GIRLS ARE EASY&lt;br /&gt;126 EASY MONEY&lt;br /&gt;127 MONEY FOR NOTHING&lt;br /&gt;128 NOTHING BUT TROUBLE&lt;br /&gt;129 TROUBLE EVERY DAY&lt;br /&gt;130 DAY FOR NIGHT&lt;br /&gt;131 NIGHT OF THE LIVING DEAD&lt;br /&gt;132 DEAD MAN&lt;br /&gt;133 DEAD MAN WALKING&lt;br /&gt;134 WALKING AND TALKING&lt;br /&gt;135 TALKING ABOUT SEX&lt;br /&gt;136 SEX AND THE OTHER MAN&lt;br /&gt;137 MAN OF THE HOUSE&lt;br /&gt;138 THE HOUSE OF THE DEAD&lt;br /&gt;139 DEAD END&lt;br /&gt;140 END OF DAYS&lt;br /&gt;141 DAYS OF HEAVEN&lt;br /&gt;142 HEAVEN CAN WAIT&lt;br /&gt;143 WAIT UNTIL DARK&lt;br /&gt;144 DARK STAR&lt;br /&gt;145 STAR WARS EPISODE V THE EMPIRE STRIKES BACK&lt;br /&gt;146 BACK TO THE BEACH&lt;br /&gt;147 THE BEACH&lt;br /&gt;148 BEACH PARTY&lt;br /&gt;149 PARTY MONSTER&lt;br /&gt;150 MONSTER HOUSE&lt;br /&gt;151 HOUSE OF DRACULA&lt;br /&gt;152 DRACULA DEAD AND LOVING IT&lt;br /&gt;153 IT HAD TO BE YOU&lt;br /&gt;154 YOU LIGHT UP MY LIFE&lt;br /&gt;155 MY LIFE&lt;br /&gt;156 LIFE OR SOMETHING LIKE IT&lt;br /&gt;157 IT HAPPENED ONE NIGHT&lt;br /&gt;158 ONE NIGHT STAND&lt;br /&gt;159 STAND IN&lt;br /&gt;160 IN THE HEAT OF THE NIGHT&lt;br /&gt;161 NIGHT AND THE CITY&lt;br /&gt;162 THE CITY&lt;br /&gt;163 CITY BY THE SEA&lt;br /&gt;164 SEA OF LOVE&lt;br /&gt;165 LOVE WALKED IN&lt;br /&gt;166 IN THE COMPANY OF MEN&lt;br /&gt;167 MEN IN BLACK&lt;br /&gt;168 BLACK LIKE ME&lt;br /&gt;169 ME MYSELF I&lt;br /&gt;170 I WANT TO LIVE&lt;br /&gt;171 LIVE AND LET DIE&lt;br /&gt;172 DIE MONSTER DIE&lt;br /&gt;173 DIE MOMMIE DIE&lt;br /&gt;174 DIE ANOTHER DAY&lt;br /&gt;175 DAY OF THE DEAD&lt;br /&gt;176 THE DEAD&lt;br /&gt;177 DEAD OF NIGHT&lt;br /&gt;178 NIGHT AND DAY&lt;br /&gt;179 DAY OF THE WOMAN&lt;br /&gt;180 THE WOMAN IN RED&lt;br /&gt;181 RED EYE&lt;br /&gt;182 EYE FOR AN EYE&lt;br /&gt;183 AN EYE FOR AN EYE&lt;br /&gt;184 EYE OF GOD&lt;br /&gt;185 GOD TOLD ME TO&lt;br /&gt;186 TO DIE FOR&lt;br /&gt;187 FOR THE BOYS&lt;br /&gt;188 THE BOYS&lt;br /&gt;189 BOYS LIFE&lt;br /&gt;190 LIFE AS A HOUSE&lt;br /&gt;191 HOUSE OF FRANKENSTEIN&lt;br /&gt;192 FRANKENSTEIN MEETS THE WOLF MAN&lt;br /&gt;193 THE WOLF MAN&lt;br /&gt;194 MAN TROUBLE&lt;br /&gt;195 TROUBLE IN PARADISE&lt;br /&gt;196 PARADISE ROAD&lt;br /&gt;197 ROAD HOUSE&lt;br /&gt;198 HOUSE PARTY&lt;br /&gt;199 HOUSE PARTY 3&lt;br /&gt;200 3 NINJAS KICK BACK&lt;br /&gt;201 BACK TO SCHOOL&lt;br /&gt;202 SCHOOL OF ROCK&lt;br /&gt;203 ROCK STAR&lt;br /&gt;204 STAR TREK IV THE VOYAGE HOME&lt;br /&gt;205 HOME ALONE&lt;br /&gt;206 ALONE IN THE DARK&lt;br /&gt;207 DARK BLUE&lt;br /&gt;208 BLUE CAR&lt;br /&gt;209 CAR 54 WHERE ARE YOU&lt;br /&gt;210 YOU CANT TAKE IT WITH YOU&lt;br /&gt;211 YOU CAN COUNT ON ME&lt;br /&gt;212 ME WITHOUT YOU&lt;br /&gt;213 YOU SO CRAZY&lt;br /&gt;214 CRAZY PEOPLE&lt;br /&gt;215 PEOPLE WILL TALK&lt;br /&gt;216 TALK OF ANGELS&lt;br /&gt;217 ANGELS WITH DIRTY FACES&lt;br /&gt;218 FACES OF DEATH&lt;br /&gt;219 DEATH WISH&lt;br /&gt;220 WISH UPON A STAR&lt;br /&gt;221 A STAR IS BORN&lt;br /&gt;222 BORN AMERICAN&lt;br /&gt;223 AMERICAN DREAM&lt;br /&gt;224 DREAM A LITTLE DREAM&lt;br /&gt;225 DREAM MAN&lt;br /&gt;226 MAN ON FIRE&lt;br /&gt;227 FIRE IN THE SKY&lt;br /&gt;228 SKY HIGH&lt;br /&gt;229 HIGH SPIRITS&lt;br /&gt;230 SPIRITS OF THE DEAD&lt;br /&gt;231 DEAD BANG&lt;br /&gt;232 BANG BANG YOURE DEAD&lt;br /&gt;233 DEAD HEAT&lt;br /&gt;234 HEAT AND DUST&lt;br /&gt;235 DUST TO GLORY&lt;br /&gt;236 GLORY ROAD&lt;br /&gt;237 ROAD GAMES&lt;br /&gt;238 GAMES PEOPLE PLAY NEW YORK&lt;br /&gt;239 NEW YORK NEW YORK&lt;br /&gt;240 NEW YORK COP&lt;br /&gt;241 COP LAND&lt;br /&gt;242 LAND OF THE DEAD&lt;br /&gt;243 DEAD MAN ON CAMPUS&lt;br /&gt;244 CAMPUS MAN&lt;br /&gt;245 MAN ON THE MOON&lt;br /&gt;246 MOON OVER PARADOR&lt;br /&gt;Time: 00:49:32&lt;/pre&gt;This is still just a simple &lt;a href="http://en.wikipedia.org/wiki/Best_first_search"&gt;best-first search&lt;/a&gt; and I still have high hopes for a &lt;strike&gt;&lt;a href="http://en.wikipedia.org/wiki/Beam_search"&gt;beam search&lt;/a&gt;&lt;/strike&gt; &lt;a href="http://en.wikipedia.org/wiki/Beam_stack_search"&gt;beam-stack search&lt;/a&gt; to do a lot better.&lt;br /&gt;&lt;br /&gt;[Correction 2008-07-02]: When I say beam search I mean beam-stack search.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-1789560196289325862?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/1789560196289325862/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=1789560196289325862' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/1789560196289325862'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/1789560196289325862'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/07/sling-blade-runner-iii.html' title='Sling Blade Runner III'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-6823720011583106493</id><published>2008-06-23T22:34:00.006+02:00</published><updated>2008-07-02T19:47:50.493+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='Kōan'/><category scheme='http://www.blogger.com/atom/ns#' term='Puzzles'/><title type='text'>Sling Blade Runner II</title><content type='html'>In an effort to understand my heuristic's performance better I've compared the chains  created by icefox's (312 titles) and &lt;a href="http://commentsarelies.blogspot.com/2008/05/sling-blade-runner.html"&gt;my program&lt;/a&gt; (225 titles). Interestingly enough I've found several "gaps" in my chain that really shouldn't be there. Below is a snippet of icefox's chain (the number specifies the title's position in my chain):&lt;pre&gt;219 ROAD GAMES&lt;br /&gt;220 GAMES PEOPLE PLAY NEW YORK&lt;br /&gt;--- NEW YORK NEW YORK&lt;br /&gt;221 NEW YORK COP&lt;br /&gt;222 COP LAND&lt;/pre&gt;&lt;span style="font-style: italic;"&gt;New York Cop&lt;/span&gt; has a subtree of size 7 and since &lt;span style="font-style: italic;"&gt;New York New York&lt;/span&gt;'s subtree is only 6 titles in size, &lt;span style="font-style: italic;"&gt;New York Cop&lt;/span&gt; will &lt;span style="font-weight: bold;"&gt;always&lt;/span&gt; be selected by my heuristic function. But apparently there's a blind spot in my find/extend function that makes it skip &lt;span style="font-style: italic;"&gt;New York New York&lt;/span&gt; even though it's not used in my chain (indicated by ---). Terribly annoying.&lt;br /&gt;&lt;br /&gt;[Update 2008-06-25]: Now that I've "fixed" (I changed a constant from 2 to 1 ;-) the code, the longest chain found is 233 titles. A small improvement, but still. Here's the list with the titles that have been added (the number specifies the title's position in my 225-length chain):&lt;pre&gt;...&lt;br /&gt;036 IT HAD TO BE YOU&lt;br /&gt;--- YOU CANT TAKE IT WITH YOU&lt;br /&gt;037 YOU CAN COUNT ON ME&lt;br /&gt;038 ME MYSELF I&lt;br /&gt;039 I LOVE YOU TO DEATH&lt;br /&gt;--- DEATH WISH V THE FACE OF DEATH&lt;br /&gt;040 DEATH WISH&lt;br /&gt;...&lt;br /&gt;077 ALASKA SPIRIT OF THE WILD&lt;br /&gt;--- THE WILD&lt;br /&gt;078 THE WILD ONE&lt;br /&gt;...&lt;br /&gt;082 LIVE AND LET DIE&lt;br /&gt;--- DIE MONSTER DIE&lt;br /&gt;083 DIE MOMMIE DIE&lt;br /&gt;...&lt;br /&gt;089 DEATH WISH 3&lt;br /&gt;--- 3 NINJAS&lt;br /&gt;090 3 NINJAS KNUCKLE UP&lt;br /&gt;...&lt;br /&gt;101 EYE FOR AN EYE&lt;br /&gt;--- AN EYE FOR AN EYE&lt;br /&gt;102 EYE OF GOD&lt;br /&gt;...&lt;br /&gt;120 THE JUNGLE BOOK&lt;br /&gt;--- JUNGLE BOOK&lt;br /&gt;121 BOOK OF LOVE&lt;br /&gt;...&lt;br /&gt;220 GAMES PEOPLE PLAY NEW YORK&lt;br /&gt;--- NEW YORK NEW YORK&lt;br /&gt;221 NEW YORK COP&lt;br /&gt;...&lt;/pre&gt;[Update 2008-06-26]: Modifying a few parameters creates a chain with 237 titles.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-6823720011583106493?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/6823720011583106493/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=6823720011583106493' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/6823720011583106493'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/6823720011583106493'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/06/sling-blade-runner-ii.html' title='Sling Blade Runner II'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-8213076452615768335</id><published>2008-06-23T10:46:00.001+02:00</published><updated>2008-06-23T10:52:29.691+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Games'/><category scheme='http://www.blogger.com/atom/ns#' term='Contest'/><category scheme='http://www.blogger.com/atom/ns#' term='Toys'/><category scheme='http://www.blogger.com/atom/ns#' term='Peace'/><title type='text'>Make a Peace-toy</title><content type='html'>The &lt;a href="http://stockholmsfreds.se/"&gt;Stockholm local group&lt;/a&gt; of the &lt;a href="http://www.svenskafreds.se/english/"&gt;Swedish Peace and Arbitration Society (SPAS)&lt;/a&gt; has arranged a &lt;a href="http://stockholmsfreds.se/arkiv/2008/05/26/fredsleksakstavlingen/"&gt;contest&lt;/a&gt; in designing "peace-toys". It's a very interesting idea, and a rather difficult one if you ask me, since peace is almost always defined negatively (i.e. peace is the absence of war). It will be very interesting to see what people come up with.&lt;br /&gt;&lt;br /&gt;I suggested to Sofia that we should design a variant of the classic &lt;a href="http://en.wikipedia.org/wiki/Risk_%28game%29"&gt;board game Risk&lt;/a&gt; modified to use non-violent techniques of conflict management. Obviously the game's ultimate goal cannot be world domination and it can't be something, like world democratization, where everyone is a winner either. It's probably going to be very difficult just to figure out the basis for the game. I haven't spent enough time thinking about it and I don't have much hope of participating since the deadline is 1/9 and I don't think Sofia and I will have much time to spend on it during summer but maybe if it rains a lot. Let's hope so ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-8213076452615768335?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/8213076452615768335/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=8213076452615768335' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8213076452615768335'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8213076452615768335'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/06/make-peace-toy.html' title='Make a Peace-toy'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-997175952649150033</id><published>2008-06-18T22:56:00.000+02:00</published><updated>2008-06-18T22:56:52.645+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Testing'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><title type='text'>Testing CLIPS applications with TextTest</title><content type='html'>I was recently re-introduced to &lt;a href="http://www.texttest.org"&gt;TextTest&lt;/a&gt; - a tool for text-based functional testing. TextTest uses the same approach as is used to test CLIPS functionality and that has been suggested several times on the &lt;a href="http://groups.google.com/group/CLIPSESG"&gt;CLIPSESG&lt;/a&gt; mailing list for unit-testing CLIPS applications.&lt;br /&gt;&lt;br /&gt;The benefits of using TextTest for your CLIPS testing is that you can automate the execution of the tests and that you get a nice summary of the differences between expected output and actual output. So if you're currently testing your application "manually" using the CLIPS Dialog window and a diff tool you should give TextTest a try. It may well save you some time.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-997175952649150033?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/997175952649150033/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=997175952649150033' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/997175952649150033'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/997175952649150033'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/06/testing-clips-applications-with.html' title='Testing CLIPS applications with TextTest'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-958476278024639765</id><published>2008-06-14T09:00:00.000+02:00</published><updated>2008-06-14T09:04:45.643+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Scheme'/><category scheme='http://www.blogger.com/atom/ns#' term='Lisp'/><title type='text'>Roots of Lisp II</title><content type='html'>Since I wrote the &lt;a href="http://commentsarelies.blogspot.com/2008/05/roots-of-lisp.html"&gt;post about Paul Graham's article Roots of Lisp&lt;/a&gt; about two weeks ago I've been searching for similar efforts to read, learn from and basically - steal.  And I've got to say. There are some pretty impressive stuff out there.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.canonical.org/%7Ekragen/sw/urscheme/"&gt;Ur-Scheme&lt;/a&gt; is a self-hosting partial implementation of &lt;a href="http://schemers.org/Documents/Standards/R5RS/"&gt;R5RS&lt;/a&gt; that runs on Intel x86 Linux.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.iro.umontreal.ca/%7Eboucherd/mslug/meetings/20041020/minutes-en.html"&gt;The 90 minute Scheme to C compiler&lt;/a&gt; which is a presentation of how to write your own Scheme compiler (it takes 90 minutes to present, not to write the compiler).&lt;br /&gt;&lt;br /&gt;The &lt;a href="http://www.accesscom.com/%7Edarius/hacks/icbins/icbins.tar.gz"&gt;icbins&lt;/a&gt; Lisp to C compiler and interpreter in 10 pages of C by &lt;a href="http://www.accesscom.com/%7Edarius/"&gt;Darius Bacon&lt;/a&gt;. He's written an even smaller one (6 pages) but which doesn't include the interpreter.&lt;br /&gt;&lt;br /&gt;Out of the three I suspect that icbins is what comes closest to answering my question (what kind of boiler-plate code would be required to support such an implementation) though I believe that it is written to support more (probably much more) than what is described in &lt;a href="http://paulgraham.com/rootsoflisp.html"&gt;Graham's article&lt;/a&gt; or in the &lt;a href="http://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf"&gt;Lisp 1.5 Programmer's Manual&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-958476278024639765?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/958476278024639765/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=958476278024639765' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/958476278024639765'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/958476278024639765'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/06/roots-of-lisp-ii.html' title='Roots of Lisp II'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-99591506171089605</id><published>2008-06-11T15:02:00.002+02:00</published><updated>2008-06-11T15:13:43.036+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Work'/><title type='text'>Switching jobs</title><content type='html'>After summer I'm going to start working for Nordea IT instead of Nordea Life &amp;amp; Pensions. It's a big change in many ways (new business area, new systems) and a very small one in others (same cubicle, same coffee machine, same people to go to lunch with).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-99591506171089605?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/99591506171089605/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=99591506171089605' title='1 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/99591506171089605'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/99591506171089605'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/06/switching-jobs.html' title='Switching jobs'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-7986609788636242298</id><published>2008-06-06T20:22:00.002+02:00</published><updated>2008-06-06T21:37:39.810+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='pyClips'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><title type='text'>Creating an .exe for a CLIPS application</title><content type='html'>Every now and then someone (on &lt;a href="http://groups.google.com/group/CLIPSESG"&gt;CLIPSESG&lt;/a&gt;) asks about how to create a standalone binary (usually a Windows .exe) for a CLIPS application they wrote. The process for doing so is described in Section 5 of the &lt;a href="http://clipsrules.sourceforge.net/documentation/v624/apg.htm"&gt;CLIPS Advanced Programming Guide&lt;/a&gt;. But even though that may be the &lt;span style="font-style: italic;"&gt;correct &lt;/span&gt;answer to such questions it isn't always  what the poster wants to hear. At least that's the impression I get.&lt;br /&gt;&lt;br /&gt;The problem is (&lt;span style="font-style: italic;"&gt;probably&lt;/span&gt;, I'm guessing here), that the description is kind of brief, contains little or no examples and is written for an experienced (or at least decent) C programmer.  Which, unfortunately, kind of misses the mark since it seems that most people that ask about how to create .exe files for their applications are (relatively) new programmers and probably feel a bit lost reading that section. I know I do.&lt;br /&gt;&lt;br /&gt;Though I can see several reasons why distributing your CLIPS application as a binary file (in any format) is a bad idea. I also know that sometimes it's the only way, so I'm not going to question your motives or try to talk you out of it. Instead. Here is a step-for-step demonstration on how to do it using &lt;a href="http://pyclips.sourceforge.net/web/"&gt;PyCLIPS&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 0. Know the difference.&lt;/span&gt;&lt;br /&gt;I'm not sure whether or not the steps described in CLIPS APG Section 5 results in a binary that includes the CLIPS IDE (the Dialog window and other useful things). But what I am describing here &lt;span style="font-weight: bold;"&gt;does not&lt;/span&gt;! So if you rely on user input at runtime you're also going to have to create a GUI for your application. I've written &lt;a href="http://commentsarelies.blogspot.com/2008/01/clips-wine-demo-using-wxpython.html"&gt;about&lt;/a&gt; &lt;a href="http://commentsarelies.blogspot.com/2008/01/autodemowxpy.html"&gt;that&lt;/a&gt; &lt;a href="http://commentsarelies.blogspot.com/2008/01/sudokudemowxpy.html"&gt;before&lt;/a&gt; and all of those examples are also available as PyCLIPS downloads.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 1: Install the tools.&lt;/span&gt;&lt;br /&gt;The first thing you need to to do is to get all of the tools needed. You're going to have to download and install &lt;a href="http://www.python.org/"&gt;Python&lt;/a&gt;, &lt;a href="http://sourceforge.net/project/showfiles.php?group_id=114052"&gt;PyCLIPS&lt;/a&gt; and &lt;a href="http://sourceforge.net/project/showfiles.php?group_id=15583"&gt;py2exe&lt;/a&gt;. If you also need a GUI or want to try either of the wxPython CLIPS demos you should get &lt;a href="http://wxpython.org/download.php"&gt;wxPython&lt;/a&gt;. Make sure to get the latest version of Python (2.5.2 as I'm writing this) and releases matching it (2.5) for the other projects. Installation should be very simple since all projects provide binaries and the installers pretty much take care of themselves.&lt;br /&gt;&lt;br /&gt;When you're done with all of the installations you can make sure that everything went alright by opening up IDLE (it should be in your start menu) and try the following:&lt;pre&gt;&gt;&gt;&gt; import clips&lt;br /&gt;&gt;&gt;&gt; import py2exe&lt;br /&gt;&gt;&gt;&gt; import wx&lt;/pre&gt;If either of those raises an exception you're going to have to re-install. Installation instructions and trouble-shooting guidance can be found on the projects' websites and in their docs.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 2: Wrap your application in PyCLIPS.&lt;/span&gt;&lt;br /&gt;Once you've got all the tools in place it's time to write a wrapper for your CLIPS application. I'll use the sudoku demo (sudoku.clp and solve.clp) as base for my example application.&lt;br /&gt;&lt;br /&gt;I'll wrap it in a Python program that reads a file as input (containing an unsolved sudoku puzzle), triggers the CLIPS program to solve the puzzle, queries CLIPS for the answer and writes the solution to stdout.&lt;br /&gt;&lt;br /&gt;The program is in essence a copy of the &lt;span style="font-family:courier new;"&gt;on_openfile&lt;/span&gt; and &lt;span style="font-family:courier new;"&gt;on_solve&lt;/span&gt; functions from the wxPython demo so I've copy-pasted most of it. You can download a .zip with the code &lt;a href="http://www.pulp.se/clips/pyclips/py2exe-demo.zip"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 3: Write a setup.py script.&lt;/span&gt;&lt;br /&gt;The &lt;a href="http://www.py2exe.org/"&gt;py2exe website&lt;/a&gt; has tutorials and examples on how to best use it so I'll be quite brief here.&lt;br /&gt;&lt;br /&gt;In order to compile a Python program/script into a standalone Windows executable you're going to have write a small configuration file (setup.py) which specifies the options py2exe should work with. Py2exe is an extension to distutils so you should read &lt;a href="http://docs.python.org/dist/dist.html"&gt;those docs&lt;/a&gt; as well.&lt;br /&gt;&lt;br /&gt;The setup.py which I'll use looks like this:&lt;pre&gt;from distutils.core import setup&lt;br /&gt;import py2exe&lt;br /&gt;&lt;br /&gt;setup(console=['sudoku.py'], data_files=[('', ['sudoku.clp', 'solve.clp'])])&lt;/pre&gt;there are a lot more options available but since this is more of a teaser (or a shameless plug for Python and PyCLIPS depending on how you look at it) I'll let you look into that yourself.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Step 4: Compile the application.&lt;/span&gt;&lt;br /&gt;All we need to do to generate the .exe is to run the setup.py script in a command prompt:&lt;pre&gt;C:\Program\...\py2exe-demo&gt;setup.py py2exe&lt;br /&gt;running py2exe&lt;br /&gt;...&lt;br /&gt;*** copy data files ***&lt;br /&gt;copying C:\Python25\lib\site-packages\py2exe\run.exe -&gt; C:\Program\...\py2exe-demo\dist\sudoku.exe&lt;br /&gt;&lt;br /&gt;*** binary dependencies ***&lt;br /&gt;Your executable(s) also depend on these dlls which are not included,&lt;br /&gt;you may or may not need to distribute them.&lt;br /&gt;&lt;br /&gt;Make sure you have the license if you distribute any of them, and&lt;br /&gt;make sure you don't distribute files belonging to the operating system.&lt;br /&gt;&lt;br /&gt;ADVAPI32.dll - C:\WINDOWS\system32\ADVAPI32.dll&lt;br /&gt;USER32.dll - C:\WINDOWS\system32\USER32.dll&lt;br /&gt;SHELL32.dll - C:\WINDOWS\system32\SHELL32.dll&lt;br /&gt;KERNEL32.dll - C:\WINDOWS\system32\KERNEL32.dll&lt;br /&gt;msvcrt.dll - C:\WINDOWS\system32\msvcrt.dll&lt;br /&gt;&lt;br /&gt;C:\Program\...\py2exe-demo&gt;cd dist&lt;br /&gt;C:\Program\...\py2exe-demo\dist&gt;sudoku.exe ..\Game03.txt&lt;br /&gt;5 9 7 8 3 4 1 2 6&lt;br /&gt;2 6 8 9 1 5 7 3 4&lt;br /&gt;3 1 4 7 2 6 5 8 9&lt;br /&gt;8 7 3 2 4 1 9 6 5&lt;br /&gt;9 2 5 6 7 3 4 1 8&lt;br /&gt;1 4 6 5 8 9 2 7 3&lt;br /&gt;4 5 2 1 6 8 3 9 7&lt;br /&gt;7 8 9 3 5 2 6 4 1&lt;br /&gt;6 3 1 4 9 7 8 5 2&lt;/pre&gt;Seems to work. But listing the contents of the &lt;span style="font-family:courier new;"&gt;dist&lt;/span&gt; directory &lt;span style="font-style: italic;"&gt;might&lt;/span&gt; make you a bit disappointed:&lt;pre&gt;C:\Program\...\py2exe-demo\dist&gt;dir&lt;br /&gt;...&lt;br /&gt;2006-09-19  09:52            77 824 bz2.pyd&lt;br /&gt;2008-06-04  19:42         1 034 699 library.zip&lt;br /&gt;2006-03-16  16:19           348 160 MSVCR71.dll&lt;br /&gt;2006-09-19  09:52         2 109 440 python25.dll&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;2008-01-18  21:08            39 997 solve.clp&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;2008-01-18  21:08             5 036 sudoku.clp&lt;/span&gt;&lt;br /&gt;2008-06-04  19:42            18 944 sudoku.exe&lt;br /&gt;2006-09-19  09:52           475 136 unicodedata.pyd&lt;br /&gt;2006-09-19  09:52             4 608 w9xpopen.exe&lt;br /&gt;2008-03-09  04:29         1 009 152 _clips.pyd&lt;br /&gt;...&lt;/pre&gt;Neither of the .clp files have been compiled!&lt;br /&gt;&lt;br /&gt;If the purpose of generating the .exe is to hide implementation details, such as the CLIPS program, then you're going to have to convert the .clp files into PyCLIPS programs. It's easy enough to do, but it's boring.&lt;br /&gt;&lt;br /&gt;You have to go through each .clp file and&lt;br /&gt;1) Either remove all comments or convert them into Python comments (use # instead of ;).&lt;br /&gt;2) Indent all lines in the file with 4 spaces.&lt;br /&gt;3) Insert &lt;span style="font-family:courier new;"&gt;import clips&lt;/span&gt; as the first line.&lt;br /&gt;4) Insert &lt;span style="font-family:courier new;"&gt;def load():&lt;/span&gt; as the second line.&lt;br /&gt;5) Wrap all definitions with a &lt;span style="font-family:courier new;"&gt;clips.Build(""" ... """)&lt;/span&gt;.&lt;br /&gt;6) Rename the file to &lt;span style="font-family:courier new;"&gt;[filename]_clp.py&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;After doing that you can remove the &lt;span style="font-family:courier new;"&gt;data_files&lt;/span&gt; option in setup.py and modify a few lines in sudoku.py. If we change:&lt;pre&gt;clips.Load("sudoku.clp")&lt;br /&gt;clips.Load("solve.clp")&lt;/pre&gt;to&lt;pre&gt;import sudoku_clp, solve_clp&lt;br /&gt;sudoku_clp.load()&lt;br /&gt;solve_clp.load()&lt;/pre&gt;and run the compilation again, the problem is "fixed". Since all of the CLIPS constructs are now embedded in Python files they will be sucked into the .exe. The "fixed" code is available &lt;a href="http://www.pulp.se/clips/pyclips/py2exe-demo-fixed.zip"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The steps outlined above might at least be an alternative for those of us that aren't that well-versed in C/C++. I'm sure that the same, or at least something similar, can be done with other tools and languages - but that's for someone else to write about.&lt;br /&gt;&lt;br /&gt;The code I've used as the example ought to be pretty simple to understand, but if you have any questions or if you think I've been too brief at some point, leave a comment or send me an e-mail and we'll sort it out.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-7986609788636242298?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/7986609788636242298/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=7986609788636242298' title='1 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7986609788636242298'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7986609788636242298'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/06/creating-exe-for-clips-application.html' title='Creating an .exe for a CLIPS application'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-7410469496961181669</id><published>2008-06-02T21:30:00.003+02:00</published><updated>2008-06-02T21:36:09.846+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Family'/><category scheme='http://www.blogger.com/atom/ns#' term='Eowyn'/><title type='text'>Eowyn's first birthday</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_3hqf70Lx0Mk/SERJHr_PIqI/AAAAAAAAAEA/lE10FKQztEc/s1600-h/bild+523.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://4.bp.blogspot.com/_3hqf70Lx0Mk/SERJHr_PIqI/AAAAAAAAAEA/lE10FKQztEc/s200/bild+523.jpg" alt="" id="BLOGGER_PHOTO_ID_5207367465563005602" border="0" /&gt;&lt;/a&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_3hqf70Lx0Mk/SERFoEgi7NI/AAAAAAAAADo/C6rtJfTbtvU/s1600-h/bild+438.jpg"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://1.bp.blogspot.com/_3hqf70Lx0Mk/SERFoEgi7NI/AAAAAAAAADo/C6rtJfTbtvU/s200/bild+438.jpg" alt="" id="BLOGGER_PHOTO_ID_5207363623854468306" border="0" /&gt;&lt;/a&gt;&lt;span style="font-size:78%;"&gt;&lt;span style="font-style: italic;"&gt;Eowyn and cousin Nora + granddad in the background.&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;div style="text-align: right;"&gt;&lt;span style="font-size:78%;"&gt;&lt;span style="font-style: italic;"&gt;Sofia and Eowyn.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;Today is Eowyn's first birthday and we've celebrated it (twice) during the weekend. The weather's been great for about a week so we had both birthday parties outside with (almost) all of our relatives visiting. Eo got lots of great presents and seemed to enjoy the attention.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;font-size:78%;" &gt;Granddad, grandmom, some aunts, an uncle, a few cousins, me and Eowyn sitting in the middle.&lt;/span&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_3hqf70Lx0Mk/SERK62H0knI/AAAAAAAAAEI/uHRWXGnimjk/s1600-h/bild+532.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_3hqf70Lx0Mk/SERK62H0knI/AAAAAAAAAEI/uHRWXGnimjk/s320/bild+532.jpg" alt="" id="BLOGGER_PHOTO_ID_5207369443968324210" border="0" /&gt;&lt;/a&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_3hqf70Lx0Mk/SEQ8Yabp0OI/AAAAAAAAADY/ew1v0o8viNs/s1600-h/Bild+485.jpg"&gt;&lt;br /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-7410469496961181669?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/7410469496961181669/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=7410469496961181669' title='3 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7410469496961181669'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7410469496961181669'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/06/eowyns-first-birthday.html' title='Eowyn&apos;s first birthday'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_3hqf70Lx0Mk/SERJHr_PIqI/AAAAAAAAAEA/lE10FKQztEc/s72-c/bild+523.jpg' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-4226122965309391617</id><published>2008-05-28T17:00:00.001+02:00</published><updated>2008-05-28T17:00:09.047+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Lisp'/><title type='text'>Roots of Lisp</title><content type='html'>The first time I read &lt;a href="http://paulgraham.com/"&gt;Paul Graham's&lt;/a&gt; article &lt;a href="http://paulgraham.com/rootsoflisp.html"&gt;The Roots of Lisp&lt;/a&gt; was a couple of years ago. I remember thinking that it's cool that Lisp can be defined in itself using only seven functions (&lt;span style="font-family: courier new;"&gt;quote&lt;/span&gt;, &lt;span style="font-family: courier new;"&gt;atom&lt;/span&gt;, &lt;span style="font-family: courier new;"&gt;eq&lt;/span&gt;, &lt;span style="font-family: courier new;"&gt;cons&lt;/span&gt;, &lt;span style="font-family: courier new;"&gt;car&lt;/span&gt;, &lt;span style="font-family: courier new;"&gt;cdr&lt;/span&gt; and &lt;span style="font-family: courier new;"&gt;cond&lt;/span&gt;) but it's even more interesting to think about what kind of boiler-plate code would be required to support such an implementation. I mean, it's not like &lt;a href="http://www-formal.stanford.edu/jmc/index.html"&gt;McCarthy&lt;/a&gt; and the others had a high-level language available when they wrote the first Lisp interpreter.&lt;br /&gt;&lt;br /&gt;Yesterday I stumbled on the &lt;a href="http://www.softwarepreservation.org/projects/LISP"&gt;Software Preservation Group's History of Lisp-page&lt;/a&gt;. It contains a lot of information about those first steps in Lisp history. One of many interesting things to read about is &lt;a href="http://www.informatimago.com/"&gt;Pascal Bourguignon's&lt;/a&gt; &lt;a href="http://www.mcjones.org/dustydecks/archives/2005/06/08/41/"&gt;work&lt;/a&gt; on getting &lt;a href="http://www.softwarepreservation.org/projects/LISP/LISP1.5-Bonnie-sBirthdayAssembly.pdf"&gt;the first Lisp compiler&lt;/a&gt; (written in 1962) running on an &lt;a href="http://www.cozx.com/%7Edpitts/ibm7090.html"&gt;IBM 7090 emulator&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Whilst Pascal's project is indeed impressive work and interesting in many ways it would be even more interesting to do something similar using &lt;a href="http://gcc.gnu.org/"&gt;GCC&lt;/a&gt; in order to get a minimal Lisp interpreter. And I mean &lt;span style="font-weight: bold;"&gt;really&lt;/span&gt; minimal. For instance, &lt;span style="font-style: italic;"&gt;complicated&lt;/span&gt; things to parse such as &lt;span style="font-family: courier new;"&gt;(foo bar baz)&lt;/span&gt; won't be supported, a minimal parser would require you to write &lt;span style="font-family: courier new;"&gt;(foo . (bar . (baz . ())))&lt;/span&gt;. But since that's just the first layer in the implementation we could write a new reader using our minimal Lisp. One that would support, among other things, parsing lists as we'd usually write them.&lt;br /&gt;&lt;br /&gt;I wonder if the same seven functions will be sufficient to pull that off or if it turns out to require more things. Unfortunately I haven't got the time to find out. I'll just keep reading the available historical material and keep hoping for someone else to step up for this experiment ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-4226122965309391617?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/4226122965309391617/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=4226122965309391617' title='3 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/4226122965309391617'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/4226122965309391617'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/05/roots-of-lisp.html' title='Roots of Lisp'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-2572204226221906558</id><published>2008-05-23T09:00:00.003+02:00</published><updated>2008-05-23T09:08:12.938+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Domain Specific Languages and XML</title><content type='html'>A couple of days ago &lt;a href="http://p-cos.blogspot.com/"&gt;Pascal Costanza&lt;/a&gt; wrote &lt;a href="http://p-cos.blogspot.com/2008/05/fear-of-parsers.html"&gt;an interesting reply&lt;/a&gt; to &lt;a href="http://martinfowler.com/bliki/"&gt;Martin Fowler's&lt;/a&gt; post &lt;a href="http://www.martinfowler.com/bliki/ParserFear.html"&gt;ParserFear&lt;/a&gt;. Like Pascal, I think Martin misses the point, and I agree that the problem is not that it's difficult to implement a DSL. The problem is that the bang/buck ratio favors other solutions (usually XML). But, I'd also like to point out that even when a DSL would prove useful it's still not enough to go and design your &lt;span style="font-weight: bold;"&gt;own&lt;/span&gt; language.&lt;br /&gt;&lt;br /&gt;Creating a DSL from scratch is &lt;span style="font-weight: bold;"&gt;very likely&lt;/span&gt; to be, mostly, a duplication of effort. And it ought to be very difficult to justify the cost of that effort considering that there are so many well-designed, easily embedded, languages already. I'm thinking about languages like &lt;a href="http://www.python.org/"&gt;Python&lt;/a&gt;, &lt;a href="http://www.ruby-lang.org/en/"&gt;Ruby&lt;/a&gt; and &lt;a href="http://developer.mozilla.org/en/docs/JavaScript"&gt;JavaScript&lt;/a&gt; to mention a few.&lt;br /&gt;&lt;br /&gt;Don't get me wrong. I am a fan of the DSL approach (I try to stay away from XML whenever I can) but I think that it's a bit much to go to such lengths to create custom syntax instead of looking for a decent match in existing languages.&lt;br /&gt;&lt;br /&gt;Even in situations where an XML parser is clearly sufficient, I find it better to expose a subset of an &lt;span style="font-style: italic;"&gt;existing&lt;/span&gt; programming language (usually Python) to the users instead. That way I can grow the capabilities of the language easily and I can re-use all of the resources available (IDE plug-ins, tools, tutorials and other documentation) when instructing users.&lt;br /&gt;&lt;br /&gt;The approach is similar to, or maybe exactly the same as, how Martin describes creating a DSL around a &lt;a href="http://martinfowler.com/dslwip/SemanticModel.html"&gt;Semantic Model&lt;/a&gt;. The difference is that I use reflection to populate the model instead of a custom parser. I've found that this works really well with a language like Python. It's usually a lot less code than maintaining &lt;a href="http://antlr.org/"&gt;ANTLR&lt;/a&gt; grammar files or XML schemas/parsers and I think it usually ends up being a lot more understandable. But I guess it's really a matter of taste. You say &lt;span style="font-family: verdana;font-family:arial;" &gt;tomato&lt;/span&gt;, I say &lt;span style="font-family:courier new;"&gt;tomato&lt;/span&gt; and all that.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-2572204226221906558?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/2572204226221906558/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=2572204226221906558' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/2572204226221906558'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/2572204226221906558'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/05/domain-specific-languages-and-xml.html' title='Domain Specific Languages and XML'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-7526593090098688851</id><published>2008-05-20T10:41:00.002+02:00</published><updated>2008-05-20T15:38:46.043+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='pyRete'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>NotNode woes</title><content type='html'>Since the &lt;a href="http://commentsarelies.blogspot.com/2008/03/1000-loc-removed.html"&gt;rewrite of the RuleCompiler&lt;/a&gt; I've been making good progress at good speed with PyRete.  It might appear different to the casual observer but the reason it's taking so long to get even an alpha release out is because I've only got about an hour or two a week to spend.&lt;br /&gt;&lt;br /&gt;Anyway, last week I spent my hours trying to add support for the NotNode. And, instead of thinking ahead, I implemented it, more or less, &lt;span style="font-weight: bold;"&gt;exactly&lt;/span&gt; in the same faulty manner as I did last time. Which didn't work, I might add. I knew it! But somehow, I kept hoping that changes I had made &lt;span style="font-style: italic;"&gt;somewhere else&lt;/span&gt; would automagically clean up things &lt;span style="font-style: italic;"&gt;here&lt;/span&gt; as well. Stupid! &lt;span style="font-style: italic;"&gt;Stupid!&lt;/span&gt; &lt;span style="font-style: italic; font-weight: bold;"&gt;Stupid!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;So, now I've got a RuleCompiler that works as long as the Not-CE contains no more than one Pattern-CE. So &lt;span style="font-family:courier new;"&gt;Not(Foo(bar == var))&lt;/span&gt; works fine but &lt;span style="font-family:courier new;"&gt;Not(Foo(bar == var) and Not(Bar(foo == var)))&lt;/span&gt; won't compile. Well. It &lt;span style="font-style: italic;"&gt;compiles&lt;/span&gt;. It's just that it won't result in a correct, usable structure.&lt;br /&gt;&lt;br /&gt;The problem is that my approach tries to tack on some state-flags to bits and pieces in the compilation process. But since the Python AST visitor uses a callback interface it's difficult (not impossible) to store state between calls. The more complex the expression being parsed the closer to insanity &lt;span style="font-style: italic;"&gt;I&lt;/span&gt; get.&lt;br /&gt;&lt;br /&gt;The easier solution, and probably the more correct one as well, is to treat whatever is within the Not-CE as a Rete Network in itself. Once compilation is done I rip out the ProductionNode from it and replace it with a NotNode (where the sub-network feeds from the right) and join it to the main network (feeding from the left).&lt;br /&gt;&lt;br /&gt;That requires some small changes in my current implementation, but once I get it to support recursive use it's going to work with both Exists-CE and Forall-CE without me having to add an increasingly complex structure for storing state. But, then again. I knew that already! I even knew that &lt;span style="font-style: italic;"&gt;before&lt;/span&gt; I started working on it. I just didn't believe it would turn out so bad this time. So, I'll spend the rest of the day kicking myself and then it's back to work. Doing it over, but &lt;span style="font-style: italic;"&gt;right&lt;/span&gt; this time.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-7526593090098688851?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/7526593090098688851/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=7526593090098688851' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7526593090098688851'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7526593090098688851'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/05/notnode-woes.html' title='NotNode woes'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-4433147748362642600</id><published>2008-05-14T09:00:00.000+02:00</published><updated>2008-05-14T09:00:02.126+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='CodeKata'/><title type='text'>Why I love Python</title><content type='html'>Two days ago I attended the first &lt;a href="http://groups.google.com/group/gothpy"&gt;GothPy&lt;/a&gt; meeting here in Gothenburg. Jeppesen was kind enough to let us use one of their conference rooms for the meeting. About ten people showed up to listen to Andrew's &lt;a href="http://us.pycon.org/2007"&gt;PyCon 2007&lt;/a&gt; &lt;a href="http://us.pycon.org/zope/talks/2007/fri/track2/013/talkDetails2"&gt;talk&lt;/a&gt; about &lt;a href="http://www.stackless.com/"&gt;Stackless Python&lt;/a&gt; and to work on the &lt;a href="http://codingdojo.org/cgi-bin/wiki.pl?KataBankOCR"&gt;BankOCR code kata&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The kata was very interesting. It was my first and I was dead certain we'd end up fighting about IDE's or whether function names should be &lt;span style="font-family:courier new;"&gt;likeThis&lt;/span&gt; or &lt;span style="font-family:courier new;"&gt;like_this&lt;/span&gt;, but everyone was very polite and helpful. Funny thing though. When we planned the meeting, Emily insisted that we should bring a computer that is &lt;span style="font-style: italic;"&gt;not-a-Mac&lt;/span&gt;, preferably a Windows PC. About two minutes into the meeting there are at least six Macs on the table and I only counted to seven computers in total. But apart from the fact that no one could get along with the keyboard, the PyDev/Eclipse editor and that the unit test results where &lt;span style="color: rgb(255, 0, 0);"&gt;printed in red&lt;/span&gt; regardless of whether they failed or succeded everything went fine using a Windows-machine ;-)&lt;br /&gt;&lt;br /&gt;We actually managed to get further than I thought we would during the kata.  The last piece of code we wrote was the validation part (User story #2)&lt;br /&gt;&lt;blockquote&gt;... The next step therefore is to validate that the numbers you read are in fact valid account numbers. A valid account number has a valid checksum. This can be calculated as follows:&lt;br /&gt;&lt;pre&gt;account number:  3  4  5  8  8  2  8  6  5&lt;br /&gt;position names:  d9 d8 d7 d6 d5 d4 d3 d2 d1&lt;br /&gt;&lt;br /&gt;checksum calculation:&lt;br /&gt;(d1+2*d2+3*d3 +..+9*d9) mod 11 = 0&lt;/pre&gt;So now you should also write some code that calculates the checksum for a given number, and identifies if it is a valid account number.&lt;/blockquote&gt;We made three different versions of the &lt;span style="font-family:courier new;"&gt;is_valid&lt;/span&gt; method. Jacob, who had the keyboard at the time, implemented it like this:&lt;pre&gt;&gt;&gt;&gt; def is_valid(account_number):&lt;br /&gt;...   total = 0&lt;br /&gt;...   for i in xrange(9):&lt;br /&gt;...     total += (9-i)* account_number[i]&lt;br /&gt;...   return total % 11 == 0&lt;/pre&gt;and I suggested this:&lt;pre&gt;&gt;&gt;&gt; def is_valid(account_number):&lt;br /&gt;...   return sum(d* i for d, i in zip(reversed(account_number), range(1,10))) % 11 == 0&lt;/pre&gt;but the clear winner in our little refactoring battle was Andrew who wrote this:&lt;pre&gt;&gt;&gt;&gt; def is_valid(account_number):&lt;br /&gt;...   d9,d8,d7,d6,d5,d4,d3,d2,d1 = account_number&lt;br /&gt;...   return (d1+2*d2+3*d3+4*d4+5*d5+6*d6+7*d7+8*d8+9*d9) % 11 == 0&lt;/pre&gt;which is brilliant and reminded me of why I love Python. It looks like pseudo-code! Reading Andrew's implementation is basically like reading the specification.&lt;br /&gt;&lt;br /&gt;NOTE! I re-wrote these functions from memory so I may have missed some little detail. My apologies if that is the case. Also, some variables have been renamed to protect the innocent.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-4433147748362642600?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/4433147748362642600/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=4433147748362642600' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/4433147748362642600'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/4433147748362642600'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/05/why-i-love-python.html' title='Why I love Python'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-6734938916093866092</id><published>2008-05-12T08:35:00.008+02:00</published><updated>2008-07-02T20:14:03.762+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='Kōan'/><category scheme='http://www.blogger.com/atom/ns#' term='Puzzles'/><title type='text'>Sling Blade Runner</title><content type='html'>I found the &lt;a href="http://itasoftware.com/careers/SolveThisWorkHerePuzzles.html"&gt;ITA Software Hiring Puzzles&lt;/a&gt; almost a year ago and ever since I haven't really been able to stop thinking about one of the puzzles: Sling Blade Runner.&lt;br /&gt;&lt;blockquote&gt;"How long a chain of overlapping movie titles, like Sling Blade Runner, can you find?"&lt;br /&gt;&lt;br /&gt;Use the following listing of movie titles: &lt;a href="http://itasoftware.com/careers/puzzles/MOVIES.LST"&gt;MOVIES.LST&lt;/a&gt;. Multi-word overlaps, as in "License &lt;em&gt;to Kill&lt;/em&gt; a Mockingbird," are allowed. The same title may not be used more than once in a solution. Heuristic solutions that may not always produce the greatest number of titles will be accepted: seek a reasonable tradeoff of efficiency and optimality.&lt;/blockquote&gt;The file MOVIES.LST contains 6561 movie titles in alphabetical order.&lt;br /&gt;&lt;br /&gt;It *sounds* simple enough, but it's actually quite a challenging problem. In fact, if I'm not mistaken, it's one of those problems you really can't find an *optimal* solution to. Just better or worse approximations thereof.&lt;br /&gt;&lt;br /&gt;I think I've made something like five or six, more or less serious, attempts at getting a decent solution during the year. But thinking about this problem has made me read several books, web pages and articles on various subjects like graph theory and clustering. I've written code in Lisp, CLIPS and Python to test ideas I've had. All in all; it's been a very rewarding, educational and fun year.&lt;br /&gt;&lt;br /&gt;Last week I decided to give it another serious attempt. That resulted in (after two days and 6 hours worth of coding) a small (~200 LOC) Python program that finds a chain that is 225 titles long. I really have no idea if that is good or bad. I know that &lt;a href="http://stuffthathappens.com/blog/2007/10/03/sling-blade-runner/"&gt;Eric Burke found a chain that is 245 titles long&lt;/a&gt; so I guess I could do better. But if that chain is optimal, or close to optimal, I'm quite pleased anyway.&lt;br /&gt;&lt;br /&gt;My program uses a &lt;a href="http://en.wikipedia.org/wiki/Best-first_search"&gt;best-first search&lt;/a&gt; approach using the size of the successors tree as the heuristic. It's a rather crude approach but works surprisingly well. Since it doesn't backtrack or explore more than one path at a time it doesn't really require that much space. This also means that I can keep using my laptop while the program is running.&lt;br /&gt;&lt;br /&gt;The program works as follows:&lt;br /&gt;&lt;br /&gt;The first step is to build a dict containing all the successors for a title. I'm actually using the line number (from MOVIES.LST) as the identifier instead of the title itself, but this should give you an idea of what it looks like:&lt;pre&gt;{ ..., 'SLING BLADE': ['BLADE II', 'BLADE RUNNER', 'BLADE TRINITY',], ... }&lt;/pre&gt;It takes about half a second to create the dict for this data set. It contains 6561 keys and the sum of the lengths of the values is 15157.&lt;br /&gt;&lt;br /&gt;The second step is the search. It is done by selecting the title with the most successors (that isn't already in the list), adding it to the list and repeating until there are no more titles that can be added.&lt;br /&gt;&lt;br /&gt;The dict is sorted by the length of the list of successors so we don't have to search using all of the 6561 titles as starting points. Since, at least in theory we'll get shorter and shorter chains the further down we go.&lt;br /&gt;&lt;br /&gt;The gist of the search function looks like this:&lt;pre&gt;&gt;&gt;&gt; def best_first_search(xref, lookahead = 5, cutoff = -1):&lt;br /&gt;...   [...]&lt;br /&gt;...     for index in sorted(xref.keys(), key = lambda k: tree_size(xref, k, 0, [k,]), reverse = True):&lt;br /&gt;...       [...]&lt;br /&gt;...       while True:&lt;br /&gt;...         [...]&lt;br /&gt;...         for next in sorted(xref[curr], key = lambda k: tree_size(xref, k, lookahead, chain), reverse = True):&lt;br /&gt;...           if next not in chain:&lt;br /&gt;...             chain.append(next)&lt;br /&gt;...             assigned = True&lt;br /&gt;...             break&lt;br /&gt;...         if not assigned:&lt;br /&gt;...           if len(chain) &gt; len(longest):&lt;br /&gt;...             print len(chain), "(#%s)" % (i), "%02.2fs" % (time.time()-start)&lt;br /&gt;...             longest = chain&lt;br /&gt;...           break&lt;br /&gt;...       [...]&lt;br /&gt;...   return longest&lt;/pre&gt;The function that calculates the size of the successors tree (see below) does so by traversing it to a certain depth. The deeper you search, the longer it takes, but there seems to be diminishing returns when using a search-depth greater than six or seven.&lt;pre&gt;&gt;&gt;&gt; def tree_size(xref, curr, depth, chain = None):&lt;br /&gt;...   if chain is None:&lt;br /&gt;...     chain = []&lt;br /&gt;...   result = 0&lt;br /&gt;...   if depth &gt; 0:&lt;br /&gt;...     for next in xref[curr][1:]:&lt;br /&gt;...       if next not in chain:&lt;br /&gt;...         result += tree_size(xref, next, depth- 1, chain+ [next,])&lt;br /&gt;...   else:&lt;br /&gt;...     length = 0&lt;br /&gt;...     for title in xref[curr][1:]:&lt;br /&gt;...       if title not in chain:&lt;br /&gt;...         length += 1&lt;br /&gt;...     result = length&lt;br /&gt;...   return result&lt;/pre&gt;Using the above we can get a chain that is 203 titles long. It takes about two and a half minutes and is found as the 17th title searched. A chain that is 200 titles long is found when expanding the third title and takes about 20 seconds to get to. The next best chain is 202 titles long and takes about 1 minute 15 seconds to find at the ninth title expanded. The total runtime for the program using a cutoff at 20 takes about three minutes to complete. Here is the output:&lt;pre&gt;161 (#1) 7.98s&lt;br /&gt;177 (#2) 14.72s&lt;br /&gt;200 (#3) 20.57s&lt;br /&gt;202 (#9) 72.25s&lt;br /&gt;203 (#17) 146.46s&lt;br /&gt;&lt;br /&gt;1 WOMEN IN LOVE&lt;br /&gt;2 LOVE WALKED IN&lt;br /&gt;3 IN THIS OUR LIFE&lt;br /&gt;4 LIFE OR SOMETHING LIKE IT&lt;br /&gt;5 IT HAPPENED ONE NIGHT&lt;br /&gt;...&lt;br /&gt;199 SEX AND THE OTHER MAN&lt;br /&gt;200 MAN OF THE YEAR&lt;br /&gt;201 YEAR OF THE DRAGON&lt;br /&gt;202 DRAGON SEED&lt;br /&gt;203 SEED OF CHUCKY&lt;br /&gt;203 titles in chain 182.76s&lt;/pre&gt;To get a longer chain we need to do some tweaking. The problem is that by selecting the title with the largest successors tree we might get long chains, but we're missing all of those little detours that make it the "longest" chain. For example, if we have a title A with two successors, B and C, where B has several successors (D, E, F, ...) and C only has B as its successor. Even though A-&gt;C-&gt;B, clearly, is the better choice my program will *always* choose the direct route A-&gt;B.&lt;br /&gt;&lt;br /&gt;To compensate for this blind spot, every chain is expanded by trying to find longer paths between each pair of nodes in the chain (see below). Unfortunately, by adding this extra step the program requires more memory and becomes noticeably slower.&lt;pre&gt;&gt;&gt;&gt; def expand(xref, chain, max_depth, longest):&lt;br /&gt;...   [...]&lt;br /&gt;...     for curr, next in zip(chain[:-1], chain[1:]):&lt;br /&gt;...       expansions = None&lt;br /&gt;...       for i in range(2, max_depth+ 1):&lt;br /&gt;...         paths = find(xref, curr, next, i, chain)&lt;br /&gt;...         if paths:&lt;br /&gt;...           expansions = paths&lt;br /&gt;...       if expansions is not None:&lt;br /&gt;...         expansion = sorted(expansions, key = lambda k: len(k), reverse = True)[0]&lt;br /&gt;...         [...]&lt;br /&gt;...         chain[chain.index(next):chain.index(next)] = expansion&lt;br /&gt;...   return chain&lt;/pre&gt;The &lt;span style="font-family:courier new;"&gt;find&lt;/span&gt; function looks like this:&lt;pre&gt;&gt;&gt;&gt; def find(xref, curr, end, depth, chain = None):&lt;br /&gt;...   if chain is None:&lt;br /&gt;...     chain = []&lt;br /&gt;...   if depth &gt; 0:&lt;br /&gt;...     result = []&lt;br /&gt;...     for next in xref[curr][1:]:&lt;br /&gt;...       if next not in chain:&lt;br /&gt;...         paths = find(xref, next, end, depth- 1, chain+ [next,])&lt;br /&gt;...         if paths:&lt;br /&gt;...           for path in paths:&lt;br /&gt;...             result.append([next,]+ path)&lt;br /&gt;...     return result&lt;br /&gt;...   elif end in xref[curr][1:]:&lt;br /&gt;...     return [[],]&lt;br /&gt;...   return None&lt;/pre&gt;I've tried various values for &lt;span style="font-family:courier new;"&gt;max_depth&lt;/span&gt; and 25 seems to be a good-enough value. I have found several expansions that are longer than that but they don't seem to add to the overall result anyway.&lt;br /&gt;&lt;br /&gt;If we change the block of code that checks for and assigns the longest chain in the &lt;span style="font-family:courier new;"&gt;best_first_search&lt;/span&gt; function to:&lt;pre&gt;...         if not assigned:&lt;br /&gt;...           chain = expand(xref, chain, max_depth, len(longest))&lt;br /&gt;...           if len(chain) &gt; len(longest):&lt;br /&gt;...             print len(chain), "(#%s)" % (i), "%02.2fs" % (time.time()-start)&lt;br /&gt;...             longest = chain&lt;br /&gt;...           break&lt;/pre&gt;and run the program again we get the first result after about a minute and it is 205 titles long.&lt;br /&gt;&lt;br /&gt;The longest chain is 225 titles and it is found at #134 but it takes almost 50 minutes to find. Running the whole search (500 "best" titles) takes about 4 hours. Running it with a cutoff of 150 takes about 55 minutes and produces the following output:&lt;pre&gt;205 (#1) 52.72s&lt;br /&gt;206 (#5) 176.38s&lt;br /&gt;208 (#6) 199.54s&lt;br /&gt;[10]&lt;br /&gt;209 (#12) 338.63s&lt;br /&gt;211 (#13) 360.17s&lt;br /&gt;212 (#18) 471.40s&lt;br /&gt;[20]&lt;br /&gt;213 (#21) 538.34s&lt;br /&gt;[30]&lt;br /&gt;[40]&lt;br /&gt;[50]&lt;br /&gt;[60]&lt;br /&gt;[70]&lt;br /&gt;[80]&lt;br /&gt;215 (#85) 1904.34s&lt;br /&gt;[90]&lt;br /&gt;[100]&lt;br /&gt;[110]&lt;br /&gt;218 (#116) 2592.82s&lt;br /&gt;[120]&lt;br /&gt;[130]&lt;br /&gt;219 (#131) 2858.43s&lt;br /&gt;225 (#134) 2900.19s&lt;br /&gt;[140]&lt;br /&gt;[150]&lt;br /&gt;1 OFF THE BLACK&lt;br /&gt;2 THE BLACK ANGEL&lt;br /&gt;3 ANGEL EYES&lt;br /&gt;4 EYES OF AN ANGEL&lt;br /&gt;5 ANGEL BABY&lt;br /&gt;6 BABY SECRET OF THE LOST LEGEND&lt;br /&gt;7 LEGEND OF THE LOST&lt;br /&gt;8 THE LOST BOYS&lt;br /&gt;9 BOYS ON THE SIDE&lt;br /&gt;10 SIDE OUT&lt;br /&gt;11 OUT COLD&lt;br /&gt;12 COLD FEVER&lt;br /&gt;13 FEVER PITCH&lt;br /&gt;14 PITCH BLACK&lt;br /&gt;15 BLACK HAWK DOWN&lt;br /&gt;16 DOWN WITH LOVE&lt;br /&gt;17 LOVE WALKED IN&lt;br /&gt;18 IN THIS OUR LIFE&lt;br /&gt;19 LIFE OR SOMETHING LIKE IT&lt;br /&gt;20 IT HAPPENED ONE NIGHT&lt;br /&gt;21 ONE NIGHT STAND&lt;br /&gt;22 STAND IN&lt;br /&gt;23 IN THE HEAT OF THE NIGHT&lt;br /&gt;24 THE NIGHT OF THE FOLLOWING DAY&lt;br /&gt;25 DAY OF THE DEAD&lt;br /&gt;26 THE DEAD&lt;br /&gt;27 DEAD OF NIGHT&lt;br /&gt;28 NIGHT OF THE LIVING DEAD&lt;br /&gt;29 DEAD MAN&lt;br /&gt;30 DEAD MAN WALKING&lt;br /&gt;31 WALKING AND TALKING&lt;br /&gt;32 TALKING ABOUT SEX&lt;br /&gt;33 SEX AND THE OTHER MAN&lt;br /&gt;34 MAN OF THE HOUSE&lt;br /&gt;35 HOUSE OF DRACULA&lt;br /&gt;36 DRACULA DEAD AND LOVING IT&lt;br /&gt;37 IT HAD TO BE YOU&lt;br /&gt;38 YOU CAN COUNT ON ME&lt;br /&gt;39 ME MYSELF I&lt;br /&gt;40 I LOVE YOU TO DEATH&lt;br /&gt;41 DEATH WISH&lt;br /&gt;42 WISH UPON A STAR&lt;br /&gt;43 A STAR IS BORN&lt;br /&gt;44 BORN AMERICAN&lt;br /&gt;45 AMERICAN ME&lt;br /&gt;46 ME WITHOUT YOU&lt;br /&gt;47 YOU LIGHT UP MY LIFE&lt;br /&gt;48 MY LIFE&lt;br /&gt;49 LIFE AS A HOUSE&lt;br /&gt;50 HOUSE OF FRANKENSTEIN&lt;br /&gt;51 FRANKENSTEIN AND THE MONSTER FROM HELL&lt;br /&gt;52 FROM HELL&lt;br /&gt;53 HELL UP IN HARLEM&lt;br /&gt;54 HARLEM RIVER DRIVE&lt;br /&gt;55 DRIVE ME CRAZY&lt;br /&gt;56 CRAZY AS HELL&lt;br /&gt;57 HELL NIGHT&lt;br /&gt;58 NIGHT AND THE CITY&lt;br /&gt;59 THE CITY&lt;br /&gt;60 CITY OF ANGELS&lt;br /&gt;61 ANGELS WITH DIRTY FACES&lt;br /&gt;62 FACES OF DEATH&lt;br /&gt;63 DEATH SHIP&lt;br /&gt;64 SHIP OF FOOLS&lt;br /&gt;65 FOOLS RUSH IN&lt;br /&gt;66 IN THE WINTER DARK&lt;br /&gt;67 DARK STAR&lt;br /&gt;68 STAR TREK IV THE VOYAGE HOME&lt;br /&gt;69 HOME ALONE&lt;br /&gt;70 ALONE IN THE DARK&lt;br /&gt;71 THE DARK HALF&lt;br /&gt;72 HALF LIGHT&lt;br /&gt;73 LIGHT OF DAY&lt;br /&gt;74 DAY FOR NIGHT&lt;br /&gt;75 NIGHT FALLS ON MANHATTAN&lt;br /&gt;76 MANHATTAN MURDER MYSTERY&lt;br /&gt;77 MYSTERY ALASKA&lt;br /&gt;78 ALASKA SPIRIT OF THE WILD&lt;br /&gt;79 THE WILD ONE&lt;br /&gt;80 ONE NIGHT WITH THE KING&lt;br /&gt;81 THE KING AND I&lt;br /&gt;82 I WANT TO LIVE&lt;br /&gt;83 LIVE AND LET DIE&lt;br /&gt;84 DIE MOMMIE DIE&lt;br /&gt;85 DIE HARD&lt;br /&gt;86 HARD EIGHT&lt;br /&gt;87 EIGHT AND A HALF WOMEN&lt;br /&gt;88 WOMEN IN LOVE&lt;br /&gt;89 LOVE AND DEATH&lt;br /&gt;90 DEATH WISH 3&lt;br /&gt;91 3 NINJAS KNUCKLE UP&lt;br /&gt;92 UP CLOSE AND PERSONAL&lt;br /&gt;93 PERSONAL BEST&lt;br /&gt;94 BEST OF THE BEST&lt;br /&gt;95 BEST MEN&lt;br /&gt;96 MEN IN BLACK&lt;br /&gt;97 BLACK AND WHITE&lt;br /&gt;98 WHITE HUNTER BLACK HEART&lt;br /&gt;99 HEART CONDITION&lt;br /&gt;100 CONDITION RED&lt;br /&gt;101 RED EYE&lt;br /&gt;102 EYE FOR AN EYE&lt;br /&gt;103 EYE OF GOD&lt;br /&gt;104 GOD TOLD ME TO&lt;br /&gt;105 TO DIE FOR&lt;br /&gt;106 FOR THE BOYS&lt;br /&gt;107 THE BOYS&lt;br /&gt;108 BOYS AND GIRLS&lt;br /&gt;109 GIRLS WILL BE GIRLS&lt;br /&gt;110 GIRLS GIRLS GIRLS&lt;br /&gt;111 GIRLS OF SUMMER&lt;br /&gt;112 SUMMER SCHOOL&lt;br /&gt;113 SCHOOL OF ROCK&lt;br /&gt;114 ROCK STAR&lt;br /&gt;115 STAR TREK THE MOTION PICTURE&lt;br /&gt;116 PICTURE BRIDE&lt;br /&gt;117 BRIDE OF THE WIND&lt;br /&gt;118 THE WIND AND THE LION&lt;br /&gt;119 THE LION KING&lt;br /&gt;120 KING OF THE JUNGLE&lt;br /&gt;121 THE JUNGLE BOOK&lt;br /&gt;122 BOOK OF LOVE&lt;br /&gt;123 LOVE IS THE DEVIL&lt;br /&gt;124 THE DEVIL RIDES OUT&lt;br /&gt;125 OUT OF THE BLUE&lt;br /&gt;126 BLUE CAR&lt;br /&gt;127 CAR 54 WHERE ARE YOU&lt;br /&gt;128 YOU ONLY LIVE ONCE&lt;br /&gt;129 ONCE AROUND&lt;br /&gt;130 AROUND THE BEND&lt;br /&gt;131 BEND OF THE RIVER&lt;br /&gt;132 THE RIVER&lt;br /&gt;133 THE RIVER WILD&lt;br /&gt;134 WILD THINGS&lt;br /&gt;135 THINGS TO COME&lt;br /&gt;136 COME AND GET IT&lt;br /&gt;137 IT TAKES TWO&lt;br /&gt;138 TWO FRIENDS&lt;br /&gt;139 FRIENDS WITH MONEY&lt;br /&gt;140 MONEY FOR NOTHING&lt;br /&gt;141 NOTHING BUT TROUBLE&lt;br /&gt;142 TROUBLE EVERY DAY&lt;br /&gt;143 DAY OF THE WOMAN&lt;br /&gt;144 THE WOMAN IN RED&lt;br /&gt;145 RED RIVER&lt;br /&gt;146 RIVER OF NO RETURN&lt;br /&gt;147 RETURN TO HORROR HIGH&lt;br /&gt;148 HIGH SPIRITS&lt;br /&gt;149 SPIRITS OF THE DEAD&lt;br /&gt;150 DEAD BANG&lt;br /&gt;151 BANG BANG YOURE DEAD&lt;br /&gt;152 DEAD MAN ON CAMPUS&lt;br /&gt;153 CAMPUS MAN&lt;br /&gt;154 MAN TROUBLE&lt;br /&gt;155 TROUBLE IN PARADISE&lt;br /&gt;156 PARADISE ROAD&lt;br /&gt;157 ROAD HOUSE&lt;br /&gt;158 HOUSE PARTY&lt;br /&gt;159 HOUSE PARTY 3&lt;br /&gt;160 3 NINJAS KICK BACK&lt;br /&gt;161 BACK STAGE&lt;br /&gt;162 STAGE FRIGHT&lt;br /&gt;163 FRIGHT NIGHT&lt;br /&gt;164 NIGHT MOTHER&lt;br /&gt;165 MOTHER NIGHT&lt;br /&gt;166 NIGHT ON EARTH&lt;br /&gt;167 EARTH GIRLS ARE EASY&lt;br /&gt;168 EASY COME EASY GO&lt;br /&gt;169 GO NOW&lt;br /&gt;170 NOW YOU SEE HIM NOW YOU DONT&lt;br /&gt;171 DONT GO IN THE HOUSE&lt;br /&gt;172 THE HOUSE OF THE DEAD&lt;br /&gt;173 DEAD END&lt;br /&gt;174 END OF DAYS&lt;br /&gt;175 DAYS OF HEAVEN&lt;br /&gt;176 HEAVEN CAN WAIT&lt;br /&gt;177 WAIT UNTIL DARK&lt;br /&gt;178 DARK CITY&lt;br /&gt;179 CITY BY THE SEA&lt;br /&gt;180 SEA OF LOVE&lt;br /&gt;181 LOVE LIFE&lt;br /&gt;182 LIFE IS BEAUTIFUL&lt;br /&gt;183 BEAUTIFUL PEOPLE&lt;br /&gt;184 PEOPLE I KNOW&lt;br /&gt;185 I KNOW WHAT YOU DID LAST SUMMER&lt;br /&gt;186 SUMMER CATCH&lt;br /&gt;187 CATCH A FIRE&lt;br /&gt;188 FIRE ON THE MOUNTAIN&lt;br /&gt;189 THE MOUNTAIN MEN&lt;br /&gt;190 MEN WITH GUNS&lt;br /&gt;191 GUNS OF THE MAGNIFICENT SEVEN&lt;br /&gt;192 THE MAGNIFICENT SEVEN&lt;br /&gt;193 THE MAGNIFICENT SEVEN RIDE&lt;br /&gt;194 RIDE THE HIGH COUNTRY&lt;br /&gt;195 COUNTRY LIFE&lt;br /&gt;196 LIFE WITH FATHER&lt;br /&gt;197 FATHER OF THE BRIDE&lt;br /&gt;198 BRIDE OF THE MONSTER&lt;br /&gt;199 MONSTER HOUSE&lt;br /&gt;200 HOUSE PARTY 2&lt;br /&gt;201 2 DAYS IN THE VALLEY&lt;br /&gt;202 VALLEY GIRL&lt;br /&gt;203 GIRL IN THE CADILLAC&lt;br /&gt;204 CADILLAC MAN&lt;br /&gt;205 MAN ON FIRE&lt;br /&gt;206 FIRE IN THE SKY&lt;br /&gt;207 SKY HIGH&lt;br /&gt;208 HIGH SCHOOL HIGH&lt;br /&gt;209 HIGH CRIMES&lt;br /&gt;210 CRIMES OF PASSION&lt;br /&gt;211 PASSION IN THE DESERT&lt;br /&gt;212 DESERT BLUE&lt;br /&gt;213 BLUE STEEL&lt;br /&gt;214 STEEL DAWN&lt;br /&gt;215 DAWN OF THE DEAD&lt;br /&gt;216 DEAD HEAT&lt;br /&gt;217 HEAT AND DUST&lt;br /&gt;218 DUST TO GLORY&lt;br /&gt;219 GLORY ROAD&lt;br /&gt;220 ROAD GAMES&lt;br /&gt;221 GAMES PEOPLE PLAY NEW YORK&lt;br /&gt;222 NEW YORK COP&lt;br /&gt;223 COP LAND&lt;br /&gt;224 LAND OF THE DEAD&lt;br /&gt;225 THE DEAD POOL&lt;br /&gt;225 titles in chain 3203.74s&lt;/pre&gt;I've got a few other ideas and strategies (&lt;strike&gt;&lt;a href="http://en.wikipedia.org/wiki/Beam_search"&gt;beam search&lt;/a&gt;&lt;/strike&gt; &lt;a href="http://en.wikipedia.org/wiki/Beam_stack_search"&gt;beam-stack search&lt;/a&gt; for instance) I'd like to try to see if I can get an even longer chain, but I should probably re-implement the code in Lisp to make it faster first. 50 minutes is a tad on the long side ;-)&lt;br /&gt;&lt;br /&gt;[Update 2008-05-12] I just did some more extensive Googling and my results are not that good in comparison to the &lt;a href="http://reddit.com/info/2r7qw/comments/c03rq5c"&gt;312 titles long chain found by icefox&lt;/a&gt;. Back to work.&lt;br /&gt;&lt;br /&gt;[Correction 2008-07-02]: When I say beam search I mean beam-stack search.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-6734938916093866092?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/6734938916093866092/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=6734938916093866092' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/6734938916093866092'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/6734938916093866092'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/05/sling-blade-runner.html' title='Sling Blade Runner'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-3493053511381312777</id><published>2008-05-07T10:32:00.001+02:00</published><updated>2008-05-07T10:47:50.630+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><title type='text'>Implied deftemplates considered harmful</title><content type='html'>&lt;a href="http://clipsrules.sourceforge.net/"&gt;CLIPS&lt;/a&gt; is an extremely flexible and adaptive programming environment. Unfortunately, that's not always for the best.&lt;br /&gt;&lt;br /&gt;Implied deftemplates (see chapter 3.4 in &lt;a href="http://clipsrules.sourceforge.net/documentation/v624/bpg.htm"&gt;CLIPS Basic Programming Guide&lt;/a&gt;) can be a nuisance when debugging. It makes a difficult task even more difficult because certain, *trivial*, errors (more specifically, spelling errors) can go undetected. It's a bit similar to &lt;a href="http://en.wikipedia.org/wiki/Weak_typing"&gt;weak typing&lt;/a&gt;, though usually not as bad.&lt;br /&gt;&lt;br /&gt;I've learned (the hard way) to stay clear from ordered facts and implied deftemplates but if you still do use them and your application isn't really behaving as you expect. Try a &lt;span style="font-family:courier new;"&gt;(list-deftemplates)&lt;/span&gt; as your first debugging effort. You may well be surprised.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-3493053511381312777?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/3493053511381312777/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=3493053511381312777' title='7 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/3493053511381312777'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/3493053511381312777'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/05/implied-deftemplates-considered-harmful.html' title='Implied deftemplates considered harmful'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-7708344003403957347</id><published>2008-05-04T11:30:00.000+02:00</published><updated>2008-05-04T11:32:06.966+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><title type='text'>12.3.6 Evaluating a Construct within a String</title><content type='html'>The &lt;a href="http://clipsrules.sourceforge.net/documentation/v624/bpg.htm"&gt;CLIPS Basic Programming Guide&lt;/a&gt; is (more or less) the only reference you'll ever need for your CLIPS programming needs. It is however sometimes a bit brief. Like the section describing how to add constructs at run-time:&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;blockquote&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;12.3.6 Evaluating a Construct within a String&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The build function evaluates the string as though it were entered at the command prompt.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Syntax&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;(build  [string-or-symbol-expression])&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;where the only argument is the construct to be added. The return value is TRUE if the construct was added (or FALSE if an error occurs).&lt;br /&gt;&lt;br /&gt;The build function is not available for binary-load only or run-time CLIPS configurations (see the Advanced Programming Guide).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Example&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;CLIPS&gt; (clear)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;CLIPS&gt; (build "(defrule foo (a) =&gt; (assert (b)))")&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;TRUE&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;CLIPS&gt; (rules)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;foo&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;For a total of 1 rule.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;CLIPS&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/blockquote&gt;It could use a few more examples. Or at least one that is slightly more complex. Personally, I've never used &lt;span style="font-family:courier new;"&gt;build&lt;/span&gt; without wrapping it in a deffunction. But that also introduces the complexity of handling several &lt;span style="font-family:courier new;"&gt;str-cat&lt;/span&gt; calls to build up the definition of the construct in a variable.&lt;br /&gt;&lt;br /&gt;Almost all such builder-deffunctions that I've made follow the same basic template&lt;pre&gt;|CLIPS&gt; (deffunction build-foo-deftemplate (?n)&lt;br /&gt;|  (bind ?templ (str-cat "(deftemplate foo (slot id)"))&lt;br /&gt;|  (loop-for-count (?i ?n)&lt;br /&gt;|    (bind ?templ (str-cat ?templ&lt;br /&gt;|                          " (slot slot" ?i ")")))&lt;br /&gt;|  (bind ?templ (str-cat ?templ ")"))&lt;br /&gt;|  (build ?templ))&lt;br /&gt;|CLIPS&gt; (build-foo-deftemplate 3)&lt;br /&gt;|TRUE&lt;br /&gt;|CLIPS&gt; (ppdeftemplate foo)&lt;br /&gt;|(deftemplate MAIN::foo&lt;br /&gt;|   (slot id)&lt;br /&gt;|   (slot slot1)&lt;br /&gt;|   (slot slot2)&lt;br /&gt;|   (slot slot3))&lt;br /&gt;|CLIPS&gt;&lt;/pre&gt;Granted, this example might not be very useful but at least it shows the steps required to incrementally build up a definition in a variable. It's easy to get lost in all of those extra parens but if you've got a decent editor it can match them up for you even though they're embedded in strings.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-7708344003403957347?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/7708344003403957347/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=7708344003403957347' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7708344003403957347'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7708344003403957347'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/05/1236-evaluating-construct-within-string.html' title='12.3.6 Evaluating a Construct within a String'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-5070404480659212226</id><published>2008-04-28T10:32:00.000+02:00</published><updated>2008-04-28T10:32:12.870+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='CodeKata'/><title type='text'>KataMinesweeper</title><content type='html'>I've been playing around with a few of the katas at &lt;a href="http://www.codingdojo.org/cgi-bin/wiki.pl?KataCatalogue"&gt;codingdojo.org&lt;/a&gt; these past few days. I'm new to the concept but I like the &lt;a href="http://en.wikipedia.org/wiki/Kata_%28programming%29"&gt;idea&lt;/a&gt; and since I'm going to attend a kata session led by Emily in a couple of weeks I thought I'd prepare.&lt;br /&gt;&lt;br /&gt;Here's my best-so-far attempt at &lt;a href="http://www.codingdojo.org/cgi-bin/wiki.pl?KataMinesweeper"&gt;KataMinesweeper&lt;/a&gt;:&lt;pre&gt;|def convert(rows, cols, field):&lt;br /&gt;|    """&lt;br /&gt;|    &gt;&gt;&gt; print convert(1,1,['*'])&lt;br /&gt;|    *&lt;br /&gt;|    &gt;&gt;&gt; print convert(2,2,['*.', '..'])&lt;br /&gt;|    *1&lt;br /&gt;|    11&lt;br /&gt;|    &gt;&gt;&gt; print convert(4,4,['*...', '....', '.*..', '....'])&lt;br /&gt;|    *100&lt;br /&gt;|    2210&lt;br /&gt;|    1*10&lt;br /&gt;|    1110&lt;br /&gt;|    &gt;&gt;&gt; print convert(3,5,['**...', '.....', '.*...'])&lt;br /&gt;|    **100&lt;br /&gt;|    33200&lt;br /&gt;|    1*100&lt;br /&gt;|    """&lt;br /&gt;| &lt;br /&gt;|    _field = dict(zip(range(rows), [dict(zip(range(cols), f)) for f in field]))&lt;br /&gt;| &lt;br /&gt;|    for r in range(rows):&lt;br /&gt;|        for c in range(cols):&lt;br /&gt;|            if _field[r][c] == '.':&lt;br /&gt;|                count = 0&lt;br /&gt;|                for _r,_c in [(r-1,c-1), (r-1,c), (r-1,c+1), (r,c-1), (r,c+1), (r+1,c-1), (r+1,c), (r+1,c+1)]:&lt;br /&gt;|                    try:&lt;br /&gt;|                        if _field[_r][_c] == '*':&lt;br /&gt;|                            count += 1&lt;br /&gt;|                    except:&lt;br /&gt;|                        pass&lt;br /&gt;|                     &lt;br /&gt;|                _field[r][c] = str(count)&lt;br /&gt;|             &lt;br /&gt;|    return "\n".join(["".join(f.values()) for f in _field.values()])&lt;br /&gt;| &lt;br /&gt;|def content(filename):&lt;br /&gt;|    f = open(filename)&lt;br /&gt;|    lines = f.readlines()&lt;br /&gt;|    f.close()&lt;br /&gt;| &lt;br /&gt;|    return lines&lt;br /&gt;| &lt;br /&gt;|def solve(lines):&lt;br /&gt;|    """&lt;br /&gt;|    &gt;&gt;&gt; solve(['4 4\\n','*...\\n','....\\n','.*..\\n','....\\n',&lt;br /&gt;|    ...        '3 5\\n','**...\\n','.....\\n','.*...\\n',&lt;br /&gt;|    ...        '0 0\\n'])&lt;br /&gt;|    Field #1:&lt;br /&gt;|    *100&lt;br /&gt;|    2210&lt;br /&gt;|    1*10&lt;br /&gt;|    1110&lt;br /&gt;|    [BLANKLINE]&lt;br /&gt;|    Field #2:&lt;br /&gt;|    **100&lt;br /&gt;|    33200&lt;br /&gt;|    1*100&lt;br /&gt;|    [BLANKLINE]&lt;br /&gt;|    """&lt;br /&gt;| &lt;br /&gt;|    l, n = 0, 1&lt;br /&gt;|    while True:&lt;br /&gt;|        rows, cols = map(int, lines[l].split())&lt;br /&gt;|        if rows == 0 and cols == 0:&lt;br /&gt;|            break&lt;br /&gt;|         &lt;br /&gt;|        print "Field #%s:" % n&lt;br /&gt;|        print convert(rows, cols, [line.strip() for line in lines[l+1:l+rows+1]])&lt;br /&gt;|        print&lt;br /&gt;|     &lt;br /&gt;|        n += 1&lt;br /&gt;|        l += rows+1&lt;br /&gt;|     &lt;br /&gt;|if __name__ == '__main__':&lt;br /&gt;|    import doctest&lt;br /&gt;|    doctest.testmod()&lt;br /&gt;| &lt;br /&gt;|    import sys&lt;br /&gt;|    solve(content(sys.argv[-1]))&lt;/pre&gt;and running it with the suggested test-case in a text-file (minesweeper.txt) gives the following output:&lt;pre&gt;|C:\Playground\Katas&gt;minesweeper.py minesweeper.txt&lt;br /&gt;|Field #1:&lt;br /&gt;|*100&lt;br /&gt;|2210&lt;br /&gt;|1*10&lt;br /&gt;|1110&lt;br /&gt;|&lt;br /&gt;|Field #2:&lt;br /&gt;|**100&lt;br /&gt;|33200&lt;br /&gt;|1*100&lt;/pre&gt;It needs more tests. I know ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-5070404480659212226?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/5070404480659212226/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=5070404480659212226' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/5070404480659212226'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/5070404480659212226'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/04/kataminesweeper.html' title='KataMinesweeper'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-1653956768932707409</id><published>2008-04-26T11:39:00.005+02:00</published><updated>2008-04-26T11:47:40.014+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pyRete'/><title type='text'>Another SVN update</title><content type='html'>This has been a slow week, the whole family has come down with the flu and I haven't been able to do that much.&lt;br /&gt;&lt;br /&gt;I've come a bit further though and now the code in the &lt;a href="http://code.google.com/p/pyrete/source/browse"&gt;SVN repo&lt;/a&gt; has a working execution environment and conflict resolver. There are still a lot of things to add and work out but it really feels as if the pieces are starting to fall into place.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-1653956768932707409?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/1653956768932707409/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=1653956768932707409' title='2 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/1653956768932707409'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/1653956768932707409'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/04/another-svn-update.html' title='Another SVN update'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-7991867446804766052</id><published>2008-04-21T10:14:00.000+02:00</published><updated>2008-04-21T10:15:11.657+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Debugging'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><title type='text'>Debugging in CLIPS</title><content type='html'>I spend part of my time at &lt;a href="http://groups.google.com/group/CLIPSESG"&gt;CLIPSESG&lt;/a&gt; and I try to help whenever I can. Usually that involves figuring out why a rule is, or isn't, firing and mostly it's a simple-enough error or an "obvious" mistake that causes the problem. The thing is that it's mostly real easy to find them as well. At least if you know where to look.&lt;br /&gt;&lt;br /&gt;One (very useful) way of looking at your CLIPS program is by using the built-in &lt;span style="font-weight: bold; font-style: italic;"&gt;facts&lt;/span&gt; and &lt;span style="font-weight: bold; font-style: italic;"&gt;matches&lt;/span&gt; functions (see chapter 13.4.1 &lt;span style="font-style: italic;"&gt;Displaying the Fact-List&lt;/span&gt; and chapter 13.6.4 &lt;span style="font-style: italic;"&gt;Displaying Matches for a Rule&lt;/span&gt; in the &lt;a href="http://clipsrules.sourceforge.net/documentation/v624/bpg.htm"&gt;CLIPS Basic Programming Guide&lt;/a&gt;). There are some things to think about though.&lt;br /&gt;&lt;br /&gt;Evaluating &lt;span style="font-family:courier new;"&gt;(facts)&lt;/span&gt; is tricky in the same way &lt;span style="font-family:courier new;"&gt;select * from Foo&lt;/span&gt; is tricky in SQL. You've got to make sure that when you've got enough facts in Working Memory you don't print them all in the shell. Mostly because it takes a while, but also because you can't scroll more than a few hundred lines. So if what you're interested in is printed early on, chances are you won't be able to see it anyway.&lt;br /&gt;&lt;br /&gt;The &lt;span style="font-style: italic; font-weight: bold;"&gt;matches&lt;/span&gt; function is equally verbose. Unfortunately there's no way to turn that verbosity off. If you've got a lot of facts matching some pattern in a rule they will all be printed to the shell and if you've got a lot of patterns with a lot of matching facts you're not going to be able to inspect all of the output in the shell. Luckily you can use the &lt;span style="font-style: italic; font-weight: bold;"&gt;dribble-on/off&lt;/span&gt; functions (see chapter 13.2.1 &lt;span style="font-style: italic;"&gt;Generating Trace Files&lt;/span&gt; in the &lt;a href="http://clipsrules.sourceforge.net/documentation/v624/bpg.htm"&gt;BPG&lt;/a&gt;) to copy output to a file. That way, you can use your favorite text editor to inspect the output instead.&lt;br /&gt;&lt;br /&gt;I should also mention breakpoints and &lt;span style="font-style: italic; font-weight: bold;"&gt;set-break&lt;/span&gt; (see chapter 13.6.5 &lt;span style="font-style: italic;"&gt;Setting a Breakpoint for a Rule&lt;/span&gt; in the &lt;a href="http://clipsrules.sourceforge.net/documentation/v624/bpg.htm"&gt;BPG&lt;/a&gt;). They're great if you need to inspect the application after it has done some processing but before it has finished. You can also use the poor-man's version: &lt;span style="font-family:courier new;"&gt;(run &lt;span style="font-style: italic;"&gt;n&lt;/span&gt;)&lt;/span&gt; if you're really certain on how many rule firings it takes to build up the problematic state. But I've found that I usually only use that when I'm in an exploratory mood and step through the application using &lt;span style="font-family:courier new;"&gt;(run 1)&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;It might take a while to become familiar with debugging in CLIPS but it's well worth the effort.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-7991867446804766052?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/7991867446804766052/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=7991867446804766052' title='2 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7991867446804766052'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7991867446804766052'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/04/debugging-in-clips.html' title='Debugging in CLIPS'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-5600203867317383484</id><published>2008-04-16T13:07:00.003+02:00</published><updated>2008-04-16T15:12:14.174+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='pyRete'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>SVN update</title><content type='html'>I haven't updated the &lt;a href="http://code.google.com/p/pyrete/source/browse"&gt;PyRete SVN repo&lt;/a&gt; for some time now. Mostly because I haven't really had any (working) code I wanted to share. But no more of that. Today I managed to compile:&lt;pre&gt;&gt;&gt;&gt; @prodsys.rule&lt;br /&gt;... def rule1(self, large = Large, medium = Medium, small = Small):&lt;br /&gt;...   if large(value == x, another == y) and \&lt;br /&gt;...      medium(value == y, another == z) and \&lt;br /&gt;...      small(value == z, another == x):&lt;br /&gt;...     pass&lt;/pre&gt;into a Rete Network. Seems to work as well ;-)&lt;br /&gt;&lt;br /&gt;I'm not saying I'm done or anything. It'll probably take a while to finish all of the details and write test cases for all the relevant variants of rules that can be expressed but I'm hoping that most of the tricky parts have been sorted out. And since the public engine functions (reset, watch etc) already have been implemented once. I figure it's easy to port them to the new code. It'll probably be boring but it should be easy enough.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-5600203867317383484?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/5600203867317383484/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=5600203867317383484' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/5600203867317383484'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/5600203867317383484'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/04/svn-update.html' title='SVN update'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-9020753188166095648</id><published>2008-04-15T20:47:00.003+02:00</published><updated>2008-04-15T20:56:47.222+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Fun Stuff'/><category scheme='http://www.blogger.com/atom/ns#' term='Gothenburg'/><title type='text'>Swedish Championships of Robotics</title><content type='html'>This weekend, the University of Chalmers is hosting the &lt;a href="http://www.robotsm.se/?l=en"&gt;Swedish Championships of   Robotics&lt;/a&gt;. The whole family is going to go watch and it looks like it's going to be a killer event. I can't wait!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-9020753188166095648?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/9020753188166095648/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=9020753188166095648' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/9020753188166095648'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/9020753188166095648'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/04/swedish-championships-of-robotics.html' title='Swedish Championships of Robotics'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-1522373002856954060</id><published>2008-04-11T22:26:00.002+02:00</published><updated>2008-04-13T18:08:43.524+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='pyRete'/><category scheme='http://www.blogger.com/atom/ns#' term='Rule compiler'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>AST to AST translation in Python</title><content type='html'>In an attempt to further simplify PyRete's rule compiler I've split it into three parts.&lt;br /&gt;&lt;br /&gt;1) Pre-processing (splits a rule into subrules, if necessary),&lt;br /&gt;2) macro-expansion (translates, for example, &lt;span style="font-family:courier new;"&gt;Exists(CE*)&lt;/span&gt; into &lt;span style="font-family:courier new;"&gt;Not(Not(CE*))&lt;/span&gt;) and&lt;br /&gt;3) Rete Network construction (creates Production-, Alpha- and BetaNodes and joins them together).&lt;br /&gt;&lt;br /&gt;The complexity of trying to handle all of that in one class just got out of hand. Also, I think this design will make PyRete a lot more flexible and easier to extend with custom constructs (given that they are not violating Python's syntax that is).&lt;br /&gt;&lt;br /&gt;Anyway, the idea is that the Rete Network construction (step 3) should only have to deal with constructs that can be translated directly into a node in the Rete Network and that everything else should be handled in either of the two previous steps. This means, among other things, that in step 3 there'll only be Object-Pattern-CEs, Test-CEs and Not-CEs left.&lt;br /&gt;&lt;br /&gt;I'll talk about parts of step 2 (the macro-expansion) since I think it's the most interesting part.&lt;br /&gt;&lt;br /&gt;Unfortunately there isn't that much written about AST to AST translation in Python. Not very surprising considering that it's not generally the level of abstraction most people decide to interfer with the compilation-chain. Most attempts either cover the whole thing, from front to back, like &lt;a href="http://www.dalkescientific.com/Python/python4ply.html"&gt;python4ply&lt;/a&gt; and &lt;a href="http://www.fiber-space.de/EasyExtend/doc/EE.html"&gt;EasyExtend&lt;/a&gt; or perform low-level bytecode manipulation as &lt;a href="http://sourceforge.net/projects/bytecodehacks/"&gt;bytecodehacks&lt;/a&gt; and &lt;a href="http://psyco.sourceforge.net/"&gt;psyco&lt;/a&gt;. Even the &lt;a href="http://docs.python.org/lib/compiler.html"&gt;Python docs for the compiler module&lt;/a&gt; and its &lt;a href="http://docs.python.org/lib/module-compiler.visitor.html"&gt;AST visitor class&lt;/a&gt; is quite sparse to say the least.&lt;br /&gt;&lt;br /&gt;Luckily I've spent more than a year poking around on the AST level of Python code so I'm used to trial and error and having exceptions thrown in my face every now and then. The translation scheme I've implemented is not as smooth as having Common Lisp or Scheme macros but it does the job.&lt;br /&gt;&lt;br /&gt;Here's the code for handling the Exists-CE:&lt;pre&gt;&gt;&gt;&gt; class ExistsCE(object):&lt;br /&gt;...   @classmethod&lt;br /&gt;...   def expand(self, node):&lt;br /&gt;...     nodesToVisit = [node]&lt;br /&gt;...     while len(nodesToVisit) &gt; 0:&lt;br /&gt;...       curr = nodesToVisit.pop(0)&lt;br /&gt;...       for child in curr.getChildNodes():&lt;br /&gt;...         nodesToVisit.append(child)&lt;br /&gt;...       if isinstance(curr, CallFunc) and curr.node.name == "Exists":&lt;br /&gt;...         curr.node.name = "Not"&lt;br /&gt;...         curr.args = [CallFunc(Name("Not"), curr.args)]&lt;br /&gt;...     return node&lt;/pre&gt;and here's a sample usage (I've highlighted the interesting parts).&lt;pre&gt;&gt;&gt;&gt; ast = compiler.parse("Exists(Spam(eggs == 1, bacon = 2))")&lt;br /&gt;&gt;&gt;&gt; print ast&lt;br /&gt;Module(None, Stmt([Discard(&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;CallFunc(Name('Exists')&lt;/span&gt;, [CallFunc(Name('Spam'), [Com&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;pare(Name('eggs'), [('==', Const(1))]), Keyword('bacon', Const(2))], None, None&lt;/span&gt;)&lt;br /&gt;], None, None))]))&lt;/pre&gt;and if we run it through the ExistsCE macro expander we get:&lt;pre&gt;&gt;&gt;&gt; print ExistsCE.expand(ast)&lt;br /&gt;Module(None, Stmt([Discard(&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;CallFunc(Name('Not')&lt;/span&gt;, [&lt;span style="color: rgb(255, 0, 0);"&gt;CallFunc(Name('Not')&lt;/span&gt;, [CallFun&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;c(Name('Spam'), [Compare(Name('eggs'), [('==', Const(1))]), Keyword('bacon', Con&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;st(2))], None, None)], None, None&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;)], None, None))]))&lt;/pre&gt;which is exactly the same AST as if we had written two nested Not calls instead:&lt;pre&gt;&gt;&gt;&gt; print compiler.parse("Not(Not(Spam(eggs == 1, bacon = 2)))")&lt;br /&gt;Module(None, Stmt([Discard(&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;CallFunc(Name('Not')&lt;/span&gt;, [&lt;span style="color: rgb(255, 0, 0);"&gt;CallFunc(Name('Not')&lt;/span&gt;, [CallFun&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;c(Name('Spam'), [Compare(Name('eggs'), [('==', Const(1))]), Keyword('bacon', Con&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;st(2))], None, None)], None, None&lt;/span&gt;)], None, None))]))&lt;/pre&gt;I wonder if I can abstract away some of that boiler plate, make it more general and usable. I'll have to tinker some more but it shouldn't be impossible.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-1522373002856954060?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/1522373002856954060/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=1522373002856954060' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/1522373002856954060'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/1522373002856954060'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/04/ast-to-ast-translation-in-python.html' title='AST to AST translation in Python'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-6577875060558444677</id><published>2008-04-08T21:40:00.003+02:00</published><updated>2008-04-08T21:52:41.322+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Rule Engine'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><title type='text'>The Absolute Minimum Every CLIPS Developer Absolutely, Positively Must Know About the Not-CE and Template-Pattern-Constraints (No Excuses!)</title><content type='html'>Ok. So I stole the title from &lt;a href="http://www.joelonsoftware.com/articles/Unicode.html"&gt;Joel's very popular article about Unicode&lt;/a&gt;, but it's not &lt;span style="font-weight: bold;"&gt;just&lt;/span&gt; a way to get attention. The point is that there are some things about how &lt;a href="http://clipsrules.sourceforge.net/"&gt;CLIPS&lt;/a&gt; (and other Rule Engines) handle &lt;span style="font-family:courier new;"&gt;not&lt;/span&gt; that might not be all that obvious to programmers used to languages such as &lt;a href="http://www.python.org/"&gt;Python&lt;/a&gt; or &lt;a href="http://java.sun.com/"&gt;Java&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;More specifically, I'm talking about the difference between&lt;pre&gt;|CLIPS&gt; (defrule rule1&lt;br /&gt;|  &lt;span style="color: rgb(255, 0, 0); font-weight: bold;"&gt;(spam (eggs ?eggs) (bacon ?bacon))&lt;/span&gt;&lt;br /&gt;|  &lt;span style="color: rgb(255, 0, 0); font-weight: bold;"&gt;(test (not (and (eq ?eggs 1) (eq ?bacon 2))))&lt;br /&gt;&lt;/span&gt;|  =&gt;)&lt;/pre&gt;and&lt;pre&gt;|CLIPS&gt; (defrule rule2&lt;br /&gt;|  &lt;span style="color: rgb(255, 0, 0); font-weight: bold;"&gt;(not (spam (eggs 1) (bacon 2))&lt;/span&gt;&lt;br /&gt;|  =&gt;)&lt;br /&gt;&lt;/pre&gt;You're not alone if you've ever made the mistake of thinking that wrapping a template-pattern-CE with a &lt;span style="font-family:courier new;"&gt;not&lt;/span&gt; would negate the constraints inside it. That's, however, not what the Not-CE does.&lt;br /&gt;&lt;br /&gt;A logical not is the simplest possible operator. It simply returns &lt;span style="font-family:courier new;"&gt;True&lt;/span&gt; if the expression it applies to returns &lt;span style="font-family:courier new;"&gt;False&lt;/span&gt; and vice versa. In CLIPS, it's easy enough to figure out what expression a &lt;span style="font-family:courier new;"&gt;not&lt;/span&gt; applies to, but you might be surprised at how that expression is evaluated?&lt;br /&gt;&lt;br /&gt;The Not-CE evaluates to &lt;span style="font-family:courier new;"&gt;True&lt;/span&gt; if there are &lt;span&gt;no facts in Working Memory matching the pattern&lt;/span&gt; it applies to. You should think of &lt;span style="font-family:courier new;"&gt;not&lt;/span&gt; as short hand for &lt;span style="font-style: italic;"&gt;Not-in-Working-Memory&lt;/span&gt; and not as a Not-Equals variant. I know it's not at all that obvious since there are no hints (compare Java's &lt;span style="font-family:courier new;"&gt;!=&lt;/span&gt;) but it should make it's behaviour less of a surprise.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-6577875060558444677?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/6577875060558444677/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=6577875060558444677' title='2 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/6577875060558444677'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/6577875060558444677'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/04/absolute-minimum-every-clips-developer.html' title='The Absolute Minimum Every CLIPS Developer Absolutely, Positively Must Know About the Not-CE and Template-Pattern-Constraints (No Excuses!)'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-4484823460182481253</id><published>2008-04-05T13:52:00.003+02:00</published><updated>2008-04-05T14:24:01.931+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Humour'/><title type='text'>Python as a boat</title><content type='html'>There are a lot of funny what-kind-of-X-would-programming-language-Y-be-lists floating around on the internet. And, here's another one from &lt;a href="http://compsci.ca/blog/if-a-programming-language-was-a-boat/"&gt;CompSci.ca/blog&lt;/a&gt;. I especially like &lt;a href="http://compsci.ca/blog/if-a-programming-language-was-a-boat/#comment-103284"&gt;jpc's description of Python&lt;/a&gt; (in the comments):&lt;br /&gt;&lt;br /&gt;"&lt;span style="font-style: italic;"&gt;Python would be a catamaran. Light and functional, with conspicuous spacing.&lt;/span&gt;"&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-4484823460182481253?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/4484823460182481253/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=4484823460182481253' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/4484823460182481253'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/4484823460182481253'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/04/python-as-boat.html' title='Python as a boat'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-8367422925856322198</id><published>2008-04-04T21:29:00.001+02:00</published><updated>2008-04-05T07:42:41.644+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Rete Algorithm'/><title type='text'>Another attempt at pattern matching</title><content type='html'>In my &lt;a href="http://commentsarelies.blogspot.com/2008/04/lisa-multislots-and-thoughts-about.html"&gt;previous post&lt;/a&gt; I'm talking about feature-tests without really explaining what I mean. So here's an attempt to re-phrase, expand on and give some background to my thoughts.&lt;br /&gt;&lt;br /&gt;I spent yesterday re-reading parts of &lt;a href="http://portal.acm.org/citation.cfm?id=908813"&gt;Forgy's thesis&lt;/a&gt; (using OPS2) and the &lt;a href="http://portal.acm.org/citation.cfm?id=115736"&gt;AI Journal article&lt;/a&gt; (using &lt;a href="http://www.scs.cmu.edu/afs/cs/project/ai-repository/ai/areas/expert/systems/ops5/0.html"&gt;OPS5&lt;/a&gt;) describing the Rete Algorithm.&lt;br /&gt;&lt;br /&gt;The implementation described in the thesis uses a whole lot of nodes to test for &lt;span style="font-style: italic;"&gt;features&lt;/span&gt; of the fact-structure as well as the values contained in a fact. This approach of classifying things is known as &lt;a href="http://en.wikipedia.org/wiki/Structural_typing"&gt;Structural Typing&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Compiling the MB11 production in OPS2 (see 2.2.1 &lt;span style="font-style: italic;"&gt;Productions with one Condition Element&lt;/span&gt; in the thesis):&lt;pre&gt;MB11 ( (Want (Monkey Near =P))&lt;br /&gt;       --&gt;&lt;br /&gt;      (Want (Monkey On Floor)) )&lt;/pre&gt;produces no less than five nodes in the Rete Network:&lt;br /&gt;&lt;br /&gt;1. Is the element a list of two subelements?&lt;br /&gt;2. Is the first subelement &lt;span style="font-style: italic;"&gt;Want&lt;/span&gt;?&lt;br /&gt;3. Is the second subelement a list of three subelements?&lt;br /&gt;4. Is the first subelement of the second subelement &lt;span style="font-style: italic;"&gt;Monkey&lt;/span&gt;?&lt;br /&gt;5. Is the second subelement of the second subelement &lt;span style="font-style: italic;"&gt;Near&lt;/span&gt;?&lt;br /&gt;&lt;br /&gt;The article, however, describes a different approach, based on &lt;a href="http://en.wikipedia.org/wiki/Type_system#Static_typing"&gt;Static Typing&lt;/a&gt;, which requires you to declare the structure of a fact before using it. This way, we can remove all feature-tests and replace them with only one test, an expression node (also known as an object type node).&lt;br /&gt;&lt;br /&gt;The MB11 production for OPS5 looks like this:&lt;pre&gt;(p mb11&lt;br /&gt; (goal ^status active ^type walk-to ^object [p])&lt;br /&gt;  --&gt;&lt;br /&gt; (make goal ^status active ^type on ^object floor))&lt;/pre&gt;They're not the same, I know.&lt;br /&gt;&lt;br /&gt;But still, from the description in the article this would be compiled into three nodes:&lt;br /&gt;&lt;br /&gt;1. Is the element's class &lt;span style="font-style: italic;"&gt;goal&lt;/span&gt;?&lt;br /&gt;2. Is the value of the &lt;span style="font-style: italic;"&gt;status&lt;/span&gt; attribute &lt;span style="font-style: italic;"&gt;active&lt;/span&gt;?&lt;br /&gt;3. Is the value of the &lt;span style="font-style: italic;"&gt;type&lt;/span&gt; attribute &lt;span style="font-style: italic;"&gt;walk-to&lt;/span&gt;?&lt;br /&gt;&lt;br /&gt;It doesn't take too large a rulebase before the benefits of this approach start to show. But at the same time, it seems that we have also lost the possibility to match nested structures.&lt;br /&gt;&lt;br /&gt;In OPS5, according to the &lt;a href="http://www.cs.gordon.edu/local/courses/cs323/OPS5/ops5.html"&gt;OPS5 Reference Manual&lt;/a&gt;, there's a possibility to create a vector attribute which can hold several values. Here's part of an OPS5 dialog that shows what happens when you match a vector attribute:&lt;pre&gt;OPS&gt; (vector-attribute bar)&lt;br /&gt;(BAR)&lt;br /&gt;OPS&gt; (literalize foo bar)&lt;br /&gt;NIL&lt;br /&gt;OPS&gt; (p rule&lt;br /&gt;       (foo ^bar [bar])&lt;br /&gt;        --&gt;&lt;br /&gt;       (write " [bar] = " [bar] (crlf)))&lt;br /&gt;*&lt;br /&gt;NIL&lt;br /&gt;OPS&gt; (make foo ^bar 1 2 3)&lt;br /&gt;NIL&lt;br /&gt;OPS&gt; (run)&lt;br /&gt;1. RULE 1 [bar] =  1&lt;/pre&gt;I haven't yet been able to match several values with one variable (as $? allows you to in CLIPS). I'm not sure if this is even possible in OPS5. Actually, in chapter 2.6 &lt;span style="font-style: italic;"&gt;The Range of Applicability of the Algorithm&lt;/span&gt; of Forgy's thesis he describes some of the constraints that we have to work around. In short, they are: 1) facts must only contain constants and 2) it must be possible to determine (at compile-time) which subelement each condition matches. This last constraint applies to Multifield variables and is probably one explanation to why they're so much slower than regular variables.&lt;br /&gt;&lt;br /&gt;My question in the previous post was: &lt;span style="font-style: italic;"&gt;why aren't nested data structures supported?&lt;/span&gt; Don't get me wrong. I'm not out to convince anyone to change anything or question whatever decisions has been made. I don't know if the issue is related to Multifield variables or not and I'm not sure I really care. The situation is as it is and CLIPS works just fine for my needs.&lt;br /&gt;&lt;br /&gt;But. I'd really, *really* love to learn more about the design decisions made in different production systems regarding this, and other, areas. If you know of papers, articles, books or magazines that contain information about OPS, CLIPS and/or ART history please let me know.&lt;br /&gt;&lt;br /&gt;[Update 2008-04-05]: The article &lt;i&gt;The OPS Languages - An Historical Overview&lt;/i&gt; in &lt;a href="http://www.pcai.com/web/issues/pcai_95_toc.html"&gt;this issue&lt;/a&gt; of PC AI magazine looks interesting enough to order.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-8367422925856322198?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/8367422925856322198/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=8367422925856322198' title='6 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8367422925856322198'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8367422925856322198'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/04/another-attempt-at-pattern-matching.html' title='Another attempt at pattern matching'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-320728202121931744</id><published>2008-04-02T16:02:00.001+02:00</published><updated>2008-04-02T16:28:38.306+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Rule Engine'/><category scheme='http://www.blogger.com/atom/ns#' term='Lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='LISA'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><title type='text'>LISA, Multislots and thoughts about Pattern matching</title><content type='html'>&lt;a href="http://sourceforge.net/projects/lisa"&gt;LISA&lt;/a&gt; has no multislots. That's just the way it is. You can add a list to a slot, but you have to match it as-is unless you provide a custom test-function.&lt;br /&gt;&lt;br /&gt;About a week ago, Pedro Kröger announced on the &lt;a href="http://sourceforge.net/mailarchive/forum.php?forum_name=lisa-users"&gt;Lisa Users mailing list&lt;/a&gt; that he was converting the &lt;a href="http://www.inf.u-szeged.hu/%7Ejelasity/migyak/zebra.clp"&gt;Zebra puzzle&lt;/a&gt; from &lt;a href="http://clipsrules.sourceforge.net/"&gt;CLIPS&lt;/a&gt; to LISA. The rule syntax is so similar between the engines that it ought to have been a quite straightforward and simple exercise. But, since the CLIPS solution uses implied deftemplates (which are implemented as multislots) the LISA program wouldn't work.&lt;br /&gt;&lt;br /&gt;This, and many other situations like it, has made me wonder about why nested fact structures aren't supported. Matching, on any level in a nested list, is actually described in Forgy's thesis (from 1979) but hasn't, for some reason, been implemented in most rule engines. Probably because it's faster to only allow flat structures.&lt;br /&gt;&lt;br /&gt;The Rete implementation described in the thesis constructs an awful lot of nodes but I'm not sure you need all of them. A rule compiler ought to be able to figure out which part of a structure to test without doing too many feature-tests ("is the second subelement a list of 3 subelements" etc).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-320728202121931744?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/320728202121931744/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=320728202121931744' title='12 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/320728202121931744'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/320728202121931744'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/04/lisa-multislots-and-thoughts-about.html' title='LISA, Multislots and thoughts about Pattern matching'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-9067745018671634675</id><published>2008-03-29T08:35:00.001+01:00</published><updated>2008-03-29T08:42:32.738+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><title type='text'>Utility for working with CSV-files in CLIPS</title><content type='html'>A couple of months ago I wrote a CLIPS function that could load and assert data from a &lt;a href="http://en.wikipedia.org/wiki/Comma-separated_values"&gt;CSV-file&lt;/a&gt;. It was simple but it worked well for my needs. When I started working on &lt;a href="http://clipsrules.wiki.sourceforge.net/"&gt;The CLIPS Cookbook&lt;/a&gt; a couple of weeks ago, a CSV-utility was one of the first things I thought about. Unfortunately, it turned out to be more difficult than I thought to produce an &lt;a href="http://tools.ietf.org/html/rfc4180"&gt;RFC4180&lt;/a&gt; compliant parser so the recipe has remained a placeholder. But, it won't much longer.&lt;br /&gt;&lt;br /&gt;Yesterday, I managed to get an implementation to work such that it can handle both import and export from and to CSV-files. I've still got some polishing to do but here's a short introduction to using it.&lt;br /&gt;&lt;br /&gt;Assuming we've got a set of link-facts in Working Memory:&lt;pre&gt;CLIPS&gt; (facts)&lt;br /&gt;f-0     (initial-fact)&lt;br /&gt;f-1     (link (title "CLIPSESG") (url "http://groups.google.com/group/CLIPSESG"))&lt;br /&gt;f-2     (link (title "CLIPS") (url "http://clipsrules.sourceforge.net/"))&lt;br /&gt;For a total of 3 facts.&lt;/pre&gt;We can export them to a CSV-file using the function CSV-export:&lt;pre&gt;CLIPS&gt; (load "csv.clp")&lt;br /&gt;:!!!!&lt;br /&gt;TRUE&lt;br /&gt;CLIPS&gt; (CSV-export "links.csv" link)&lt;br /&gt;2&lt;/pre&gt;The file links.csv looks like this afterwards:&lt;pre&gt;title,url&lt;br /&gt;"CLIPSESG","http://groups.google.com/group/CLIPSESG"&lt;br /&gt;"CLIPS","http://clipsrules.sourceforge.net/"&lt;/pre&gt;And if we instead want to load the link-facts from file we can use CSV-import:&lt;pre&gt;CLIPS&gt; (load "csv.clp")&lt;br /&gt;:!!!!&lt;br /&gt;TRUE&lt;br /&gt;CLIPS&gt; (CSV-import "links.csv" link)&lt;br /&gt;2&lt;br /&gt;CLIPS&gt; (facts)&lt;br /&gt;f-0     (initial-fact)&lt;br /&gt;f-1     (link (title "CLIPSESG") (url "http://groups.google.com/group/CLIPSESG"))&lt;br /&gt;f-2     (link (title "CLIPS") (url "http://clipsrules.sourceforge.net/"))&lt;br /&gt;For a total of 3 facts.&lt;/pre&gt;Both functions take the filename and the deftemplate as parameters. You can also specify which slots should be exported/imported but that's really not neccessary. However, the first row of the CSV-file must contain slot names (column headers)  if you import facts without specifying slots.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-9067745018671634675?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/9067745018671634675/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=9067745018671634675' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/9067745018671634675'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/9067745018671634675'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/03/utility-for-working-with-csv-files-in.html' title='Utility for working with CSV-files in CLIPS'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-8516473148705578242</id><published>2008-03-25T08:53:00.000+01:00</published><updated>2008-03-25T09:28:40.326+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Expert system'/><category scheme='http://www.blogger.com/atom/ns#' term='Rule Engine'/><category scheme='http://www.blogger.com/atom/ns#' term='Book'/><title type='text'>Expert Systems Book</title><content type='html'>I live about 50 m from the &lt;a href="http://www.chalmers.se/en/"&gt;University of Chalmers&lt;/a&gt; and, naturally, I make use of their library.&lt;br /&gt;&lt;br /&gt;During my visit last week I accidently found a very interesting 6-volume set of books named &lt;i&gt;Expert Systems: The Technology of Knowledge Management and Decision Making for the 21st Century&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;I'm currently reading the first volume and it is very good. It's a bit theoretical, but I can really recommend it to anyone interested in Expert Systems. Here's a short list of selected chapters to whet your appetite:&lt;br /&gt;&lt;br /&gt;Volume 1.&lt;br /&gt;Ch. 4 - Reasoning with Imperfect Information.&lt;br /&gt;&lt;br /&gt;Volume 2.&lt;br /&gt;Ch. 9 - Model of Reasoning with Conflicting Information Sources in Knowledge Based Systems.&lt;br /&gt;&lt;br /&gt;Volume 3.&lt;br /&gt;Ch. 24 - Cellular Automata Architectures for Pattern Recognition.&lt;br /&gt;&lt;br /&gt;Volume 4.&lt;br /&gt;Ch. 29 - Automatic Knowledge Discovery in Larger Scale Knowledge-Data Bases.&lt;br /&gt;Ch. 34 - Neural Networks for Economic Forecasting Problems.&lt;br /&gt;&lt;br /&gt;Volume 5.&lt;br /&gt;Ch. 37 - Hybrid Expert Systems: An Approach to Combining Neural Computation and Rule-Based Reasoning.&lt;br /&gt;Ch. 40 - Distributed Logic Processors in Process Indentification.&lt;br /&gt;&lt;br /&gt;Volume 6.&lt;br /&gt;Ch. 46 - Methodology for Building Case-Based Resaoning Systems in Ill-Structured Optimization Domains.&lt;br /&gt;&lt;br /&gt;There are 50 chapters all in all spanning almost 2000 pages so this is probably going to take me a while.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-8516473148705578242?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/8516473148705578242/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=8516473148705578242' title='5 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8516473148705578242'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8516473148705578242'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/03/expert-systems-book.html' title='Expert Systems Book'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-7419056632364973242</id><published>2008-03-21T09:59:00.004+01:00</published><updated>2008-03-21T11:15:10.659+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='pyRete'/><category scheme='http://www.blogger.com/atom/ns#' term='Rule compiler'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>1000 LOC removed</title><content type='html'>There's not many things that make me happier than being able to remove existing code. These past few days have been quite good in that respect.&lt;br /&gt;&lt;br /&gt;Previously I compiled a Rete Network from a &lt;a href="http://docs.python.org/lib/node884.html"&gt;Python AST&lt;/a&gt; by passing the rule function's AST to an ASTVisitor which returned a dictionary holding information about which nodes should be created. The dictionary was then passed to a method (construct_rete) which was in charge of creating the Rete Network and figuring out in what way the nodes should be connected.&lt;br /&gt;&lt;br /&gt;That way of doing it turned out to be an ugly beast of complexity which managed to resist all of my attempts to make it simpler. I spent more than half a year trying before I binned it completely and yesterday I updated &lt;a href="http://code.google.com/p/pyrete/source/browse"&gt;PyRete's SVN repository&lt;/a&gt; with the code I've got so far.&lt;br /&gt;&lt;br /&gt;The approach I use now is that I pass the rule function's AST to an ASTVisitor which *directly* calls the Rete implementation. This way I don't have to go through all of the trouble of maintaining a dictionary with intermediate details and the result is that I've managed to get rid of about 1000 lines of code and the compilation process is more or less understandable.&lt;br /&gt;&lt;br /&gt;"&lt;span style="font-style: italic;"&gt;K-I-S-S. Keep it simple, stupid. Great advice. Hurts my feelings every time.&lt;/span&gt;"    -Dwight Schrute&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-7419056632364973242?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/7419056632364973242/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=7419056632364973242' title='1 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7419056632364973242'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7419056632364973242'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/03/1000-loc-removed.html' title='1000 LOC removed'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-5036381530712342493</id><published>2008-03-14T22:27:00.003+01:00</published><updated>2008-03-14T22:55:08.184+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Mathematics'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>I hate mathematics</title><content type='html'>Via &lt;a href="http://metabang.com/unclogit/?p=267"&gt;Gary King&lt;/a&gt;, I found this little weirdness, called the 6174 problem.&lt;br /&gt;&lt;ol style="font-style: italic;"&gt;&lt;li&gt;Pick any four distinct digits (e.g., 6, 2, 9, 0)&lt;/li&gt;&lt;li&gt;arrange them into the largest possible integer (e.g., 9620)&lt;/li&gt;&lt;li&gt;arrange them into the smallest (e.g., 0269)&lt;/li&gt;&lt;li&gt;subtract the smaller from the larger (e.g., (- 9620 0269) =&gt; 9351)&lt;/li&gt;&lt;li&gt;go back to step 2 and repeat&lt;/li&gt;&lt;/ol&gt;The cool thing is that, regardless of which digits you choose, you will end up with 6174. Here's the sequence for the digits 6, 2, 9, 0:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;9620 - 0269 = 9351&lt;br /&gt;9531 - 1359 = 8172&lt;br /&gt;8721 - 1278 = 7443&lt;br /&gt;7443 - 3447 = 3996&lt;br /&gt;9963 - 3699 = 6264&lt;br /&gt;6642 - 2466 = 4176&lt;br /&gt;&lt;/code&gt;&lt;code&gt;7641 - 1467 = 6174&lt;/code&gt;&lt;br /&gt;&lt;code&gt;7641 - 1467 = 6174&lt;/code&gt;  ...&lt;br /&gt;&lt;/pre&gt;Apparently that's the longest one, I haven't bothered to check myself but I did translate Gary's Lisp code to Python. I took the memoize implementation straight from the &lt;a href="http://wiki.python.org/moin/PythonDecoratorLibrary#head-11870a08b0fa59a8622201abfac735ea47ffade5"&gt;Python Decorator Library&lt;/a&gt;. The &lt;a href="http://www.pulp.se/python/6174.py"&gt;whole thing&lt;/a&gt; is about 100 lines (which includes doctests).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-5036381530712342493?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/5036381530712342493/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=5036381530712342493' title='1 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/5036381530712342493'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/5036381530712342493'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/03/i-hate-mathematics.html' title='I hate mathematics'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-7403049232959716166</id><published>2008-03-14T21:21:00.000+01:00</published><updated>2008-03-14T21:22:33.007+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><category scheme='http://www.blogger.com/atom/ns#' term='Cookbook'/><title type='text'>ANN: CLIPS Cookbook</title><content type='html'>The CLIPS Cookbook is now available at &lt;a href="http://clipsrules.wiki.sourceforge.net/"&gt;http://clipsrules.wiki.sourceforge.net/&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;At this point it only includes 5 recipes but I hope that, with a little help and given time, it will grow into a valuable resource as a complement to the CLIPS documentation.&lt;br /&gt;&lt;br /&gt;The Cookbook uses a public wiki which means that anyone can, and is encouraged to, add new or improve existing recipes. I will try to, periodically, add recipes based on what people ask about in &lt;a href="http://groups.google.com/group/CLIPSESG/"&gt;CLIPSESG&lt;/a&gt; but you don't have to wait for me or anyone else if you think something is missing. Just add it. If you don't feel comfortable writing a recipe yourself, please add a page for it anyway and indicate that you want someone to help you write it.&lt;br /&gt;&lt;br /&gt;All standard expectations of &lt;a href="http://en.wikipedia.org/wiki/Wikipedia:Etiquette"&gt;good wiki-behaviour&lt;/a&gt; applies so please behave.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-7403049232959716166?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/7403049232959716166/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=7403049232959716166' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7403049232959716166'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7403049232959716166'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/03/ann-clips-cookbook.html' title='ANN: CLIPS Cookbook'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-3859185659058825033</id><published>2008-03-11T23:38:00.003+01:00</published><updated>2008-03-12T00:18:54.848+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='pyRete'/><category scheme='http://www.blogger.com/atom/ns#' term='Rule compiler'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Yet another post about the RuleCompiler</title><content type='html'>There's still a lot of code to be written for the RuleCompiler but I came a bit further today. I've added print statements to the parser so I can trace the creation of the Rete Network.&lt;pre&gt;&gt;&gt;&gt; def rule1(self, large = Large, medium = Medium, small = Small):&lt;br /&gt;...   salience = 10&lt;br /&gt;...   if large(value == x and x &gt; 10, another == y) and \&lt;br /&gt;...      medium(value == y and y &gt; x, another == z) and \&lt;br /&gt;...      small(value == z):&lt;br /&gt;...     pass&lt;br /&gt;&lt;br /&gt;&gt;&gt;&gt; parse(rule1)&lt;br /&gt;# p = ProductionNode(rule = rule1)&lt;br /&gt;# p.doc = None&lt;br /&gt;# o1 = ObjectTypeNode(type = Small)&lt;br /&gt;# o2 = ObjectTypeNode(type = Medium)&lt;br /&gt;# o3 = ObjectTypeNode(type = Large)&lt;br /&gt;# p.salience = 10&lt;br /&gt;# x = large.value&lt;br /&gt;# a1 = AlphaNode(large.value &gt; 10)&lt;br /&gt;# y = large.another&lt;br /&gt;# j1 = JoinNode(large.another == medium.value)&lt;br /&gt;# a2 = AlphaNode(large.another &gt; large.value)&lt;br /&gt;# z = medium.another&lt;br /&gt;# j2 = JoinNode(medium.another == small.value)&lt;/pre&gt;I'm about 90% done. It took about 8h of programming, spread out during a week. I figure the last 10% will take at least as long. They always seem to do. But this is good. In my previous attempts it took me a &lt;span style="font-weight: bold;"&gt;lot&lt;/span&gt; longer to get this far.&lt;br /&gt;&lt;br /&gt;A couple of days ago &lt;a href="http://commentsarelies.blogspot.com/2008/03/progress-with-rulecompiler.html"&gt;I thought I could lose the fact bindings&lt;/a&gt; (the method parameters) but it occured to me today that they are needed if one or more of the Facts are to be referenced in the RHS. So, the bindings stay.&lt;br /&gt;&lt;br /&gt;And, regarding &lt;a href="http://commentsarelies.blogspot.com/2008/03/python4ply-vs-custom-rule-compiler.html"&gt;python4ply&lt;/a&gt; I've decided to do without it at the moment. The only thing I've found I really miss is the ability to use ? to distinguish a variable name from an attribute name. Without it there's no way a variable can have the same name as an attribute.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-3859185659058825033?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/3859185659058825033/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=3859185659058825033' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/3859185659058825033'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/3859185659058825033'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/03/yet-another-post-about-rulecompiler.html' title='Yet another post about the RuleCompiler'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-4917781316359537423</id><published>2008-03-10T10:49:00.005+01:00</published><updated>2008-03-10T10:59:47.781+01:00</updated><title type='text'>Funny thing</title><content type='html'>It turns out that &lt;a href="http://www.dalkescientific.com/writings/diary/"&gt;Andrew&lt;/a&gt; (who, among other things, have written &lt;a href="http://dalkescientific.com/Python/python4ply.html"&gt;python4ply&lt;/a&gt;) lives 200m away from me. I think I can see his apartment from my balcony ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-4917781316359537423?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/4917781316359537423/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=4917781316359537423' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/4917781316359537423'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/4917781316359537423'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/03/funny-thing.html' title='Funny thing'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-72466016921786255</id><published>2008-03-09T13:25:00.003+01:00</published><updated>2008-03-09T13:43:04.123+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='pyRete'/><category scheme='http://www.blogger.com/atom/ns#' term='Rule compiler'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>python4ply vs a custom rule compiler</title><content type='html'>Andrew Dalke released &lt;a href="http://www.dalkescientific.com/writings/diary/archive/2008/03/09/python4ply.html"&gt;python4ply 1.0&lt;/a&gt; today. It might be useful as part of the RuleCompiler since I wouldn't have to constrain the rule syntax to Python's.&lt;br /&gt;&lt;br /&gt;One of the main reasons for doing the rule compiler the way I am, is so I wouldn't have to re-implement a parser for Python's syntax but now that Andrew's gone and done just that... I don't know. This is from his announcement:&lt;br /&gt;&lt;blockquote style="font-style: italic;"&gt;  You might use python4ply to experiment with variations in the Python language.  The PLY-based lexer and parser are much easier to change than the C implementation Python itself uses or even the ones written in Python which are part of the standard library.&lt;/blockquote&gt; It would be cool to have support for other syntaxes as well... I'll have to think about it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-72466016921786255?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/72466016921786255/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=72466016921786255' title='5 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/72466016921786255'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/72466016921786255'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/03/python4ply-vs-custom-rule-compiler.html' title='python4ply vs a custom rule compiler'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-7678317901149961916</id><published>2008-03-07T16:20:00.000+01:00</published><updated>2008-03-07T16:45:32.599+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='pyRete'/><category scheme='http://www.blogger.com/atom/ns#' term='Rule compiler'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Progress with the RuleCompiler</title><content type='html'>Finally! It's starting to feel like I'm getting somewhere with PyRete's RuleCompiler. Earlier this week I decided to change quite a lot about the allowed syntax in a rule.&lt;br /&gt;&lt;br /&gt;I've always kind of thought I'd have to have support for expressing rules in many different ways. For instance, I've tried to support at least three different approaches to expressing:&lt;pre&gt;|CLIPS&gt;(defrule rule&lt;br /&gt;|  (foo (attribute ?attr&amp;amp;:(&gt; ?attr 10)))&lt;br /&gt;|  (bar (attribute ?attr))&lt;br /&gt;|  =&gt;)&lt;/pre&gt;This way, which used to be the preferred way, you would write the rule as if you were writing procedural code:&lt;br /&gt;&lt;pre&gt;&gt;&gt;&gt; @prodsys.rule&lt;br /&gt;... def rule(engine, foo = Foo, bar = Bar):&lt;br /&gt;...   if foo.attribute == bar.attribute and \&lt;br /&gt;...      foo.attribute &gt; 10:&lt;br /&gt;...     pass&lt;/pre&gt;Another way, which always felt a bit weird and unnatural to me, was to use a (logical) variable:&lt;pre&gt;&gt;&gt;&gt; @prodsys.rule&lt;br /&gt;... def rule(engine, foo = Foo, bar = Bar):&lt;br /&gt;...   if foo.attribute == attr and \&lt;br /&gt;...      bar.attribute == attr and \&lt;br /&gt;...      attr &gt; 10:&lt;br /&gt;...     pass&lt;/pre&gt;The problem with it, and the reason I don't like it, is that it's not all that clear what is going to happen. There's no way of telling that &lt;span style="font-style: italic;"&gt;attr&lt;/span&gt; is a &lt;span&gt;logical&lt;/span&gt; variable and that makes it look like it's three ordinary comparisons, but it's not. In any other context the code would raise an Exception because the local variable &lt;span style="font-style: italic;"&gt;attr&lt;/span&gt; is unbound and that's probably how most Python programmers will read it.&lt;br /&gt;&lt;br /&gt;I thought that the above would cause a lot of confusion which is why I implemented another way of expressing the same thing which looks a lot more like the pattern-matching syntax of CLIPS:&lt;br /&gt;&lt;pre&gt;&gt;&gt;&gt; @prodsys.rule&lt;br /&gt;... def rule(engine, foo = Foo, bar = Bar):&lt;br /&gt;...   if foo(attribute == attr and attr &gt; 10) and \&lt;br /&gt;...      bar(attribute == attr):&lt;br /&gt;...     pass&lt;/pre&gt;This is, of course, also slightly problematic but at least there's no interpretation of the code that completely shadows how it's being used in PyRete.&lt;br /&gt;&lt;br /&gt;It wasn't until I decided, quite recently, that I'll only support the last type of syntax (pattern matching-style) that I started to get somewhere with the RuleCompiler. It also meant that I could get rid of the object bindings in the parameter list.&lt;br /&gt;&lt;br /&gt;The above rule should be written as:&lt;pre&gt;&gt;&gt;&gt; @prodsys.rule&lt;br /&gt;... def rule(engine):&lt;br /&gt;...   if Foo(attribute == attr and attr &gt; 10) and \&lt;br /&gt;...      Bar(attribute == attr):&lt;br /&gt;...     pass&lt;/pre&gt;It's only too bad that it takes me so long to figure these things out.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-7678317901149961916?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/7678317901149961916/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=7678317901149961916' title='3 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7678317901149961916'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7678317901149961916'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/03/progress-with-rulecompiler.html' title='Progress with the RuleCompiler'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-3422003459353570958</id><published>2008-02-21T12:50:00.004+01:00</published><updated>2008-02-21T13:22:26.659+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Humour'/><title type='text'>Exercise 16.1 in PAIP</title><content type='html'>&lt;span style="font-style: italic;"&gt;Suppose you read the headline "Elvis Alive in Kalamazoo" in a tabloid newspaper to which you attribute a certainty factor of .01. If you combine certainties using &lt;a href="http://en.wikipedia.org/wiki/MYCIN"&gt;EMYCIN&lt;/a&gt;'s combination rule, how many more copies of the newspaper would you need to see before you were .95 certain Elvis is alive?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Hilarious!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-3422003459353570958?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/3422003459353570958/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=3422003459353570958' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/3422003459353570958'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/3422003459353570958'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/02/exercise-161-in-paip.html' title='Exercise 16.1 in PAIP'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-980761981038569003</id><published>2008-02-15T10:45:00.009+01:00</published><updated>2008-02-18T20:08:32.824+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AI'/><category scheme='http://www.blogger.com/atom/ns#' term='Rete Algorithm'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Rule Engine'/><category scheme='http://www.blogger.com/atom/ns#' term='Neural Networks'/><title type='text'>Evolvable Rules</title><content type='html'>I stumbled on &lt;a href="http://evolvablerules.org/"&gt;Evolvable Rules&lt;/a&gt; and &lt;a href="http://sourceforge.net/projects/reat/"&gt;REAT&lt;/a&gt; (Rete Evolution of Augmenting Topologies) yesterday whilst searching for interesting stuff to read. I'm not yet sure about what I think of the project but &lt;a href="http://gregbarton.blogspot.com/"&gt;Greg&lt;/a&gt; sure got my attention.&lt;br /&gt;&lt;br /&gt;The idea behind evolvable rules seem to be using Artificial Neural Network (&lt;a href="http://en.wikipedia.org/wiki/Artificial_neural_network"&gt;ANN&lt;/a&gt;) techniques to &lt;span style="font-style: italic;"&gt;generate&lt;/span&gt; a Rete network &lt;strike&gt;and some additional stuff like conflict resolution and the code needed for executing the RHS of a rule&lt;/strike&gt;.&lt;br /&gt;&lt;br /&gt;There's not that much info yet but Greg shares a few references that explain the technology he intends to use. Apart from Charles Forgy's Rete papers (the &lt;a href="http://portal.acm.org/citation.cfm?id=908813&amp;amp;coll=GUIDE&amp;amp;dl=GUIDE&amp;amp;CFID=16452396&amp;amp;CFTOKEN=74319302"&gt;thesis &lt;/a&gt;and the &lt;a href="http://portal.acm.org/citation.cfm?id=115710.115736"&gt;article&lt;/a&gt;) and the &lt;a href="http://en.wikipedia.org/wiki/Rete_algorithm"&gt;Wikipedia description of the Rete algorithm&lt;/a&gt; (which we have another Charles to thank for) he also refers to &lt;a href="http://nn.cs.utexas.edu/downloads/papers/stanley.ec02.pdf"&gt;Evolving Neural Networks through Augmenting Topologies&lt;/a&gt; by Kenneth O. Stanley and Risto Miikkulainen.&lt;br /&gt;&lt;br /&gt;I haven't read the last paper that thoroughly (skimmed it once) and I'm only just learning about ANN technology but it seems to me that this requires a &lt;span style="font-weight: bold;"&gt;lot&lt;/span&gt; of work. And, if I understand correctly, it might not be very good at producing a Rete network at all and since there are "simple" rules that can be used to construct a Rete network based on, for example, an Abstract Syntax Tree I don't really see the point. Apart from being really cool of course ;-)&lt;br /&gt;&lt;br /&gt;Anyway, I'm real interested to see how all this works out. I've had similar lines of thought myself after having read &lt;a href="http://www.ai.soc.i.kyoto-u.ac.jp/%7Eishida/pdf/kde94.pdf"&gt;An Optimization Algorithm for Production Systems&lt;/a&gt; by Toru Ishida last summer. It would be really neat to find a way to automatically optimize (restructure) a Rete network based on the contents of the working memory without having to pass through all Facts and re-evaulate the goal of the optimization continuously.&lt;br /&gt;&lt;br /&gt;The problem is that I have &lt;span style="font-weight: bold;"&gt;no idea&lt;/span&gt; of how, or even if, it can be accomplished. But I bet that if someone does it, it's probably going to have something to do with using &lt;strike&gt;ANN&lt;/strike&gt; GA techniques. I'm not holding my breath though.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-980761981038569003?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/980761981038569003/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=980761981038569003' title='13 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/980761981038569003'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/980761981038569003'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/02/evolvable-rules.html' title='Evolvable Rules'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-2618083237257286778</id><published>2008-02-12T12:02:00.000+01:00</published><updated>2008-02-12T17:26:31.721+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Backward Chaining'/><category scheme='http://www.blogger.com/atom/ns#' term='pyClips'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><title type='text'>Another backward chaining example from Jess In Action</title><content type='html'>This is the multi-level backward chaining example from &lt;a href="http://www.amazon.com/Jess-Action-Java-Rule-Based-Systems/dp/1930110898/sr=1-1/qid=1171483520/ref=pd_bbs_sr_1/102-0639444-0700914?ie=UTF8&amp;amp;s=books"&gt;Jess In Action&lt;/a&gt;. My implementation of backward chaining had some problems with the original example so &lt;a href="http://www.pulp.se/clips/pyclips/shell.py"&gt;here&lt;/a&gt; is a new version of the shell that does the job.&lt;pre&gt;CLIPS[001/01]&gt; (deffunction fetch-price-from-database (?number)&lt;br /&gt;CLIPS[001/02]:   (if (eq ?number 1)&lt;br /&gt;CLIPS[001/03]:    then (return $1.99))&lt;br /&gt;CLIPS[001/04]:   (return nil))&lt;br /&gt;CLIPS[002/01]&gt; (deffunction fetch-number-from-database (?name)&lt;br /&gt;CLIPS[002/02]:   (if (eq ?name waffles)&lt;br /&gt;CLIPS[002/03]:    then (return 1))&lt;br /&gt;CLIPS[002/04]:   (return 0))&lt;br /&gt;CLIPS[003/01]&gt; (defrule price-check&lt;br /&gt;CLIPS[003/02]:   (do-price-check ?name)&lt;br /&gt;CLIPS[003/03]:   (price ?name ?price)&lt;br /&gt;CLIPS[003/04]:   =&gt;&lt;br /&gt;CLIPS[003/05]:   (printout t "Price of " ?name " is " ?price crlf))&lt;br /&gt;CLIPS[004/01]&gt; (defrule find-price&lt;br /&gt;CLIPS[004/02]:   (need-price ?name ?)&lt;br /&gt;CLIPS[004/03]:   (item-number ?name ?number)&lt;br /&gt;CLIPS[004/04]:   =&gt;&lt;br /&gt;CLIPS[004/05]:   (bind ?price (fetch-price-from-database ?number))&lt;br /&gt;CLIPS[004/06]:   (assert (price ?name ?price)))&lt;br /&gt;CLIPS[005/01]&gt; (defrule find-item-number&lt;br /&gt;CLIPS[005/02]:   (need-item-number ?name ?)&lt;br /&gt;CLIPS[005/03]:   =&gt;&lt;br /&gt;CLIPS[005/04]:   (bind ?number (fetch-number-from-database ?name))&lt;br /&gt;CLIPS[005/05]:   (assert (item-number ?name ?number)))&lt;br /&gt;CLIPS[006/01]&gt; (do-backward-chaining item-number)&lt;br /&gt;; + MAIN::find-price/0&lt;br /&gt;nil&lt;br /&gt;CLIPS[007/01]&gt; (do-backward-chaining price)&lt;br /&gt;; + MAIN::price-check/0&lt;br /&gt;nil&lt;br /&gt;CLIPS[008/01]&gt; (ppdefrule price-check/0)&lt;br /&gt;(defrule MAIN::price-check/0&lt;br /&gt; (do-price-check ?name)&lt;br /&gt; =&gt;&lt;br /&gt; (assert (need-price ?name nil)))&lt;br /&gt;CLIPS[009/01]&gt; (ppdefrule find-price/0)&lt;br /&gt;(defrule MAIN::find-price/0&lt;br /&gt; (need-price ?name ?)&lt;br /&gt; =&gt;&lt;br /&gt; (assert (need-item-number ?name nil)))&lt;br /&gt;CLIPS[010/01]&gt; (reset)&lt;br /&gt;CLIPS[011/01]&gt; (assert (do-price-check waffles))&lt;fact-1&gt;&lt;br /&gt;CLIPS[012/01]&gt; (watch rules)&lt;br /&gt;CLIPS[013/01]&gt; (run)&lt;br /&gt;FIRE    1 price-check/0: f-1&lt;br /&gt;FIRE    2 find-price/0: f-2&lt;br /&gt;FIRE    3 find-item-number: f-3&lt;br /&gt;FIRE    4 find-price: f-2,f-4&lt;br /&gt;FIRE    5 price-check: f-1,f-5&lt;br /&gt;Price of waffles is $1.99&lt;br /&gt;CLIPS[014/01]&gt; (facts)&lt;br /&gt;f-0     (initial-fact)&lt;br /&gt;f-1     (do-price-check waffles)&lt;br /&gt;f-2     (need-price waffles nil)&lt;br /&gt;f-3     (need-item-number waffles nil)&lt;br /&gt;f-4     (item-number waffles 1)&lt;br /&gt;f-5     (price waffles $1.99)&lt;br /&gt;For a total of 6 facts.&lt;br /&gt;CLIPS[015/01]&lt;fact-1&gt;&gt;&lt;/fact-1&gt;&lt;/fact-1&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-2618083237257286778?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/2618083237257286778/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=2618083237257286778' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/2618083237257286778'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/2618083237257286778'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/02/another-backward-chaining-example-from.html' title='Another backward chaining example from Jess In Action'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-5695998068377229055</id><published>2008-02-05T16:08:00.000+01:00</published><updated>2008-02-05T18:30:05.033+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='pyClips'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><title type='text'>Implementing backward chaining in PyCLIPS II.</title><content type='html'>In my &lt;a href="http://commentsarelies.blogspot.com/2008/01/implementing-backward-chaining-in.html"&gt;previous post&lt;/a&gt; I described a proof-of-concept implementation for backward chaining in PyCLIPS. One of the drawbacks was that it doesn't work on deftemplates.&lt;br /&gt;&lt;br /&gt;Consider the factorial example again:&lt;pre&gt;CLIPS[001/01]&gt; (deftemplate factorial&lt;br /&gt;CLIPS[001/02]:   (slot v)&lt;br /&gt;CLIPS[001/03]:   (slot r))&lt;br /&gt;CLIPS[002/01]&gt; (defrule print-factorial-10&lt;br /&gt;CLIPS[002/02]:   (factorial (v 10) (r ?r1))&lt;br /&gt;CLIPS[002/03]:   =&gt;&lt;br /&gt;CLIPS[002/04]:   (printout t "The factorial of 10 is " ?r1 crlf))&lt;br /&gt;CLIPS[003/01]&gt; (defrule do-factorial&lt;br /&gt;CLIPS[003/02]:   (need-factorial (v ?x) (r nil))&lt;br /&gt;CLIPS[003/03]:   =&gt;&lt;br /&gt;CLIPS[003/04]:   (bind ?r 1)&lt;br /&gt;CLIPS[003/05]:   (bind ?n ?x)&lt;br /&gt;CLIPS[003/06]:   (while (&gt; ?n 1)&lt;br /&gt;CLIPS[003/07]:     (bind ?r (* ?r ?n))&lt;br /&gt;CLIPS[003/08]:     (bind ?n (- ?n 1)))&lt;br /&gt;CLIPS[003/09]:   (assert (factorial (v ?x) (r ?r))))&lt;br /&gt;[PYCLIPS] C09: unable to understand argument&lt;br /&gt;[PRNTUTIL2] Syntax Error:  Check appropriate syntax for defrule.&lt;br /&gt;ERROR:&lt;br /&gt;(defrule MAIN::do-factorial&lt;br /&gt;(need-factorial (&lt;br /&gt;CLIPS[004/01]&gt;&lt;/pre&gt;The problem is that the CE that matches need-factorial requires a deftemplate and we haven't provided one. The "solution" is to have the do-backward-chaining function define the neccessary deftemplate dynamically. So all you need to think of is that you &lt;span style="font-weight: bold;"&gt;must&lt;/span&gt; either call do-backward-chaining or define the appropriate need-template &lt;span style="font-weight: bold;"&gt;before&lt;/span&gt; defining any rules that try to match it.&lt;br /&gt;&lt;pre&gt;CLIPS[001/01]&gt; (deftemplate factorial&lt;br /&gt;CLIPS[001/02]:   (slot v)&lt;br /&gt;CLIPS[001/03]:   (slot r))&lt;br /&gt;CLIPS[002/01]&gt; (defrule print-factorial-10&lt;br /&gt;CLIPS[002/02]:   (factorial (v 10) (r ?r1))&lt;br /&gt;CLIPS[002/03]:   =&gt;&lt;br /&gt;CLIPS[002/04]:   (printout t "The factorial of 10 is " ?r1 crlf))&lt;br /&gt;CLIPS[003/01]&gt; (do-backward-chaining factorial)&lt;br /&gt;; + MAIN::need-factorial&lt;br /&gt;; + MAIN::print-factorial-10/0&lt;br /&gt;nil&lt;br /&gt;CLIPS[004/01]&gt; (defrule do-factorial&lt;br /&gt;CLIPS[004/02]:   (need-factorial (v ?x) (r nil))&lt;br /&gt;CLIPS[004/03]:   =&gt;&lt;br /&gt;CLIPS[004/04]:   (bind ?r 1)&lt;br /&gt;CLIPS[004/05]:   (bind ?n ?x)&lt;br /&gt;CLIPS[004/06]:   (while (&gt; ?n 1)&lt;br /&gt;CLIPS[004/07]:     (bind ?r (* ?r ?n))&lt;br /&gt;CLIPS[004/08]:     (bind ?n (- ?n 1)))&lt;br /&gt;CLIPS[004/09]:   (assert (factorial (v ?x) (r ?r))))&lt;br /&gt;CLIPS[005/01]&gt; (reset)&lt;br /&gt;CLIPS[006/01]&gt; (run)&lt;br /&gt;The factorial of 10 is 3628800&lt;br /&gt;CLIPS[007/01]&gt;&lt;/pre&gt;The code is &lt;a href="http://www.pulp.se/clips/pyclips/shell.py"&gt;here&lt;/a&gt;, if you're interested. I'm not saying it's done or anything and I should probably have cleaned it up a bit more but I've been battling a cold for two weeks now and there are tons of other interesting things to try out. So this will have to do, at least for now.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-5695998068377229055?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/5695998068377229055/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=5695998068377229055' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/5695998068377229055'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/5695998068377229055'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/02/implementing-backward-chaining-in.html' title='Implementing backward chaining in PyCLIPS II.'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-385310197371759735</id><published>2008-01-27T00:11:00.000+01:00</published><updated>2008-01-27T01:38:51.564+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='pyClips'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><title type='text'>Implementing backward chaining in PyCLIPS.</title><content type='html'>I have been having an interesting e-mail conversation with &lt;a href="http://almostearthling.blogspot.com/"&gt;Francesco&lt;/a&gt; about &lt;a href="http://pyclips.sourceforge.net/"&gt;PyCLIPS&lt;/a&gt; this last week. PyCLIPS is mostly geared towards adding CLIPS to Python programs but it can also be used to add Python functionality to CLIPS. I've been urging him to add his &lt;a href="http://pyclips.sourceforge.net/web/?q=node/19"&gt;Shell utility&lt;/a&gt; to the main package. The reason is because I think it would make a brilliant starting point for extensions. Regular expressions, backward chaining, database access and GUI capabilities are all good examples of what could be done.&lt;br /&gt;&lt;br /&gt;I've done a little proof-of-concept implementation of backward chaining to show the idea and to provide a basis for discussion of these kinds of things. There are still a &lt;span style="font-weight: bold;"&gt;lot&lt;/span&gt; of things to do if this experiment is ever going to be useful and it might not even be possible to reach decent functionality (but it might be worth exploring further?). However, there are ways around most things when you control the dialog window ;-)&lt;br /&gt;&lt;br /&gt;The general idea of the experiment is to implement something similar to how Jess handles backward chaining. Since there's no way to extend the syntax of CLIPS I can't add an explicit-CE and instead we'll have to make do with an &lt;span style="font-style: italic;"&gt;explicit&lt;/span&gt; function.&lt;pre&gt;&gt; (deffunction MAIN::explicit ($?rules)&lt;br /&gt;&gt;   (progn$ (?rule $?rules)&lt;br /&gt;&gt;      (python-call explicit ?rule)))&lt;/pre&gt;I'll explain how it works after we've looked at &lt;span style="font-style: italic;"&gt;do-backward-chaining&lt;/span&gt; and talked about some of the Python code.&lt;pre&gt;&gt; (deffunction MAIN::do-backward-chaining ($?templates)&lt;br /&gt;&gt;   (progn$ (?template $?templates)&lt;br /&gt;&gt;      (python-call do-backward-chaining ?template)))&lt;/pre&gt;I'm using Francesco's shell.py script as a basis for this experiment. I've modified it slightly though. When loaded it defines the above methods and attaches them to their Python counterpart using:&lt;pre&gt;_cm.RegisterPythonFunction(do_backward_chaining, "do-backward-chaining")&lt;br /&gt;_cm.RegisterPythonFunction(explicit, "explicit")&lt;/pre&gt;I've also made it such that whenever a user enters "(clear)" in the console the above functions are re-defined. I haven't found a good way of hooking into the clear command yet so if it's executed from a batch, this little hack won't do any good.&lt;br /&gt;&lt;br /&gt;OK, let's see how it "works". Here's the factorial example from Jess In Action:&lt;pre&gt;CLIPS[1/1]&gt; (defrule print-factorial-10&lt;br /&gt;CLIPS[1/2]:   (factorial 10 ?r1)&lt;br /&gt;CLIPS[1/3]:   =&gt;&lt;br /&gt;CLIPS[1/4]:   (printout t "The factorial of 10 is " ?r1 crlf))&lt;br /&gt;CLIPS[2/1]&gt; (defrule do-factorial&lt;br /&gt;CLIPS[2/2]:   (need-factorial ?x ?)&lt;br /&gt;CLIPS[2/3]:   =&gt;&lt;br /&gt;CLIPS[2/4]:   (bind ?r 1)&lt;br /&gt;CLIPS[2/5]:   (bind ?n ?x)&lt;br /&gt;CLIPS[2/6]:   (while (&gt; ?n 1)&lt;br /&gt;CLIPS[2/7]:     (bind ?r (* ?r ?n))&lt;br /&gt;CLIPS[2/8]:     (bind ?n (- ?n 1)))&lt;br /&gt;CLIPS[2/9]:   (assert (factorial ?x ?r)))&lt;br /&gt;CLIPS[3/1]&gt; (do-backward-chaining factorial)&lt;br /&gt;; + rule MAIN::print-factorial-10/0&lt;br /&gt;nil&lt;br /&gt;CLIPS[4/1]&gt; (reset)&lt;br /&gt;CLIPS[5/1]&gt; (run)&lt;br /&gt;The factorial of 10 is 3628800&lt;br /&gt;CLIPS[6/1]&gt;&lt;/pre&gt;The rules themselves are really not that interesting. What's interesting is that none of them ought to have been activated. And the reason they are is because of the function call to do-backward-chaining. We get a hint at what it has done on the line that says&lt;br /&gt;&lt;pre&gt;; + rule MAIN::print-factorial-10/0&lt;/pre&gt;What happens is that the do-backward-chaining function in CLIPS calls the do_backward_chaining function in Python which searches through all rules (in all modules) for CEs that match&lt;pre&gt;r"^\s*?\(%s\b(.*?)\)$" % (template)&lt;/pre&gt;(Yes. It's a Regular Expression. I know it's a bit hackish to do introspection work based on ppdefrule calls but it's not like there's any other option. At least not any that I can think of. If you know of a better way please let me know!)&lt;br /&gt;&lt;br /&gt;If it finds a CE that matches, in our case &lt;span style="font-style: italic;"&gt;(factorial 10 ?r1)&lt;/span&gt; in &lt;span style="font-style: italic;"&gt;print-factorial-10&lt;/span&gt;, it generates a new rule based on the one containing the match. It adds a "/0" to the name (making it &lt;span style="font-style: italic;"&gt;print-factorial-10/0&lt;/span&gt;), copies the LHS but removes the matching CE and constructs an RHS by taking the matching CE with a few modifications and placing it in an assert statement. The modifications it does is adding "need-" to the template name and replacing all variables with &lt;span style="font-style: italic;"&gt;nil&lt;/span&gt;. We can see the result with&lt;pre&gt;CLIPS[6/1]&gt; (ppdefrule print-factorial-10/0)&lt;br /&gt;(defrule MAIN::print-factorial-10/0&lt;br /&gt;=&gt;&lt;br /&gt;(assert (need-factorial 10 nil)))&lt;br /&gt;CLIPS[7/1]&gt;&lt;/pre&gt;So, now it's easy to see why we got a result after (reset) and (run). Here's the dialog again, with facts and rules watched.&lt;pre&gt;CLIPS[7/1]&gt; (watch facts)&lt;br /&gt;CLIPS[8/1]&gt; (watch rules)&lt;br /&gt;CLIPS[9/1]&gt; (reset)&lt;br /&gt;&lt;== f-0     (initial-fact)&lt;br /&gt;&lt;== f-1     (need-factorial 10 nil)&lt;br /&gt;&lt;== f-2     (factorial 10 3628800)&lt;br /&gt;==&gt; f-0     (initial-fact)&lt;br /&gt;CLIPS[10/1]&gt; (run)&lt;br /&gt;FIRE    1 print-factorial-10/0: f-0&lt;br /&gt;==&gt; f-1     (need-factorial 10 nil)&lt;br /&gt;FIRE    2 do-factorial: f-1&lt;br /&gt;==&gt; f-2     (factorial 10 3628800)&lt;br /&gt;FIRE    3 print-factorial-10: f-2&lt;br /&gt;The factorial of 10 is 3628800&lt;br /&gt;CLIPS[11/1]&gt;&lt;/pre&gt;This little hack does unfortunately not work with deftemplates unless you also define a &lt;span style="font-style: italic;"&gt;need-deftemplate&lt;/span&gt; as an exact copy. And, that just doesn't feel right. Also, if you've got a connected constraint in your CE, it &lt;span style="font-weight: bold;"&gt;will&lt;/span&gt; be ignored. I haven't been able to figure out a good way to communicate that type of constraint to the rule matching the need-fact so if you've got one please let me know.&lt;br /&gt;&lt;br /&gt;When it comes to &lt;span style="font-style: italic;"&gt;explicit&lt;/span&gt;, I've tried to keep it as simple as possible. I keep a list of all generated rules and when you execute&lt;pre&gt;CLIPS[11/1]&gt; (explicit print-factorial-10)&lt;br /&gt;; - rule MAIN::print-factorial-10/0&lt;/pre&gt;any generated rule for &lt;span style="font-style: italic;"&gt;print-factorial-10&lt;/span&gt; is taken away (undefruled).&lt;pre&gt;CLIPS[12/1]&gt; (reset)&lt;br /&gt;&lt;== f-0     (initial-fact)&lt;br /&gt;&lt;== f-1     (need-factorial 10 nil)&lt;br /&gt;&lt;== f-2     (factorial 10 3628800)&lt;br /&gt;==&gt; f-0     (initial-fact)&lt;br /&gt;CLIPS[13/1]&gt; (run)&lt;br /&gt;CLIPS[14/1]&gt;&lt;/pre&gt;and that's it really.&lt;br /&gt;&lt;br /&gt;That's all it does (for now at least). It's simple and it's not very elegant, but it works. I won't publish the code (yet) because I'm still trying to clean  it up. Trying to make it "easier" to build on. I'll share with anyone interested though (send me an e-mail). But I would like to hear if this is a stupid idea in general or if I've done something wrong somewhere. Is there something I havent thought of that might pop up later and turn out to be near impossible to fix?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-385310197371759735?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/385310197371759735/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=385310197371759735' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/385310197371759735'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/385310197371759735'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/01/implementing-backward-chaining-in.html' title='Implementing backward chaining in PyCLIPS.'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-7151308654925859976</id><published>2008-01-21T16:37:00.000+01:00</published><updated>2008-01-21T17:01:16.192+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='pyClips'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='wxPython'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><title type='text'>SudokuDemo.wx.py</title><content type='html'>About a week later than I had hoped I've finally managed to finish the Sudoku demo from &lt;a href="http://groups.google.com/group/CLIPSESG/browse_frm/thread/aa280fe6287d96d8"&gt;ClipsJNI&lt;/a&gt;. I've tried staying as close to the Java version as possible but there are a few differences. For example, I've added an Open File-button that allows you to load a puzzle from a text file. I've included a few sample puzzles in the zip. The format of the puzzle must be exactly as in the examples: 9 lines of 9 characters (and a newline) each. Characters "1", "2", "3" to "9" are added as-is, all other characters are "added" as blanks (I use "0" in my examples).&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_3hqf70Lx0Mk/R5S-gwQ5SwI/AAAAAAAAADE/7jHqjWp-7Po/s1600-h/SudokuDemo.gif"&gt;&lt;img style="cursor: pointer;" src="http://3.bp.blogspot.com/_3hqf70Lx0Mk/R5S-gwQ5SwI/AAAAAAAAADE/7jHqjWp-7Po/s320/SudokuDemo.gif" alt="" id="BLOGGER_PHOTO_ID_5157956943166917378" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I've still got some work to do before I upload this as a &lt;a href="http://sourceforge.net/projects/pyclips"&gt;pyClips&lt;/a&gt; example on SourceForge. I really want to clean up the code and try to make it as easy to understand as possible. I also intend to write a short introduction to using &lt;a href="http://www.wxpython.org"&gt;wxPython&lt;/a&gt; and pyClips together.&lt;br /&gt;&lt;br /&gt;The code is &lt;a href="http://www.pulp.se/clips/pyclips/SudokuDemo.zip"&gt;here&lt;/a&gt; if you want to have a premature look. If you find any bugs or peculiar behaviour please let me know. Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-7151308654925859976?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/7151308654925859976/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=7151308654925859976' title='4 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7151308654925859976'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7151308654925859976'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/01/sudokudemowxpy.html' title='SudokuDemo.wx.py'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_3hqf70Lx0Mk/R5S-gwQ5SwI/AAAAAAAAADE/7jHqjWp-7Po/s72-c/SudokuDemo.gif' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-7100254282509969075</id><published>2008-01-18T10:47:00.000+01:00</published><updated>2008-01-18T12:03:15.809+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='wxPython'/><title type='text'>A simple wx.grid.Grid example</title><content type='html'>I've had the opportunity to help Dereje with a &lt;a href="http://www.wxpython.org/"&gt;wxPython&lt;/a&gt; question this week. His question was how to add an image to a cell in a wx.Grid. The wxPython demo and docs are great (really) but are not always the easiest to understand and the wx.Grid examples are a bit over-complicated so I've made an implementation that's as simple as can be. Here goes.&lt;pre&gt;&gt;&gt;&gt; import wx&lt;br /&gt;&gt;&gt;&gt; import wx.grid&lt;br /&gt;&gt;&gt;&gt; class MyApp(wx.App):&lt;br /&gt;...     def OnInit(self):&lt;br /&gt;...         frame = wx.Frame(None, -1, title = "wx.Grid - Bitmap example")&lt;br /&gt;...         grid = wx.grid.Grid(frame)&lt;br /&gt;...         grid.CreateGrid(1,1)&lt;br /&gt;...         img = wx.Bitmap("&lt;a href="http://www.python.org/community/logos/"&gt;python-logo.png&lt;/a&gt;", wx.BITMAP_TYPE_PNG)&lt;br /&gt;...         imageRenderer = MyImageRenderer(img)&lt;br /&gt;...         grid.SetCellRenderer(0,0,imageRenderer)&lt;br /&gt;...         grid.SetColSize(0,img.GetWidth()+2)&lt;br /&gt;...         grid.SetRowSize(0,img.GetHeight()+2)&lt;br /&gt;...         frame.Show(True)&lt;br /&gt;...         return True&lt;br /&gt;&lt;br /&gt;&gt;&gt;&gt; class MyImageRenderer(wx.grid.PyGridCellRenderer):&lt;br /&gt;...     def __init__(self, img):&lt;br /&gt;...         wx.grid.PyGridCellRenderer.__init__(self)&lt;br /&gt;...         self.img = img&lt;br /&gt;...     def Draw(self, grid, attr, dc, rect, row, col, isSelected):&lt;br /&gt;...         image = wx.MemoryDC()&lt;br /&gt;...         image.SelectObject(self.img)&lt;br /&gt;...         dc.SetBackgroundMode(wx.SOLID)&lt;br /&gt;...         if isSelected:&lt;br /&gt;...             dc.SetBrush(wx.Brush(wx.BLUE, wx.SOLID))&lt;br /&gt;...             dc.SetPen(wx.Pen(wx.BLUE, 1, wx.SOLID))&lt;br /&gt;...         else:&lt;br /&gt;...             dc.SetBrush(wx.Brush(wx.WHITE, wx.SOLID))&lt;br /&gt;...             dc.SetPen(wx.Pen(wx.WHITE, 1, wx.SOLID))&lt;br /&gt;...         dc.DrawRectangleRect(rect)&lt;br /&gt;...         width, height = self.img.GetWidth(), self.img.GetHeight()&lt;br /&gt;...         if width &gt; rect.width-2:&lt;br /&gt;...             width = rect.width-2&lt;br /&gt;...         if height &gt; rect.height-2:&lt;br /&gt;...             height = rect.height-2&lt;br /&gt;...         dc.Blit(rect.x+1, rect.y+1, width, height, image, 0, 0, wx.COPY, True)&lt;br /&gt;&lt;br /&gt;&gt;&gt;&gt; app = MyApp(0)&lt;br /&gt;&gt;&gt;&gt; app.MainLoop()&lt;/pre&gt;The above code ought to look something like this:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_3hqf70Lx0Mk/R5CDyAQ5SvI/AAAAAAAAAC8/stAcx3P4vaI/s1600-h/screenshot.PNG"&gt;&lt;img style="cursor: pointer;" src="http://4.bp.blogspot.com/_3hqf70Lx0Mk/R5CDyAQ5SvI/AAAAAAAAAC8/stAcx3P4vaI/s320/screenshot.PNG" alt="" id="BLOGGER_PHOTO_ID_5156766468426779378" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The class MyImageRenderer is the same as the one from the demo. I modified it slightly so that it loads an image from file rather than from a hex coded string and that you can set the image as a parameter instead of having it defined within the renderer itself.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-7100254282509969075?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/7100254282509969075/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=7100254282509969075' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7100254282509969075'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7100254282509969075'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/01/simple-wxgridgrid-example.html' title='A simple wx.grid.Grid example'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_3hqf70Lx0Mk/R5CDyAQ5SvI/AAAAAAAAAC8/stAcx3P4vaI/s72-c/screenshot.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-4676911887383339812</id><published>2008-01-09T20:49:00.000+01:00</published><updated>2008-01-18T18:59:33.476+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='wxPython'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><title type='text'>Kicking a dead horse</title><content type='html'>This is my first week of my 8 months of daddy-duty. I've got a bit more free time now, not an obscene amount more, but Eowyn is very easy to take care of and she sleeps quite a few times during the day so I've found myself spending more time thinking about my after-work projects.&lt;br /&gt;&lt;br /&gt;So, as I was converting the ClipsJNI demos to wxPython/pyClips (still one to go though) I realised that I might have use for a project I worked on a couple of years ago.&lt;br /&gt;&lt;br /&gt;The summer of 2004 I wrote an "application browser" based on wxPython. The easiest way to explain what it does is to think of it as a regular web browser but instead of reading HTML and generating a view it reads something I called wxWML or wxWidgets Markup Language (wxML was taken already) and "generates" a wxPython rich client application.&lt;br /&gt;&lt;br /&gt;The project is called &lt;a href="http://sourceforge.net/projects/wxbrowser/"&gt;wxBrowser&lt;/a&gt; and can be found on SourceForge. We used it at work to deploy an application we couldn't get working in the web browsers at that time. Today, there are several options that are much, much better of course. XUL and Eclipse RCF come to mind.&lt;br /&gt;&lt;br /&gt;Anyway, I made one last release in the summer of 2005 and I haven't looked at it since. Until today that is.&lt;br /&gt;&lt;br /&gt;It turns out that creating a GUI for a Clips application requires you to divide your application into (at least) two distinct parts that are quite tightly coupled. You can try to make the coupling looser by having a more dynamic GUI application that decides what to show on the screen based on Facts in Working Memory (see the Auto Demo for an example of this). But there will still be quite a lot of GUI specific code that won't be all that dynamic. Which, as it turns out, was exactly the problem I wrote the wxBrowser for in the first place.&lt;br /&gt;&lt;br /&gt;If I can translate Facts in Clips' Working Memory to wxWML I could use the wxBrowser to provide a GUI for a Clips program. Sure, I'd have to write a whole lot of rules and functions to get it to work but at least I won't have to do it in another language or environment. The wxBrowser itself can be distributed as an .exe which contains pyClips so all you really need is the Clips program to run and you're good to go.&lt;br /&gt;&lt;br /&gt;There's of course a down side to all of this which is why I decided to go with the &lt;span style="font-style: italic;"&gt;Kicking a dead horse&lt;/span&gt; title instead of &lt;span style="font-style: italic;"&gt;Blowing new life into old things&lt;/span&gt; or something similar. First of all, wxPython code (and the structure of the library) isn't always that easy to understand. This was one of the major stumbling blocks in 2004 and it hasn't gone away. Secondly, there's a reason people use all sorts of tools to generate GUIs and last time I checked there was no tool that could save in Clips' deffacts format. Third, it's an evil bitch-monster of death to debug if something isn't working as expected (which almost ALWAYS is the case when working with GUIs).&lt;br /&gt;&lt;br /&gt;Well, luckily I've gotten quite good at kicking dead horses over the years so I'll give this idea a try in the next few weeks. If it doesn't work out, I'll just bring it up again in 2012 or so ;-)&lt;br /&gt;&lt;br /&gt;[2008-01-10] Update: Fourth, Client-side scripting. This will actually be less of a problem with Clips as a backend because, well, there wouldn't be any need for client side scripting since the back end is available all the time unlike in an HTTP environment.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-4676911887383339812?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/4676911887383339812/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=4676911887383339812' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/4676911887383339812'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/4676911887383339812'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/01/kicking-dead-horse.html' title='Kicking a dead horse'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-4077438040175054558</id><published>2008-01-08T19:08:00.000+01:00</published><updated>2008-01-08T19:51:17.179+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pyRete'/><title type='text'>New host for pyRete</title><content type='html'>I've registered &lt;a href="http://code.google.com/p/pyrete/"&gt;pyRete&lt;/a&gt; as a Free Software (GPL) project at &lt;a href="http://code.google.com/"&gt;Google&lt;/a&gt;. The project site is more or less empty at the moment but I intend on moving all of the source code together with examples, instructions and documentation in the next few days.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-4077438040175054558?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/4077438040175054558/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=4077438040175054558' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/4077438040175054558'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/4077438040175054558'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/01/new-host-for-pyrete.html' title='New host for pyRete'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-8279877001439069232</id><published>2008-01-08T16:33:00.000+01:00</published><updated>2008-01-08T16:56:15.595+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='wxPython'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><title type='text'>AutoDemo.wx.py</title><content type='html'>I've just finished implementing the &lt;a href="http://groups.google.com/group/CLIPSESG/browse_frm/thread/aa280fe6287d96d8"&gt;ClipsJNI&lt;/a&gt; Auto Demo example in &lt;a href="http://www.wxpython.org/"&gt;wxPython&lt;/a&gt;. This time it was easier to do a line by line (more or less)  re-write of the Java code, but that's mostly because most of the GUI is controlled from within the Clips file.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_3hqf70Lx0Mk/R4ObYgQ5SuI/AAAAAAAAAC0/AVEioURZtfU/s1600-h/AutoDemo.gif"&gt;&lt;img style="cursor: pointer;" src="http://1.bp.blogspot.com/_3hqf70Lx0Mk/R4ObYgQ5SuI/AAAAAAAAAC0/AVEioURZtfU/s320/AutoDemo.gif" alt="" id="BLOGGER_PHOTO_ID_5153133243921943266" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Just as with the &lt;a href="http://commentsarelies.blogspot.com/2008/01/clips-wine-demo-using-wxpython.html"&gt;Wine Demo&lt;/a&gt; I had to modify some of the calls to the Clips engine. I added two functions that return Fact indexes instead of Fact addresses, but that's it.&lt;br /&gt;&lt;br /&gt;The demo is &lt;a href="http://www.pulp.se/clips/pyclips/AutoDemo.zip"&gt;here&lt;/a&gt;. If you've already got Python, wxPython and pyClips installed all you have to do is run &lt;span style="font-style: italic;"&gt;python AutoDemo.wx.py&lt;/span&gt; at a command line and it should pop right up (same goes for the Wine Demo).&lt;br /&gt;&lt;br /&gt;Once I've finished the Sudoku Demo I'll write up some instructions on how to use pyClips and wxPython in general. Where to start, where to get help and such things. Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-8279877001439069232?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/8279877001439069232/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=8279877001439069232' title='8 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8279877001439069232'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8279877001439069232'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/01/autodemowxpy.html' title='AutoDemo.wx.py'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_3hqf70Lx0Mk/R4ObYgQ5SuI/AAAAAAAAAC0/AVEioURZtfU/s72-c/AutoDemo.gif' height='72' width='72'/><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-2433963306589596464</id><published>2008-01-05T19:12:00.000+01:00</published><updated>2008-01-05T22:52:02.614+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='wxPython'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><title type='text'>Clips Wine Demo using wxPython</title><content type='html'>I finally got enough free time to finish the wxPython Wine Demo &lt;a href="http://commentsarelies.blogspot.com/2007/12/winedemowxpy.html"&gt;I found last week&lt;/a&gt;. At first I wanted it to be, more or less, a line by line re-write of the Java version (&lt;a href="http://groups.google.com/group/CLIPSESG/browse_frm/thread/aa280fe6287d96d8"&gt;ClipsJNI&lt;/a&gt;) but that turned out to be too difficult in the end.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_3hqf70Lx0Mk/R3_btAQ5StI/AAAAAAAAACs/ZK-a2cqyK3g/s1600-h/WineDemo.GIF"&gt;&lt;img style="cursor: pointer;" src="http://3.bp.blogspot.com/_3hqf70Lx0Mk/R3_btAQ5StI/AAAAAAAAACs/ZK-a2cqyK3g/s320/WineDemo.GIF" alt="" id="BLOGGER_PHOTO_ID_5152078064946596562" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Instead, I've tried to do as little "over all" damage as possible.&lt;br /&gt;&lt;br /&gt;I didn't make the GUI 100% the same because that would have required too much wxPython specific code (and the point is not to show off &lt;a href="http://www.wxpython.org"&gt;wxPython&lt;/a&gt; anyway). I've also had to make a small change to the Clips code. It turned out that &lt;a href="http://sourceforge.net/projects/pyclips"&gt;pyClips&lt;/a&gt; had trouble converting a MultiField return value containing Fact Addresses, so I added a function that instead returns the Fact Indexes.&lt;br /&gt;&lt;br /&gt;Last, but not least, I designed the GUI using a wxPython XRC editor. That way I didn't have to write all that boilerplate GUI code to create, add and configure all of the widgets in the application.&lt;br /&gt;&lt;br /&gt;I have kept the code for asserting facts exactly as in the ClipsJNI example even though that's not really the way to do it in Python.&lt;br /&gt;&lt;br /&gt;All of the above are things that have been added or more complicated in Python/pyClips but there are other things as well. Things that could have been done easier. For example, there's no point in sorting the Facts in Clips, that can be done automatically in the ListCtrl widget (if you want).&lt;br /&gt;&lt;br /&gt;The code is &lt;a href="http://www.pulp.se/clips/pyclips/WineDemo.zip"&gt;here&lt;/a&gt;. At least for now, I'm hoping it can be hosted on SourceForge together with the pyClips project but that's up to Franz so I'll have to wait and see what he says first. If you find any bugs or have any other comments, please let me know.&lt;br /&gt;&lt;br /&gt;Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-2433963306589596464?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/2433963306589596464/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=2433963306589596464' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/2433963306589596464'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/2433963306589596464'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2008/01/clips-wine-demo-using-wxpython.html' title='Clips Wine Demo using wxPython'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_3hqf70Lx0Mk/R3_btAQ5StI/AAAAAAAAACs/ZK-a2cqyK3g/s72-c/WineDemo.GIF' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-8785769592373556484</id><published>2007-12-30T19:50:00.000+01:00</published><updated>2008-01-01T16:21:13.461+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='wxPython'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><title type='text'>WineDemo.wx.py</title><content type='html'>Today I found, buried deep in the Playground folder on my hard disk, a half finished &lt;a href="http://www.wxpython.org/"&gt;wxPython&lt;/a&gt; implementation of the WineDemo from Gary's &lt;a href="http://groups.google.com/group/CLIPSESG/browse_frm/thread/aa280fe6287d96d8"&gt;ClipsJNI package&lt;/a&gt;. I'd completely forgotten about it but I still think it's worth finishing. Not having a good example in Python/&lt;a href="http://pyclips.sourceforge.net/"&gt;pyClips&lt;/a&gt; is actually very annoying. Especially if you're, like me, trying to promote Python as a good way to provide a GUI for Clips programs.&lt;br /&gt;&lt;br /&gt;So I started going through the code, trying to finish it earlier today but that made me remember why I stopped in the first place. I'm having a lot of trouble converting these two lines of Java into Python:&lt;pre&gt;String evalStr = "(WINES::get-wine-list)";&lt;br /&gt;MultifieldValue pv = (MultifieldValue) clips.eval(evalStr);&lt;/pre&gt;The conversion itself is quite simple but I can't get pyClips to evaluate the function, I keep getting this:&lt;pre&gt;&gt;&gt;&gt; clips.Eval("(WINES::get-wine-list)")&lt;br /&gt;&lt;br /&gt;Traceback (most recent call last):&lt;br /&gt;File "&lt;pyshell#4&gt;", line 1, in &lt;module&gt;&lt;br /&gt;clips.Eval("(WINES::get-wine-list)")&lt;br /&gt;File "C:\Python25\lib\site-packages\clips\_clips_wrap.py", line 3430, in Eval&lt;br /&gt;return _cl2py(_c.eval(expr))&lt;br /&gt;ClipsError: C10: unable to evaluate expression&lt;/module&gt;&lt;/pyshell#4&gt;&lt;/pre&gt;&lt;strike&gt;I guess I should&lt;/strike&gt; I have reported it as a &lt;a href="http://sourceforge.net/tracker/index.php?func=detail&amp;amp;aid=1861107&amp;amp;group_id=114052&amp;amp;atid=667038"&gt;bug&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-8785769592373556484?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/8785769592373556484/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=8785769592373556484' title='4 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8785769592373556484'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8785769592373556484'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2007/12/winedemowxpy.html' title='WineDemo.wx.py'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-4176831336636669851</id><published>2007-12-27T09:35:00.000+01:00</published><updated>2007-12-27T21:57:13.520+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='CSP'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><title type='text'>Constraint Programming in Clips</title><content type='html'>A couple of weeks ago, I found Michel Futtersack and Jean Marc Labat's research on &lt;a href="http://www.droit.univ-paris5.fr/futtersack/english/research/CCP/index.html"&gt;Constraint Programming in Clips&lt;/a&gt; from 1997. I've spent part of Christmas going through the source code. It's quite an interesting "read" if you're a Clips user. They've implemented a backtracking search algorithm with forward checking. But the really cool thing is that they've packaged it all as a reusable component (in CCP.clp) to be used as the main driver to find solutions for any Constraint Satisfaction Problem (CSP).&lt;br /&gt;&lt;br /&gt;They've also provided some examples, one of them is the classic N-Queens. But I've modified it a bit to make it easier to try out different sizes of the chessboard. Also, their implementation is based around either finding &lt;b&gt;all&lt;/b&gt; solutions to a problem or finding solutions one by one until the user says stop. This makes it a bit difficult to time the algorithm and compare it against, for example, Drools' solver module. What I want it to do is to find the first solution and then quit, without any user interaction.&lt;br /&gt;&lt;br /&gt;So here's what I changed:&lt;br /&gt;&lt;br /&gt;The first question the main driver loop asks is: "Do you want all solutions?" and if you answer "No" the next question is "Do you want another solution?" So I can easily get what I want by short-circuiting the &lt;span style="font-style: italic;"&gt;yes-or-no-p&lt;/span&gt; function in CCP.clp to always return FALSE (No).&lt;pre&gt;(deffunction MAIN::yes-or-no-p (?question)&lt;br /&gt;(printout t ?question " No" crlf)&lt;br /&gt;(return FALSE))&lt;/pre&gt;I've also turned on the customzied printing of solutions (remove or comment out &lt;i&gt;print-solution&lt;/i&gt; and remove the comments from &lt;i&gt;to_customized_printing&lt;/i&gt;) because it's easier to read the result in that form.&lt;br /&gt;&lt;br /&gt;When it comes to the specifics of the N-Queens problem (in n-queens.clp) they've put the definition of the solution space in two different deffacts: &lt;span style="font-style: italic;"&gt;queens&lt;/span&gt; and &lt;span style="font-style: italic;"&gt;chessboard&lt;/span&gt;, which unfortunately also binds the n implicitly.&lt;br /&gt;&lt;br /&gt;For example, instead of having:&lt;pre&gt;(deffacts PROPAG::queens&lt;br /&gt;(var (name x1)  (possible-values (create$ 1 2 3 4 5 6 7 8 9 10 11 12)))&lt;br /&gt;(var (name x2)  (possible-values (create$ 1 2 3 4 5 6 7 8 9 10 11 12)))&lt;br /&gt;(var (name x3)  (possible-values (create$ 1 2 3 4 5 6 7 8 9 10 11 12)))&lt;br /&gt;(var (name x4)  (possible-values (create$ 1 2 3 4 5 6 7 8 9 10 11 12)))&lt;br /&gt;(var (name x5)  (possible-values (create$ 1 2 3 4 5 6 7 8 9 10 11 12)))&lt;br /&gt;(var (name x6)  (possible-values (create$ 1 2 3 4 5 6 7 8 9 10 11 12)))&lt;br /&gt;(var (name x7)  (possible-values (create$ 1 2 3 4 5 6 7 8 9 10 11 12)))&lt;br /&gt;(var (name x8)  (possible-values (create$ 1 2 3 4 5 6 7 8 9 10 11 12)))&lt;br /&gt;(var (name x9)  (possible-values (create$ 1 2 3 4 5 6 7 8 9 10 11 12)))&lt;br /&gt;(var (name x10) (possible-values (create$ 1 2 3 4 5 6 7 8 9 10 11 12)))&lt;br /&gt;(var (name x11) (possible-values (create$ 1 2 3 4 5 6 7 8 9 10 11 12)))&lt;br /&gt;(var (name x12) (possible-values (create$ 1 2 3 4 5 6 7 8 9 10 11 12)))&lt;br /&gt;)&lt;/pre&gt;I wrote a function to generate and assert &lt;span style="font-style: italic;"&gt;var&lt;/span&gt; facts:&lt;pre&gt;(deffunction PROPAG::generate-var (?i $?v)&lt;br /&gt;(assert (var (name (sym-cat x ?i)) (possible-values $?v))))&lt;/pre&gt;there's also a matching rule:&lt;pre&gt;(defrule generate-vars&lt;br /&gt;(generate-vars)&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;(problem-size ?n)&lt;/span&gt;&lt;br /&gt;=&gt;&lt;br /&gt;(loop-for-count (?i ?n)&lt;br /&gt;(bind ?v (create$))&lt;br /&gt;(loop-for-count (?j ?n)&lt;br /&gt;   (bind ?v (insert$ $?v (+ (length$ $?v) 1) ?j)))&lt;br /&gt;(generate-var ?i $?v)))&lt;/pre&gt;and the deffacts becomes:&lt;pre&gt;(deffacts PROPAG::queens&lt;br /&gt;(generate-vars))&lt;/pre&gt;Ok, maybe it's not the prettiest solution, but it gets the job done and I don't have to manually define 64, 128 or 256 var facts containing 64, 128 or 256 items long multislots. There's also similarly a function, a rule and a deffacts for the chessboard facts.&lt;br /&gt;&lt;br /&gt;The problem size (marked with red above) is now instead specified in an implied deftemplate which is injected at the batch file level. So an N-Queens of size 12 can look like this:&lt;pre&gt;CLIPS&gt; (load "CCP.clp")&lt;br /&gt;+%%%$!*****+******&lt;br /&gt;TRUE&lt;br /&gt;CLIPS&gt; (load "n-queens.clp")&lt;br /&gt;+$!%$!!*******&lt;br /&gt;TRUE&lt;br /&gt;CLIPS&gt; (deffacts PROPAG::problem-size&lt;br /&gt;(problem-size 12))&lt;br /&gt;CLIPS&gt; (watch statistics)&lt;br /&gt;CLIPS&gt; (reset)&lt;br /&gt;CLIPS&gt; (run)&lt;br /&gt;Do you want all the solutions ?  No&lt;br /&gt;the problem is solved&lt;br /&gt;Do you want another solution ?  No&lt;br /&gt;No more solution&lt;br /&gt;&lt;br /&gt;I found 1 solution(s)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;* * * * * * X * * * * *&lt;br /&gt;* * * * * * * * X * * *&lt;br /&gt;* * * * * * * * * * X *&lt;br /&gt;* * * * X * * * * * * *&lt;br /&gt;* X * * * * * * * * * *&lt;br /&gt;* * * X * * * * * * * *&lt;br /&gt;* * * * * * * * * * * X&lt;br /&gt;* * * * * * * * * X * *&lt;br /&gt;* * * * * * * X * * * *&lt;br /&gt;* * * * * X * * * * * *&lt;br /&gt;X * * * * * * * * * * *&lt;br /&gt;* * X * * * * * * * * *&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;2181 rules fired        Run time is 1.101 seconds.&lt;br /&gt;1980.92643051771 rules per second.&lt;br /&gt;99 mean number of facts (116 maximum).&lt;br /&gt;1 mean number of instances (1 maximum).&lt;br /&gt;5 mean number of activations (23 maximum).&lt;br /&gt;CLIPS&gt;&lt;/pre&gt;I really have no idea if CCP is fast or slow but here are some numbers:&lt;pre&gt; n | rules | seconds&lt;br /&gt;---+-------+-------&lt;br /&gt;04 |    31 | 000.01&lt;br /&gt;06 |   101 | 000.02&lt;br /&gt;08 |   162 | 000.04&lt;br /&gt;10 |   577 | 000.18&lt;br /&gt;12 |  2181 | 001.06&lt;br /&gt;16 |  2584 | 002.97&lt;br /&gt;20 |   613 | 001.05&lt;br /&gt;24 |  2709 | 012.95&lt;br /&gt;28 |   699 | 002.89&lt;br /&gt;32 |  9284 | 130.72&lt;br /&gt;&lt;/pre&gt;I'm guessing it's the heuristics of the forward checking (or possibly the structure of the problem) that makes the number of rules fired and the run-time to differ so much between different sizes of the chessboard. I was really expecting n = 28 to be "harder" than n = 24 but... apparently not.&lt;br /&gt;&lt;br /&gt;If you also want to experiment with N-Queens in CCP, the modified files can be found &lt;a href="http://www.pulp.se/clips/ccp/CCP.clp"&gt;here&lt;/a&gt; (CCP.clp), &lt;a href="http://www.pulp.se/clips/ccp/n-queens.clp"&gt;here&lt;/a&gt; (n-queens.clp) and &lt;a href="http://www.pulp.se/clips/ccp/n-queens.bat"&gt;here&lt;/a&gt; (n-queens.bat). They won't work with Clips 6.30 (Beta) though because of a problem with Logical CE's but I think Gary's on top of that already. Once a new release is out it will be interesting to see if the new and improved Rete implementation of Clips 6.3 will reduce the run-time and/or possibly the number of rules fired as well.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-4176831336636669851?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/4176831336636669851/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=4176831336636669851' title='8 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/4176831336636669851'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/4176831336636669851'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2007/12/constraint-programming-in-clips.html' title='Constraint Programming in Clips'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-2138807412645192371</id><published>2007-12-14T11:20:00.000+01:00</published><updated>2007-12-14T11:26:45.819+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Rule Engine'/><category scheme='http://www.blogger.com/atom/ns#' term='pyRete'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><title type='text'>MultiField variables in pyRete</title><content type='html'>I've been thinking about MultiField variables quite a bit these past few weeks. I want to fit them into pyRete. However, they're a bit different from SingleField variables - as this snippet shows:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;CLIPS&gt; (deftemplate Person (multislot name))&lt;br /&gt;CLIPS&gt; (assert (Person (name Astrid Ingrid Eowyn Lindberg)))&lt;br /&gt;[fact-1]&lt;br /&gt;CLIPS&gt; (defrule match-names&lt;br /&gt;         (Person (name $?n1 $?n2))&lt;br /&gt;        =&gt;&lt;br /&gt;         (printout t $?n1 " " $?n2 crlf))&lt;br /&gt;CLIPS&gt; (run)&lt;br /&gt;() (Astrid Ingrid Eowyn Lindberg)&lt;br /&gt;(Astrid) (Ingrid Eowyn Lindberg)&lt;br /&gt;(Astrid Ingrid) (Eowyn Lindberg)&lt;br /&gt;(Astrid Ingrid Eowyn) (Lindberg)&lt;br /&gt;(Astrid Ingrid Eowyn Lindberg) ()&lt;/pre&gt;As you can see a MultiField variable can, and will, hold all possible combinations of values found in a multislot.&lt;br /&gt;&lt;br /&gt;Given the current design of pyRete this means that I'll have to implement functionality to generate all combinations *within* a Node's match method. I'm slightly worried that this might make the processing quite a bit slower (I believe it's slower in Clips as well but I haven't benchmarked it so I can't really say for sure).&lt;br /&gt;&lt;br /&gt;The other thing is that in Clips there's no way of nesting multifield structures. You cannot have a multifield of multifields. When it comes to Python, nested tuples/lists/dicts are common and I'd like to end up with an implementation that supports them.&lt;br /&gt;&lt;br /&gt;The question is how to best do that?&lt;br /&gt;&lt;br /&gt;In pyRete today there's no equivalent to Single or MultiField variables. Variables are bound to fact-instances and not parts of a fact (attributes of an object). You *can* introduce a variable in the RHS that binds to a part of a fact, but that cannot be done using pattern-matching.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&gt;&gt;&gt; def rule(foo = Foo):&lt;br /&gt;...   if foo(something = 100):&lt;br /&gt;...     something_else = foo.something_else&lt;br /&gt;...     # ...&lt;/pre&gt;So what we're talking about here is basically the possibility to do this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&gt;&gt;&gt; def rule(foo = Foo):&lt;br /&gt;...   if foo(something = 100, something_else = something_else):&lt;br /&gt;...     # ...&lt;/pre&gt;However, this will of course let you do far more sophisticated things than that. For example, you could "filter" out facts based on attribute values in &lt;strong&gt;another&lt;/strong&gt; fact:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&gt;&gt;&gt; def rule(foo = Foo, bar = Bar):&lt;br /&gt;...   if foo(something = var) and bar(something_else = var):&lt;br /&gt;...     # ...&lt;/pre&gt;My thoughts so far are that I'll use __ as Clips use $? and _ as Clips use ?. Named Single and MultiField variables would be &lt;em&gt;_foo&lt;/em&gt; and &lt;em&gt;__foo&lt;/em&gt; respectively. Variables will default to SingleField so &lt;em&gt;foo&lt;/em&gt; and &lt;em&gt;_foo&lt;/em&gt; are really the same thing.&lt;br /&gt;&lt;br /&gt;Unfortunately those types of variable names already have a meaning in Python. Though I'm not sure I can afford to care about that since there's really no other way to distinguish variable names without breaking syntax rules (which I'm hoping to avoid).&lt;br /&gt;&lt;br /&gt;Also, I'd like to introduce a way of specifying a minimum and possibly a maximum number of values a MultiField should take on. That would allow you to, for example, say that all MultiField variables must have at least one value. My Clips example would instead give the following result:&lt;br /&gt;&lt;pre&gt;(Astrid) (Ingrid Eowyn Lindberg)&lt;br /&gt;(Astrid Ingrid) (Eowyn Lindberg)&lt;br /&gt;(Astrid Ingrid Eowyn) (Lindberg)&lt;/pre&gt;&lt;p&gt;I don't really have a suggestion for syntax for that though, &lt;em&gt;_foo_1toN&lt;/em&gt; doesn't really feel right :-)&lt;/p&gt;If you've got any suggestions or thoughts about this I'd love to hear them.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-2138807412645192371?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/2138807412645192371/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=2138807412645192371' title='2 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/2138807412645192371'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/2138807412645192371'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2007/12/multifield-variables-in-pyrete.html' title='MultiField variables in pyRete'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-8681297308978920893</id><published>2007-12-12T22:20:00.000+01:00</published><updated>2007-12-13T09:10:18.980+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><title type='text'>Guy Steele's keynote from OOPSLA 1998</title><content type='html'>I found &lt;a href="http://video.google.com/videoplay?docid=-8860158196198824415"&gt;Guy Steele's keynote from OOPSLA 1998&lt;/a&gt; on Google Video a few weeks ago. It's quite funny (both peculiar and ha-ha) and well worth watching.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-8681297308978920893?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/8681297308978920893/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=8681297308978920893' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8681297308978920893'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8681297308978920893'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2007/12/guy-steeles-keynote-from-oopsla-1998.html' title='Guy Steele&apos;s keynote from OOPSLA 1998'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-1769268647880557965</id><published>2007-11-30T21:47:00.000+01:00</published><updated>2007-11-30T22:01:15.033+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Family'/><category scheme='http://www.blogger.com/atom/ns#' term='Vacation'/><title type='text'>A week in NYC</title><content type='html'>Tomorrow at this time (Swedish time that is) Sofia, Eowyn and I will land at Newark Liberty International Airport. We're having a weeks well earned vacation in New York City. It's a nine hour flight across the Atlantic so the toughest question at this point is what books to pack?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-1769268647880557965?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/1769268647880557965/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=1769268647880557965' title='2 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/1769268647880557965'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/1769268647880557965'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2007/11/week-in-nyc.html' title='A week in NYC'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-8057576476791807104</id><published>2007-11-29T20:51:00.000+01:00</published><updated>2007-11-30T17:05:25.517+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Regular Expressions'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><title type='text'>Added some functionality in the Clips regexp library</title><content type='html'>Today I spent about three hours on a train between Stockholm and Gothenburg. That was nearly enough to complete the functionality in the &lt;a href="http://commentsarelies.blogspot.com/2007/10/small-regular-expression-library-for.html"&gt;small regexp library for Clips&lt;/a&gt; I wrote a few weeks ago.&lt;br /&gt;&lt;br /&gt;I've added &lt;span style="font-style: italic;"&gt;findall&lt;/span&gt;, &lt;span style="font-style: italic;"&gt;split&lt;/span&gt; and &lt;span style="font-style: italic;"&gt;sub&lt;/span&gt;(stitute) functions which work likeso:&lt;br /&gt;&lt;pre&gt;CLIPS&gt; (load "regexp.clp")&lt;br /&gt;:!!!!!!&lt;br /&gt;CLIPS&gt; (findall "a" "Today I spent about three hours on a train between Stockholm and Gothenburg")&lt;br /&gt;("4 4 "a"" "15 15 "a"" "36 36 "a"" "40 40 "a"" "62 62 "a"")&lt;br /&gt;CLIPS&gt; (split "a" "Today I spent about three hours on a train between Stockholm and Gothenburg")&lt;br /&gt;("Tod" "y I spent " "bout three hours on " " tr" "in between Stockholm " "nd Gothenburg")&lt;br /&gt;CLIPS&gt; (sub "a" "A" "Today I spent about three hours on a train between Stockholm and Gothenburg")&lt;br /&gt;"TodAy I spent About three hours on A trAin between Stockholm And Gothenburg"&lt;/pre&gt;Ok, my examples might not be the best but you get the idea. Both split and sub also take an optional parameter ?max which allows you to limit the number of splits and substitutions:&lt;br /&gt;&lt;pre&gt;CLIPS&gt; (sub "a" "A" "Today I spent about three hours on a train between Stockholm and Gothenburg" 2)&lt;br /&gt;"TodAy I spent About three hours on a train between Stockholm and Gothenburg"&lt;/pre&gt;The library consists of two files: &lt;a href="http://www.pulp.se/clips/regexp/regexp.clp"&gt;regexp.clp&lt;/a&gt; and &lt;a href="http://www.pulp.se/clips/regexp/regexp-test.bat"&gt;regexp-test.bat&lt;/a&gt; (requires &lt;a href="http://www.pulp.se/clips/unittest/unittest.clp"&gt;unittest.clp&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;[2007-11-30] Update: I found a bug in the search function which caused a string not to be properly searched (the search ended too early). Apparently my test cases are not very good...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-8057576476791807104?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/8057576476791807104/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=8057576476791807104' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8057576476791807104'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8057576476791807104'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2007/11/added-some-functionality-in-clips.html' title='Added some functionality in the Clips regexp library'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-7398132603962790452</id><published>2007-11-19T16:52:00.000+01:00</published><updated>2007-11-19T18:37:53.123+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Rete Algorithm'/><category scheme='http://www.blogger.com/atom/ns#' term='Rule Engine'/><category scheme='http://www.blogger.com/atom/ns#' term='pyRete'/><title type='text'>Yet another starting-over post</title><content type='html'>This is the fifth time I've decided to start over (more or less from scratch) in my attempt to produce a Rete rule engine in Python. It' irritating that it's so hard to get the pyRete code into a shape I'm pleased with and I've been feeling kind of low these past few weeks since my last attempt to beat the RuleCompiler into submission failed miserably.&lt;br /&gt;&lt;br /&gt;I know that developing a rule engine is no easy task and that others have devoted large portions of their lives doing it so 2 years isn't that much, really. I'd feel a whole lot better though if I was sure that I won't write this post again in a year or so.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-7398132603962790452?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/7398132603962790452/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=7398132603962790452' title='5 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7398132603962790452'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7398132603962790452'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2007/11/yet-another-starting-over-post.html' title='Yet another starting-over post'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-8188305704755515398</id><published>2007-11-12T18:51:00.000+01:00</published><updated>2007-11-12T21:08:25.265+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OLPC'/><title type='text'>Give one. Get one.</title><content type='html'>As of today, the &lt;a href="http://www.laptopgiving.org/en/index.php"&gt;G1G1 program&lt;/a&gt; of OLPC has started. Unfortunately the program is only intended for USA and Canada but there are &lt;a href="http://www.olpcnews.com/sales_talk/g1g1/give_one_get_one_globally.html"&gt;ways&lt;/a&gt; to get them to Europe (and elsewhere) so I may just have to order one (for Eowyn of course). $400 is only about 2500 SEK so it's definitely worth it even if the shipping will cost loads.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-8188305704755515398?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/8188305704755515398/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=8188305704755515398' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8188305704755515398'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8188305704755515398'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2007/11/give-one-get-one.html' title='Give one. Get one.'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-7958863702119782558</id><published>2007-10-22T21:59:00.000+02:00</published><updated>2007-10-22T22:58:10.386+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Rule Engine'/><category scheme='http://www.blogger.com/atom/ns#' term='Lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><title type='text'>Another Clips and Lisa comparison</title><content type='html'>I spent this evening with Emacs, CLisp and Lisa. I wanted to get this piece of Clips code working:&lt;pre&gt;|CLIPS&gt; (deftemplate movie&lt;br /&gt;|  (multislot title))&lt;br /&gt;|CLIPS&gt; (deffacts movies&lt;br /&gt;|  (movie (title "A" "B" "C"))&lt;br /&gt;|  (movie (title "B" "C" "D"))&lt;br /&gt;|  (movie (title "C" "D" "E")))&lt;br /&gt;|CLIPS&gt; (defrule chain&lt;br /&gt;|  ?m1 &lt;- (movie (title $? $?link&amp;amp;:(&gt; (length$ $?link) 0)))&lt;br /&gt;|  ?m2 &lt;- (movie (title $?link $?))&lt;br /&gt;|  (test (neq ?m2 ?m1)) ; Avoid infinite chains because of&lt;br /&gt;|                       ; movie titles like "Liar Liar".&lt;br /&gt;| =&gt;&lt;br /&gt;|  (printout t (fact-slot-value ?m1 title) " " (fact-slot-value ?m2 title) crlf))&lt;br /&gt;|CLIPS&gt; (reset)&lt;br /&gt;|==&gt; Activation 0      chain: f-1,f-2&lt;br /&gt;|==&gt; Activation 0      chain: f-2,f-3&lt;br /&gt;|==&gt; Activation 0      chain: f-1,f-3&lt;br /&gt;|CLIPS&gt; (run)&lt;br /&gt;|("A" "B" "C") ("C" "D" "E")&lt;br /&gt;|("B" "C" "D") ("C" "D" "E")&lt;br /&gt;|("A" "B" "C") ("B" "C" "D")&lt;/pre&gt;Note how elegantly &lt;span style="font-style: italic;"&gt;$?link&lt;/span&gt; matches any number of elements in a multislot.&lt;br /&gt;&lt;br /&gt;Unfortunately, there are no multislots in Lisa. But, you're allowed to add a list (or even a list of lists) to a slot. Only thing is that you either have to match it as-is or provide your own predicate function to perform whatever test you need done. Here's what I came up with:&lt;pre&gt;|LISA-USER&gt; (deftemplate movie ()&lt;br /&gt;|             (slot title))&lt;br /&gt;|#[STANDARD-CLASS MOVIE]&lt;br /&gt;|LISA-USER&gt; (deffacts movies ()&lt;br /&gt;|             (movie (title '("A" "B" "C")))&lt;br /&gt;|             (movie (title '("B" "C" "D")))&lt;br /&gt;|             (movie (title '("C" "D" "E"))))&lt;br /&gt;|(#[DEFFACTS&lt;br /&gt;|   MOVIES ; (#[MOVIE ; id -1 #x19FF2789] #[MOVIE ; id -1 #x19FF2ADD]&lt;br /&gt;|    #[MOVIE ; id -1 #x19FF2E31])&lt;br /&gt;|   #x19FF2EDD])&lt;br /&gt;|LISA-USER&gt; (defun overlap? (list-1 list-2)&lt;br /&gt;|             (if (not (equal list-1 list-2))&lt;br /&gt;|                   (dotimes (index (min (length list-1)&lt;br /&gt;|                                        (length list-2)))&lt;br /&gt;|                     (if (equal (subseq list-1 (- (length list-1) index 1) (length list-1))&lt;br /&gt;|                                (subseq list-2 0 (+ index 1)))&lt;br /&gt;|                       (return-from overlap? t))))&lt;br /&gt;|             nil)&lt;br /&gt;|OVERLAP?&lt;br /&gt;|LISA-USER&gt; (defrule chain ()&lt;br /&gt;|             (?m1 (movie (title ?title1)))&lt;br /&gt;|             (?m2 (movie (title ?title2)))&lt;br /&gt;|             (test (overlap? ?title1 ?title2))&lt;br /&gt;|             =&gt;&lt;br /&gt;|             (format t "~A ~A~%" ?title1 ?title2))&lt;br /&gt;|#[RULE CHAIN]&lt;br /&gt;|LISA-USER&gt; (reset)&lt;br /&gt;|T&lt;br /&gt;|LISA-USER&gt; (run)&lt;br /&gt;|(A B C) (B C D)&lt;br /&gt;|(B C D) (C D E)&lt;br /&gt;|(A B C) (C D E)&lt;br /&gt;|3&lt;/pre&gt;Not quite as elegant... but it works. I never managed to get the not equal test working in the rule so I had to place it in the overlap? function. Other than that, I think they're about as equivalent as they can get.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-7958863702119782558?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/7958863702119782558/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=7958863702119782558' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7958863702119782558'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7958863702119782558'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2007/10/another-clips-and-lisa-comparison.html' title='Another Clips and Lisa comparison'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-6718791719264002265</id><published>2007-10-21T19:03:00.000+02:00</published><updated>2007-10-21T19:22:50.929+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Regular Expressions'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><title type='text'>A small Regular Expression library for Clips</title><content type='html'>Inspired by the first chapter in &lt;a href="http://www.oreilly.com/catalog/9780596510046/index.html"&gt;Beautiful Code&lt;/a&gt; I've decided to try and implement a small regular expressions library in Clips. The code that's discussed in the book is taken from another book; &lt;a href="http://www.informit.com/store/product.aspx?isbn=020161586X"&gt;The Practice of Programming&lt;/a&gt; and the match code, written by Rob Pike, is amazingly enough only about 35 lines of C.&lt;br /&gt;&lt;br /&gt;I can say straight away that my code (roughly 200 lines) is *not* very beautiful. But then again, that wasn't the point of the exercise.&lt;br /&gt;&lt;br /&gt;Seeing as this is supposed to become a library I've implemented it a bit differently as well. To begin with, I've done two separate functions: &lt;span style="font-style: italic;"&gt;match&lt;/span&gt; and &lt;span style="font-style: italic;"&gt;search&lt;/span&gt;. They behave (roughly) as described in the &lt;a href="http://docs.python.org/lib/node46.html"&gt;docs for Python's re module&lt;/a&gt;: &lt;span style="font-style: italic;"&gt;match&lt;/span&gt; only matches from the beginning of the string (roughly equivalent to Rob's &lt;span style="font-style: italic;"&gt;matchhere&lt;/span&gt; function) and &lt;span style="font-style: italic;"&gt;search&lt;/span&gt; matches anywhere.&lt;br /&gt;&lt;br /&gt;I've started working on &lt;span style="font-style: italic;"&gt;findall&lt;/span&gt;, &lt;span style="font-style: italic;"&gt;split&lt;/span&gt; and &lt;span style="font-style: italic;"&gt;sub&lt;/span&gt; (substitute) but it's taking slightly longer to finish them than I've expected. I've also been *thinking* about implementing:&lt;br /&gt;1) character sets; for example \s, \d and \w&lt;br /&gt;2) match groups; so that you could write "(.+) \1" and have it match repeated character sequences&lt;br /&gt;3) and some more quantifiers; +, ? and possibly also {m[,n]}, {m[,n]}?, *?, +? and ??.&lt;br /&gt;&lt;br /&gt;but I don't know. The code is complex enough as it is and I think I'd take too long doing it (according to the book, it only took Rob about two hours to finish his implementation and I've already  spent a *lot* more time on this than that ;-)&lt;br /&gt;&lt;br /&gt;Here's how it works:&lt;pre&gt;CLIPS&gt; (load "regexp.clp")&lt;br /&gt;:!!!&lt;br /&gt;TRUE&lt;br /&gt;CLIPS&gt; (match "do"  "Lorem ipsum dolores sit amet")&lt;br /&gt;nil&lt;br /&gt;CLIPS&gt; (match ".* "  "Lorem ipsum dolores sit amet")&lt;br /&gt;"Lorem "&lt;br /&gt;CLIPS&gt; (search "do" "Lorem ipsum dolores sit amet")&lt;br /&gt;(13 14 "do")&lt;/pre&gt;The code is available &lt;a href="http://www.pulp.se/clips/regexp/regexp.clp"&gt;here&lt;/a&gt; and I've also prepared a &lt;a href="http://www.pulp.se/clips/regexp/regexp-test.bat"&gt;batch file with unit tests&lt;/a&gt; which can serve as an example of how to use the functions. NOTE! You will also have to have &lt;a href="http://www.pulp.se/clips/unittest/unittest.clp"&gt;unittest.clp&lt;/a&gt; in order to run it.&lt;br /&gt;&lt;br /&gt;If you find any bugs, please let me know. Enjoy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-6718791719264002265?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/6718791719264002265/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=6718791719264002265' title='2 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/6718791719264002265'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/6718791719264002265'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2007/10/small-regular-expression-library-for.html' title='A small Regular Expression library for Clips'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-3835022375148858941</id><published>2007-10-17T17:06:00.000+02:00</published><updated>2007-10-17T21:15:51.798+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Rule Engine'/><category scheme='http://www.blogger.com/atom/ns#' term='Lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><title type='text'>Going from Clips to Lisa</title><content type='html'>&lt;div&gt;I'm currently experimenting with (and thinking about moving my current after-work projects to) &lt;a href="http://lisa.sourceforge.net/"&gt;Lisa&lt;/a&gt;. It's close enough to Clips to make it very easy to pick up but there are a few things that will take some time to get used to.&lt;/div&gt;&lt;pre&gt;CL-USER&gt; (in-package LISA-USER)&lt;br /&gt;#[PACKAGE LISA-USER]&lt;br /&gt;LISA-USER&gt; (deftemplate foo&lt;br /&gt;    (slot name)&lt;br /&gt;    (slot age))&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;; Evaluation aborted&lt;/span&gt;&lt;br /&gt;LISA-USER&gt; (deftemplate foo ()&lt;br /&gt;    (slot name)&lt;br /&gt;    (slot age))&lt;br /&gt;#[STANDARD-CLASS FOO]&lt;br /&gt;LISA-USER&gt; (assert (foo (name "Johan") (age 34)))&lt;br /&gt;#[FOO ; id 1 #x1A015B01]&lt;br /&gt;LISA-USER&gt; (defrule remove-old-people&lt;br /&gt;      ?foo &lt;- (foo (age ?age&amp;amp;:(&gt; ?age 30)))&lt;br /&gt;      =&gt;&lt;br /&gt;      (retract ?foo))&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;; Evaluation aborted&lt;/span&gt;&lt;br /&gt;LISA-USER&gt; (defrule remove-old-people&lt;br /&gt;      (?foo (foo (age ?age (&gt; ?age 30))))&lt;br /&gt;      =&gt;&lt;br /&gt;      (retract ?foo))&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;; Evaluation aborted&lt;/span&gt;&lt;br /&gt;LISA-USER&gt; (defrule remove-old-people ()&lt;br /&gt;      (?foo (foo (age ?age (&gt; ?age 30))))&lt;br /&gt;      =&gt;&lt;br /&gt;      (retract ?foo))&lt;br /&gt;#[RULE REMOVE-OLD-PEOPLE]&lt;br /&gt;LISA-USER&gt; (agenda)&lt;br /&gt;#[ACTIVATION (INITIAL-CONTEXT.RETRACT-PEOPLE-OVER-30 (F-1) ; salience = 0)&lt;br /&gt;#x19FAED25]&lt;br /&gt;#[ACTIVATION (INITIAL-CONTEXT.RETRACT-PEOPLE-OVER-30 (F-1) ; salience = 0)&lt;br /&gt;#x19FAED25]&lt;br /&gt;For a total of 2 activations.&lt;br /&gt;; No value&lt;br /&gt;LISA-USER&gt; (facts)&lt;br /&gt;#[FOO ; id 1 #x19FAEBDD]&lt;br /&gt;For a total of 1 fact.&lt;br /&gt;; No value&lt;br /&gt;LISA-USER&gt; (run)&lt;br /&gt;1&lt;br /&gt;LISA-USER&gt; (facts)&lt;br /&gt;For a total of 0 facts.&lt;br /&gt;; No value&lt;br /&gt;LISA-USER&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div&gt;Each of the &lt;em&gt;; Evaluation aborted&lt;/em&gt; lines marks where I was sent into the debugger. I'm not sure how I managed to get two activations but I suspect that part of the rule was already created when I got the error of not having specified any keywords. No warning about redefining the rule though!&lt;br /&gt;&lt;br /&gt;Anyway, Lisa allows me to use the full power of Common Lisp side by side with a Rete-engine and I must admit I kind of like that idea. Lately, one of the things I've come to miss in Clips is the ability to define rules dynamically. I've tried using eval:&lt;/div&gt;&lt;pre&gt;CLIPS&gt; (eval "(defrule blah (whatever) =&gt;)")&lt;br /&gt;[EXPRNPSR3] Missing function declaration for defrule.&lt;br /&gt;FALSE&lt;br /&gt;&lt;/pre&gt;&lt;div&gt;but without success.&lt;/div&gt;&lt;div&gt; &lt;/div&gt;&lt;br /&gt;&lt;div&gt;Whenever such a need has appeared I have used Python to dynamically generate the source code which I then load and run in Clips but I'd prefer to keep it all in one place and since I know better than to have delusions about pyRete performance I *think* this might be the way to go.&lt;br /&gt;&lt;br /&gt;[2007-10-17] Update: Less than an hour after I posted this I learned that there is indeed a way to generate rules dynamically. The function is called &lt;span style="font-style: italic;"&gt;build&lt;/span&gt; and is described in chapter 12.3.6 &lt;span style="font-style: italic;"&gt;Evaluating a Construct within a String&lt;/span&gt; of the &lt;a href="http://www.ghg.net/clips/download/documentation/bpg.pdf"&gt;Clips Basic Programming Guide&lt;/a&gt;. Thanks Gary and Peter for pointing this out.&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-3835022375148858941?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/3835022375148858941/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=3835022375148858941' title='4 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/3835022375148858941'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/3835022375148858941'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2007/10/going-from-clips-to-lisa.html' title='Going from Clips to Lisa'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-3468739216377138213</id><published>2007-10-13T19:00:00.000+02:00</published><updated>2007-10-13T22:56:16.360+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Go'/><category scheme='http://www.blogger.com/atom/ns#' term='AI Player'/><title type='text'>Cracking Go?</title><content type='html'>The October issue of &lt;a href="http://www.spectrum.ieee.org/"&gt;IEEE Spectrum&lt;/a&gt; has a very &lt;a href="http://www.spectrum.ieee.org/oct07/5552"&gt;interesting article&lt;/a&gt; about Go AI players and whether or not one can be created that will beat the best human players. The article is written by Feng-Hsiung Hsu. He was one of the people in the team behind &lt;a href="http://en.wikipedia.org/wiki/IBM_Deep_Blue"&gt;Deep Blue&lt;/a&gt;, the chess-playing program that beat &lt;a href="http://en.wikipedia.org/wiki/Garry_Kasparov"&gt;Garry Kasparov&lt;/a&gt; in 1997, so I guess he knows what he's talking about.&lt;br /&gt;&lt;br /&gt;I was a bit disappointed though, after having read the article, since I got the feeling that the solution was more or less the Deep Blue program running on faster hardware and having been enhanced using null-move pruning and caching results of life-and-death analysis. It all sounds a bit... too simple. Life-and-death analysis is tricky business indeed and it doesn't help at all when he basically says that all the leading Go programmers today are too narrow minded in what they do and that their approach(es) won't ever lead to any decent Go AI players.&lt;br /&gt;&lt;br /&gt;Until he provides some more details about this I'm going to have to &lt;a href="http://artificial-artificial-intelligence.com/index.php/2007/10/11/cracking_go"&gt;agree with Luke Biewald&lt;/a&gt; that he himself may very well have underestimated the problem. It will however be very interesting to see what they come up with in the next few years in the research efforts that Microsoft (where he works) sponsor.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-3468739216377138213?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/3468739216377138213/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=3468739216377138213' title='2 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/3468739216377138213'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/3468739216377138213'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2007/10/cracking-go.html' title='Cracking Go?'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-7937317364158392965</id><published>2007-10-08T21:33:00.000+02:00</published><updated>2007-10-09T18:31:38.765+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='JBoss Drools'/><category scheme='http://www.blogger.com/atom/ns#' term='Puzzles'/><title type='text'>Drools puzzle #2 winner announced</title><content type='html'>&lt;a href="http://it-and-more.blogspot.com/2007/10/drools-puzzle-2-and-winner-is.html"&gt;Gernot&lt;/a&gt; just sent out an e-mail announcing that Scott Reed is the winner of Drools Puzzle #2. I'm looking forward to another puzzle. So hurry up Scott! ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-7937317364158392965?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/7937317364158392965/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=7937317364158392965' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7937317364158392965'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7937317364158392965'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2007/10/drools-puzzle-2-winner-announced.html' title='Drools puzzle #2 winner announced'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-3145782031170156030</id><published>2007-09-24T18:43:00.000+02:00</published><updated>2007-09-24T20:02:53.308+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Erlang'/><title type='text'>Erlang</title><content type='html'>All &lt;a href="http://www.tbray.org/ongoing/When/200x/2007/09/22/Erlang"&gt;the&lt;/a&gt; &lt;a href="http://ola-bini.blogspot.com/2007/09/rubyerlang-concurrency.html"&gt;cool&lt;/a&gt; &lt;a href="http://patricklogan.blogspot.com/2007/09/postmodern-io.html"&gt;kids&lt;/a&gt; are doing it! &lt;a href="http://www.erlang.org/"&gt;Erlang&lt;/a&gt; seems to be all the rage at the moment. It's (almost) difficult to find a blog that isn't about trying or learning Erlang. The first time I heard about it I thought it was a joke, then I saw &lt;a href="http://video.google.com/videoplay?docid=-5830318882717959520"&gt;the video&lt;/a&gt; and was sort of convinced it was a joke but when I saw &lt;a href="http://www.amazon.com/Programming-Erlang-Software-Concurrent-World/dp/193435600X/ref=pd_bbs_sr_1/102-4101386-4484930?ie=UTF8&amp;amp;s=books&amp;amp;qid=1190654675&amp;amp;sr=1-1"&gt;Joe's book&lt;/a&gt; in the book store last week I thought I'd give it a try.&lt;br /&gt;&lt;br /&gt;The promise of Erlang is that it's a breeze to write highly parallel, distributed, fault-tolerant systems. Apparently an Erlang program run on a quad-core computer will run (close to) four times as fast without changes to source code.&lt;br /&gt;&lt;br /&gt;I haven't finished the book or anything so I can't really say much about the language yet. It looks interesting but I'm not sure it will live up to the expectations. But it will at least give me a decent excuse to get a dual-core computer ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-3145782031170156030?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/3145782031170156030/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=3145782031170156030' title='1 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/3145782031170156030'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/3145782031170156030'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2007/09/erlang.html' title='Erlang'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-4724855605594620735</id><published>2007-09-13T22:51:00.000+02:00</published><updated>2007-09-14T21:25:10.358+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><category scheme='http://www.blogger.com/atom/ns#' term='Puzzles'/><title type='text'>Drools puzzle #2</title><content type='html'>The &lt;a href="http://blog.athico.com/2007/08/drools-puzzle-round-2-familiy-puzzle.html"&gt;second Drools puzzle&lt;/a&gt; was a lot trickier than the first one. It took a while for me to figure out a solution I was content with. I re-wrote the application three times before settling on &lt;a href="http://www.pulp.se/clips/drools-puzzle-2.zip"&gt;this one&lt;/a&gt;. It's a rather simple generate-and-test solution but it works and it's not unreasonably slow.&lt;pre&gt;CLIPS&gt; (batch ".../family.bat")&lt;br /&gt;TRUE&lt;br /&gt;CLIPS&gt; (clear)&lt;br /&gt;CLIPS&gt; (unwatch all)&lt;br /&gt;CLIPS&gt; (load "family.clp")&lt;br /&gt;:::%%%********!!&lt;br /&gt;TRUE&lt;br /&gt;CLIPS&gt; (solve)&lt;br /&gt;Who is married to whom and what are their sons called?&lt;br /&gt;1 solution(s).&lt;br /&gt;Snyder is married to Edith and their son is Victor&lt;br /&gt;Locker is married to Doris and their son is Henry&lt;br /&gt;Abel is married to Luisa and their son is Albert&lt;br /&gt;&lt;br /&gt;CLIPS&gt; &lt;/pre&gt;I hope more people have sent in solutions this time and that the next puzzle will be as good as this one.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-4724855605594620735?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/4724855605594620735/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=4724855605594620735' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/4724855605594620735'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/4724855605594620735'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2007/09/drools-puzzle-2.html' title='Drools puzzle #2'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-6363881686901529868</id><published>2007-09-12T21:20:00.000+02:00</published><updated>2007-09-12T21:35:23.546+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><title type='text'>Beautiful code</title><content type='html'>Today, I stumbled on a Google Tech Talk video where &lt;a href="http://video.google.com/videoplay?docid=-1031789501179533828&amp;q=3+beautiful+quicksort&amp;amp;total=1&amp;start=0&amp;amp;num=10&amp;so=0&amp;amp;type=search&amp;plindex=0"&gt;Jon Bentley talks about three implementations of Quicksort&lt;/a&gt;. Apparently it was his contribution to the book &lt;a href="http://www.oreilly.com/catalog/9780596510046/index.html"&gt;Beautiful Code&lt;/a&gt; (O'Reilly). His chapter is called &lt;span style="font-style: italic;"&gt;The Most Beautiful Code I Never Wrote&lt;/span&gt;. There are several interesting chapter titles and all author royalties are donated to &lt;a href="http://www.amnesty.org/"&gt;Amnesty International&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I am *so* ordering this book.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-6363881686901529868?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/6363881686901529868/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=6363881686901529868' title='2 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/6363881686901529868'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/6363881686901529868'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2007/09/beautiful-code.html' title='Beautiful code'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-831231543540804042</id><published>2007-09-02T22:30:00.001+02:00</published><updated>2007-09-14T21:24:13.134+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><category scheme='http://www.blogger.com/atom/ns#' term='Puzzles'/><title type='text'>The stable marriage problem.</title><content type='html'>Awaiting the next &lt;a href="http://blog.athico.com/2007/08/drools-puzzles.html"&gt;Drools puzzle&lt;/a&gt; I decided to write a solver for the &lt;a href="http://en.wikipedia.org/wiki/Stable_marriage_problem"&gt;stable marriage problem&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Given a set of N men and N women, marry them off in pairs after each man has ranked the women in order of preference from 1 to N, {w1, ..., wN} and each woman has done likewise, {m1, ..., mN}. If the resulting set of marriages contains no pairs of the form {mA,wB}, {mC,wD} such that mA prefers wD to wB and wD prefers mA to mC, the marriage is said to be stable.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;It's an old problem and you can read lots more about it &lt;a href="http://www.csee.wvu.edu/%7Eksmani/courses/fa01/random/lecnotes/lecture5.pdf"&gt;here&lt;/a&gt; [PDF], &lt;a href="http://www1.cs.columbia.edu/%7Eevs/intro/stable/writeup.html"&gt;here&lt;/a&gt; and &lt;a href="http://www.cs.duke.edu/%7Emlittman/courses/Archive/cps130-97/lectures/lect13/lect13.html"&gt;here&lt;/a&gt;, and there's a solver Applet &lt;a href="http://www.dcs.gla.ac.uk/research/algorithms/stable/EGSapplet/EGS.html"&gt;here&lt;/a&gt;. I haven't googled for source code and solutions to the problem but there are some descriptions of algorithms and ways to go about it (using a procedural language) in the links above.&lt;br /&gt;&lt;br /&gt;I aimed at a small rule set so it might not be super efficient but I don't think it's particularly bad either. On my machine the limit seems to be at around 15, 16 men and women. If you try it with more than that it takes a long, *long* time to finish. I should probably also mention that my solver disagrees sometimes with the solver applet I linked to above. I do believe that the solver applet is wrong though but if you can spot mistakes in &lt;a href="http://www.pulp.se/clips/stable-marriage-problem.zip"&gt;my code&lt;/a&gt;, please let me know.&lt;br /&gt;&lt;br /&gt;Enjoy!&lt;br /&gt;&lt;pre&gt;CLIPS&gt; (batch "C:/Program/CLIPS/drools-puzzle/marriage.bat")&lt;br /&gt;TRUE&lt;br /&gt;CLIPS&gt; (clear)&lt;br /&gt;CLIPS&gt; (load "marriage.clp")&lt;br /&gt;%%%***&lt;br /&gt;TRUE&lt;br /&gt;CLIPS&gt; (load "marriage-data-16.clp")&lt;br /&gt;$&lt;br /&gt;TRUE&lt;br /&gt;CLIPS&gt; (reset)&lt;br /&gt;CLIPS&gt; (run)&lt;br /&gt;man-3 (#10) is married to woman-1 (#4)&lt;br /&gt;man-11 (#4) is married to woman-8 (#9)&lt;br /&gt;man-16 (#3) is married to woman-14 (#1)&lt;br /&gt;man-8 (#2) is married to woman-4 (#2)&lt;br /&gt;man-14 (#6) is married to woman-3 (#3)&lt;br /&gt;man-5 (#1) is married to woman-2 (#3)&lt;br /&gt;man-1 (#11) is married to woman-11 (#9)&lt;br /&gt;man-4 (#1) is married to woman-6 (#2)&lt;br /&gt;man-15 (#1) is married to woman-15 (#2)&lt;br /&gt;man-13 (#2) is married to woman-16 (#2)&lt;br /&gt;man-12 (#5) is married to woman-10 (#1)&lt;br /&gt;man-7 (#5) is married to woman-9 (#1)&lt;br /&gt;man-9 (#8) is married to woman-5 (#8)&lt;br /&gt;man-10 (#1) is married to woman-7 (#5)&lt;br /&gt;man-6 (#5) is married to woman-12 (#1)&lt;br /&gt;man-2 (#2) is married to woman-13 (#1)&lt;br /&gt;CLIPS&gt; &lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-831231543540804042?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/831231543540804042/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=831231543540804042' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/831231543540804042'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/831231543540804042'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2007/09/stable-marriage-problem.html' title='The stable marriage problem.'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-8037275423717176968</id><published>2007-08-26T10:24:00.000+02:00</published><updated>2007-08-26T16:27:38.296+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='pyRete'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Duck typing, again.</title><content type='html'>I have &lt;a href="http://commentsarelies.blogspot.com/2006/09/duck-typing-and-rete.html"&gt;previously posted about duck-typing&lt;/a&gt; and whether or not it should be possible to write a rule that doesn't check the Fact object's class name but instead check the existance of the attributes that are tested in Alpha and Beta nodes.&lt;br /&gt;&lt;br /&gt;A couple of months ago I concluded that it's best to leave that sort of thing out and instead require that the rule's definition include class names for each variable.&lt;br /&gt;&lt;br /&gt;However, things are not so simple. Just because pyRete won't allow you to compile a "duck-typed" Rete Network doesn't mean the Fact objects passing through it will play nice as well.&lt;br /&gt;&lt;br /&gt;Consider the following rule:&lt;pre&gt;&gt;&gt;&gt; @pyRete.rule&lt;br /&gt;... def foo(a= Foo):&lt;br /&gt;...   if a.n &gt; 10:&lt;br /&gt;...     pass&lt;/pre&gt;and this Fact class:&lt;pre&gt;&gt;&gt;&gt; class Foo(pyRete.Fact):&lt;br /&gt;...   def __init__(self, n):&lt;br /&gt;...     self.n = n&lt;/pre&gt;Using it as defined won't cause any problems but unfortunately (or luckily, depends a bit on how you view the world ;-) there's no stopping this type of behaviour:&lt;pre&gt;&gt;&gt;&gt; obj = Foo(n = 10)&lt;br /&gt;&gt;&gt;&gt; obj.n&lt;br /&gt;10&lt;br /&gt;&gt;&gt;&gt; &lt;span style="color: rgb(255, 0, 0);"&gt;del obj.n&lt;/span&gt;&lt;br /&gt;&gt;&gt;&gt; obj.n&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;Traceback (most recent call last):&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;  File "&lt;/span&gt;&lt;pyshell#7 style="color: rgb(255, 0, 0);"&gt;", line 1, in &lt;module&gt;&lt;br /&gt;  obj.n&lt;br /&gt;AttributeError: 'Foo' object has no attribute 'n'&lt;/module&gt;&lt;/pyshell#7&gt;&lt;/pre&gt;That makes it obviuos that the ObjectType node test won't be sufficient for pyRete. Even though the above (using &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;del&lt;/span&gt;&lt;/span&gt; on an instance's property) is probably rather rare there's no way to *know* if the public interface of the Fact instance has been modified or not.&lt;br /&gt;&lt;br /&gt;I think I'm going to have to implement the Rete Network more or less as described in Forgy's 1979 paper where he tests the length and the contents of a Fact object in order to categorize it properly. In the 1982 article it appears as if these tests have vanished and have been replaced by a "type" symbol in the first element of each fact. Which, I believe, is now usually known as an ObjectType node.&lt;br /&gt;&lt;br /&gt;Now, the only question is: Should I distribute the tests among the Alpha and Beta nodes or should I extend the ObjectType node with some property existance tests as well?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-8037275423717176968?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/8037275423717176968/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=8037275423717176968' title='15 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8037275423717176968'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/8037275423717176968'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2007/08/duck-typing-again.html' title='Duck typing, again.'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>15</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-7209876019566768492</id><published>2007-08-20T21:17:00.000+02:00</published><updated>2007-09-15T09:21:56.229+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='Rule Engine'/><category scheme='http://www.blogger.com/atom/ns#' term='JBoss Rules'/><category scheme='http://www.blogger.com/atom/ns#' term='CLIPS'/><category scheme='http://www.blogger.com/atom/ns#' term='Puzzles'/><title type='text'>Drools puzzle #1</title><content type='html'>I completely missed the &lt;a href="http://blog.athico.com/2007/08/drools-puzzles.html"&gt;announcement for the Drools puzzles&lt;/a&gt; but it looks like lots of fun. Here's &lt;a href="http://www.pulp.se/clips/drools-puzzle-1.zip"&gt;my solution&lt;/a&gt; to the &lt;a href="http://blog.athico.com/2007/08/drools-puzzle-round-1-ages-of-sons.html"&gt;first puzzle&lt;/a&gt; using Clips. You run it by executing the batch file:&lt;br /&gt;&lt;pre&gt;CLIPS&gt; (batch "... /ages-of-the-sons.bat")&lt;br /&gt;TRUE&lt;br /&gt;CLIPS&gt; (clear)&lt;br /&gt;CLIPS&gt; (load "ages-of-the-sons.clp")&lt;br /&gt;%%!***&lt;br /&gt;TRUE&lt;br /&gt;CLIPS&gt; (generate-ages)&lt;br /&gt;FALSE&lt;br /&gt;CLIPS&gt; (run)&lt;br /&gt;What are the ages of the three sons of the old man?&lt;br /&gt;2, 2 and 9&lt;br /&gt;10 rules fired&lt;br /&gt;40 mean number of facts (44 maximum).&lt;br /&gt;0 mean number of instances (0 maximum).&lt;br /&gt;4 mean number of activations (8 maximum)&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-7209876019566768492?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/7209876019566768492/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=7209876019566768492' title='3 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7209876019566768492'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7209876019566768492'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2007/08/drools-puzzle-1.html' title='Drools puzzle #1'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-3030882332110178517</id><published>2007-08-16T16:29:00.000+02:00</published><updated>2007-08-16T21:09:27.869+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Rete Algorithm'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='pyRete'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Even more profiling...</title><content type='html'>Ok, this will be my last post about (this round of) profiling. Honest!&lt;br /&gt;&lt;br /&gt;I have now decided on which implementation to use so I thought I should take some time to describe it.&lt;br /&gt;&lt;br /&gt;It *is* a bit of a hack, but it allows me to remove the eval calls in the OO implementation. It's not as fast as the flattened, procedural Rete implementation. But it's quite close and, more importantly, I believe it's easier to implement and add optimizations to it.&lt;br /&gt;&lt;br /&gt;Here's the profiling output:&lt;pre&gt;using 4000 facts&lt;br /&gt;plain OO impl. Assert took 6.0490000248 s and produced 79735 facts&lt;br /&gt;hack OO impl. Assert took 3.16399979591 s and produced 79735 facts&lt;br /&gt;inline impl. Assert took 2.68400001526 s and produced 79735 facts&lt;/pre&gt;As you can see, the difference is about half a second between the hack and the inline version.&lt;pre&gt;using 10000 facts&lt;br /&gt;plain OO impl. Assert took 15.2119998932 s and produced 199735 facts&lt;br /&gt;hack OO impl. Assert took 7.88100004196 s and produced 199735 facts&lt;br /&gt;inline impl. Assert took 7.49099993706 s and produced 199735 facts&lt;/pre&gt;Luckily, that difference remains "constant" for any size of the data set and the difference between the plain OO and the hack remain at about 50%.&lt;br /&gt;&lt;br /&gt;Now, to talk about how it's done. In the plain OO version, the Rete Compiler makes an instance of AlphaTestNode like this:&lt;pre&gt;&gt;&gt;&gt; a1 = rete.AlphaTestNode('a.n &gt; 10')&lt;/pre&gt;The AlphaTestNode is defined as:&lt;pre&gt;&gt;&gt;&gt; class AlphaTestNode(object):&lt;br /&gt;...     def __init__(self, expr):&lt;br /&gt;...         self.expr = expr&lt;br /&gt;...         self.nexts = []&lt;br /&gt;...     &lt;br /&gt;...         self.right_activate = self.activate&lt;br /&gt;...     &lt;br /&gt;...     def activate(self, tag, token):&lt;br /&gt;...         if eval(self.expr, token):&lt;br /&gt;...             for next in self.nexts:&lt;br /&gt;...                 next.right_activate(tag, token)&lt;/pre&gt;whenever &lt;span style="font-style: italic;"&gt;activate&lt;/span&gt; is called on the a1 instance it will evaluate the expression 'a.n &gt; 10' using the &lt;span style="font-style: italic;"&gt;token&lt;/span&gt;, and all bindings (variable name and value) found in it, as execution scope. In order to remove the expensive eval call we have to "inline" the expression to be tested. A macro would have been very handy here (the Lisp type) but I don't expect them to show up in Python any time soon so instead we'll have to have a work-around. If we remove the class definition and replace it with a function:&lt;pre&gt;&gt;&gt;&gt; def make_AlphaTestNode(expr, var):&lt;br /&gt;...     class_definition = \&lt;br /&gt;... """class GeneratedAlphaTestNode(object):&lt;br /&gt;...     def __init__(self):&lt;br /&gt;...         self.nexts = []&lt;br /&gt;...         self.right_activate = self.activate&lt;br /&gt;...     &lt;br /&gt;...     def activate(self, tag, token):&lt;br /&gt;...         %s = token['%s']&lt;br /&gt;...         if %s:&lt;br /&gt;...             for next in self.nexts:&lt;br /&gt;...                 next.right_activate(tag, token)""" % (var, var, expr)&lt;br /&gt;...             &lt;br /&gt;...     exec(compiler.compile(class_definition, '__generated__', 'exec'))&lt;br /&gt;...     return GeneratedAlphaTestNode()&lt;/pre&gt;we can make an instance of AlphaTestNode equivalent to the one above (a1) using:&lt;pre&gt;&gt;&gt;&gt; a1 = rete.make_AlphaTestNode('a.n &gt; 10', 'a')&lt;/pre&gt;The "extra" parameter ('a') is used to generate a statement to bind the variable (a), found in the token, locally before execution. This is necessary because we cannot specify &lt;span style="font-style: italic;"&gt;token&lt;/span&gt; as the execution context (as we can when using eval or exec).&lt;br /&gt;&lt;br /&gt;The parameter &lt;span style="font-style: italic;"&gt;class_definition&lt;/span&gt; will look like this when it's sent to the &lt;span style="font-style: italic;"&gt;compile &lt;/span&gt;method:&lt;pre&gt;&gt;&gt;&gt; class GeneratedAlphaTestNode(object):&lt;br /&gt;...     def __init__(self):&lt;br /&gt;...         self.nexts = []&lt;br /&gt;...         self.right_activate = self.activate&lt;br /&gt;...&lt;br /&gt;...     def activate(self, tag, token):&lt;br /&gt;...         &lt;span style="color: rgb(255, 0, 0);"&gt;a = token['a']&lt;/span&gt;&lt;br /&gt;...         &lt;span style="color: rgb(255, 0, 0);"&gt;if a.n &gt; 10:&lt;/span&gt;&lt;br /&gt;...             for next in self.nexts:&lt;br /&gt;...                 next.right_activate(tag, token)&lt;/pre&gt;executing the code object that is returned by &lt;span style="font-style: italic;"&gt;compile &lt;/span&gt;causes the class &lt;span style="font-style: italic;"&gt;GeneratedAlphaTestNode&lt;/span&gt; to become available in local scope so all that is left to do is to make an instance and return it.&lt;br /&gt;&lt;br /&gt;Ugly!? Incomprehensible!? Yes, sir! But it shaves a lot of time off of fact evaluation in the Rete Network so I don't care!&lt;br /&gt;&lt;br /&gt;Next up is to implement a second pass in the compilation step to remove "un-necessary" nodes. This is actually the main reason I chose this design over the inlined, flattened, procedural implementation. Optimizing travseral of the Rete Network in an OO implementation means traversing it and moving "next pointers" from one node to another whilst in procedural code I would have to handle it on the level of source code (text) which feels a lot harder (I can't think of an easy way to do it so if you've got suggestions please let me know).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-3030882332110178517?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/3030882332110178517/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=3030882332110178517' title='2 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/3030882332110178517'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/3030882332110178517'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2007/08/even-more-profiling.html' title='Even more profiling...'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-6286965490178016248</id><published>2007-08-13T23:02:00.000+02:00</published><updated>2007-08-14T18:45:54.765+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Rete Algorithm'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='pyRete'/><title type='text'>Corrected profiling results</title><content type='html'>It's official: I'm a dimwit!&lt;br /&gt;&lt;br /&gt;The inline versions of the profiling tests I presented last week are wrong. The correct time for the plain inline version is in fact somewhere between a third to half of the OO implementation.&lt;br /&gt;&lt;br /&gt;The inlined version was incorrectly using eval calls to test the conditions. The whole speed-up stems from replacing, for example,&lt;pre&gt;&gt;&gt;&gt; eval('a.n &gt; 10', token)&lt;/pre&gt;with&lt;pre&gt;&gt;&gt;&gt; a = token['a']&lt;br /&gt;&gt;&gt;&gt; a.n &gt; 10&lt;/pre&gt;The reason that I can lose the eval in the inline version is because it works by translating source code from the rule definition to regular Python code, which is then compiled into a function object which is invoked at run-time.&lt;br /&gt;&lt;br /&gt;In the OO-version, the test to be performed is bound to the instance of the node and since the activate method is "pre-defined" we need eval in order to execute whatever test is stored in the instance. Most activate methods besically looks like this:&lt;pre&gt;&gt;&gt;&gt; def activate(self, tag, token):&lt;br /&gt;...   if eval(self.test, token):&lt;br /&gt;...     # store in mem and propagate&lt;/pre&gt;There might be a way of doing the same sort of "trick" to get rid of the eval in the OO-implementation as well. I'll have to think about that though.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-6286965490178016248?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/6286965490178016248/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=6286965490178016248' title='1 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/6286965490178016248'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/6286965490178016248'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2007/08/corrected-profiling-results.html' title='Corrected profiling results'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-7410457649299186032</id><published>2007-08-09T21:24:00.000+02:00</published><updated>2007-08-10T13:37:56.429+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Rete Algorithm'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='pyRete'/><title type='text'>More profiling</title><content type='html'>Turns out I was right about the error in the current pyRete version. I hand made a Rete network by connecting nodes from the refactored Rete implementation and it turned out to be as fast as (and sometimes faster than) the one based on dictionaries/functions.&lt;br /&gt;&lt;br /&gt;Whilst the one based on dicts and functions is in some was easier to handle and deal with it also has some serious drawbacks. First and foremost it saves state at *each* node. This can probably be dealt with but then I'd have to write some boiler-plate code to handle when and when not to store tokens and that was the whole point of the implementation, to reduce the boiler-plate code. Secondly, it can be harder to wrap your head around since you'd have to look at both where the function is defined and where it is called in order to find out the exact behaviour of a node. It's not magic or anything but it may be confusing at first.&lt;br /&gt;&lt;br /&gt;Given these two drawbacks of the dicts and functions implementation I decided that I wasn't going to use it. At all... unless I could find a way to perform function inlining and that the inlined version was faster than an optimized OO-Rete network.&lt;br /&gt;&lt;br /&gt;So, now I've got no less than six implementations of a small Rete network[1]:&lt;br /&gt;1) the current pyRete implementation (which is quite obviously just plain wrong).&lt;br /&gt;2) the experimental dicts and functions implementation&lt;br /&gt;3) the refactored OO-implementation&lt;br /&gt;4) an optimized version of 3[2]&lt;br /&gt;5) an implementation using function inlining based on 2&lt;br /&gt;6) an optimized version of 5[2]&lt;br /&gt;&lt;br /&gt;Here are the results (I've run these about ten times and the numbers below are typical) for 2 to 6 using 4000 facts:&lt;br /&gt;&lt;br /&gt;2) took 6.58899998665 s =&gt; 79735 facts&lt;br /&gt;3) took 5.98900008202 s =&gt; 79735 facts&lt;br /&gt;4) took 5.70799994469 s =&gt; 79735 facts&lt;br /&gt;5) took 5.63800001144 s =&gt; 79735 facts&lt;br /&gt;6) took 5.52800011635 s =&gt; 79735 facts&lt;br /&gt;&lt;br /&gt;Inlining apparently speeds up the matching but not with much and I'm not sure it's worth the effort. It would be cool though to generate a large blurb of code and see if it can be optimized by some other program like &lt;a href="http://psyco.sourceforge.net/"&gt;psyco&lt;/a&gt; or possibly one of the &lt;a href="http://bytecodehacks.sourceforge.net/"&gt;bytecodehacks&lt;/a&gt; or some such thing. I haven't looked for optimizers but I believe I will have to if I am going to get something out of the inlining implementation.&lt;br /&gt;&lt;br /&gt;All in all. This week's been good fun looking at different implementations but it's time to get back to work and make sure that the pyRete code in the subversion repository works as expected.&lt;br /&gt;&lt;br /&gt;[1] Here is the Rete network I'm using. Memory nodes are not shown.&lt;br /&gt;&lt;pre&gt;#                       TYPE&lt;br /&gt;#                       &lt;br /&gt;#   TOP         ALPHA {--+     TYPE&lt;br /&gt;#                            &lt;br /&gt;#    +--} JOIN {--+    ALPHA {--+&lt;br /&gt;#                      &lt;br /&gt;#           +--} JOIN {--+&lt;br /&gt;#                 &lt;br /&gt;#                  +--} BETA&lt;br /&gt;#                        &lt;br /&gt;#                         +--} PROD&lt;/pre&gt;[2] I have removed a few "unneccessary" memory nodes. But since I believe that that sentence can easily be misunderstood I'll try to explain a bit more.&lt;br /&gt;I have, for example removed storage of tokens at nodes which are never "read" in other parts of the network. For example the Beta Node places Tokens directly into the Production Node instead of going via a Beta Memory Node. It is "last" in the network and the Beta Memory would have fed the Production Node. Also, the first AlphaMemory has been converted into a LeftInputAdapter and sends Tokens directly to the second Join instead of going thru a DummyJoin with a DummyTopNode.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-7410457649299186032?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/7410457649299186032/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=7410457649299186032' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7410457649299186032'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/7410457649299186032'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2007/08/more-profiling.html' title='More profiling'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-5350494243727059614</id><published>2007-08-08T22:08:00.000+02:00</published><updated>2007-08-10T13:42:31.393+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Rete Algorithm'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='pyRete'/><title type='text'>Some profiling results</title><content type='html'>I've made a small test to see how my experimental Rete implementation is performing. I'm using a very simple rule that generates a lot of tokens:&lt;pre&gt;&gt;&gt;&gt; @pyRete.Rule&lt;br /&gt;... def rule(a = Foo, b = Foo):&lt;br /&gt;...   if a.n &gt; 10 and b.n &lt; 20 and a.n &gt; b.n:&lt;br /&gt;...     print a.n, b.n&lt;/pre&gt;where Foo is a value object.&lt;br /&gt;&lt;br /&gt;I'm comparing against the pyRete version currently in Subversion and the results are not looking good. In fact, they're looking *so* bad, I believe it is because of an error in the pyRete implementation rather than a difference in time for object+ method lookups (which is what I hoped to test).&lt;br /&gt;&lt;br /&gt;I'm only timing the assert_fact method calls (and the index loop):&lt;pre&gt;... start = time.time()&lt;br /&gt;... for i in range(size):&lt;br /&gt;...   pyRete.assert_fact(Foo(n=i))&lt;br /&gt;... stop = time.time()&lt;/pre&gt;so Rete compilation, parsing and such is not included. Here are the results:&lt;br /&gt;&lt;br /&gt;using 50 facts [Foo(0), Foo(1), ..., Foo(49)]&lt;br /&gt;rule: +O+O+A+A+B+P&lt;br /&gt;pyRete implementation took &lt;span style="COLOR: rgb(255,0,0)"&gt;0.210000038147 s&lt;/span&gt; =&gt; 735 facts&lt;br /&gt;expRete implementation took 0.0599999427795 s =&gt; 735 facts&lt;br /&gt;&lt;br /&gt;using 100 facts [Foo(0), Foo(1), ..., Foo(99)]&lt;br /&gt;rule: +O+O+A+A+B+P&lt;br /&gt;pyRete implementation took &lt;span style="COLOR: rgb(255,0,0)"&gt;0.871000051498 s&lt;/span&gt; =&gt; 1735 facts&lt;br /&gt;expRete implementation took 0.149999856949 s =&gt; 1735 facts&lt;br /&gt;&lt;br /&gt;using 200 facts [Foo(0), Foo(1), ..., Foo(199)]&lt;br /&gt;rule: +O+O+A+A+B+P&lt;br /&gt;pyRete implementation took &lt;span style="COLOR: rgb(255,0,0)"&gt;3.51600003242 s&lt;/span&gt; =&gt; 3735 facts&lt;br /&gt;expRete implementation took 0.319999933243 s =&gt; 3735 facts&lt;br /&gt;&lt;br /&gt;using 300 facts [Foo(0), Foo(1), ..., Foo(299)]&lt;br /&gt;rule: +O+O+A+A+B+P&lt;br /&gt;pyRete implementation took &lt;span style="COLOR: rgb(255,0,0)"&gt;7.84099984169 s&lt;/span&gt; =&gt; 5735 facts&lt;br /&gt;expRete implementation took 0.481000185013 s =&gt; 5735 facts&lt;br /&gt;&lt;br /&gt;using 400 facts [Foo(0), Foo(1), ..., Foo(399)]&lt;br /&gt;rule: +O+O+A+A+B+P&lt;br /&gt;pyRete implementation took &lt;span style="COLOR: rgb(255,0,0)"&gt;14.0199999809 s&lt;/span&gt; =&gt; 7735 facts&lt;br /&gt;expRete implementation took 0.671000003815 s =&gt; 7735 facts&lt;br /&gt;&lt;br /&gt;Clearly, something's not right. Going from 300 to 400 fact objects shouldn't take nearly twice as long.&lt;br /&gt;&lt;br /&gt;Now, in order to be fair I should also mention that the experimental Rete implementation is &lt;span style="FONT-STYLE: italic"&gt;hand-made&lt;/span&gt; (but it's topology is identical to that pyRete generates) and doesn't include things like callbacks for debugging, compilation, activations and statistics. However, that cannot account for *all* of the extra time, maybe some of it. I don't know.&lt;br /&gt;&lt;br /&gt;I will try to make a hand-made, slimmed OO Rete Network tomorrow to see if I can get a fair comparison.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-5350494243727059614?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/5350494243727059614/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=5350494243727059614' title='2 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/5350494243727059614'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/5350494243727059614'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2007/08/some-profiling-results.html' title='Some profiling results'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-4857822395625280331</id><published>2007-07-30T20:37:00.000+02:00</published><updated>2007-07-30T22:50:20.272+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Rete Algorithm'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming'/><category scheme='http://www.blogger.com/atom/ns#' term='pyRete'/><title type='text'>Experimental Rete implementation</title><content type='html'>Last week I tried a "new" approach for pyRete's Rete implementation. At the moment I'm using a number of objects linked together such that they make up a Directed Acyclic Graph. Which also happens to be the way most other implementations handle it (I think).&lt;br /&gt;&lt;br /&gt;Take for example object type tests. In pyRete they are implemented such that the Rule Compiler instantiates an ObjectTypeNode and initializes it with the type to test against. Later on it connects the instance to the &lt;span&gt;"next"&lt;/span&gt; nodes in the Rete Network (usually Alpha Nodes). The ObjectTypeNode basically looks like this:&lt;pre&gt;&gt;&gt;&gt; class ObjectTypeNode(Node):&lt;br /&gt;...   def __init__(self, type):&lt;br /&gt;...     self.type = type&lt;br /&gt;...     self.successors = []&lt;br /&gt;...   def activate(self, tag, timestamp, obj):&lt;br /&gt;...     if obj.__class__.__name__ == self.type:&lt;br /&gt;...       for node in self.successors:&lt;br /&gt;...         node.activate(tag, timestamp, obj)&lt;/pre&gt;The above is of course grossly simplified, for starters it doesn't have any error checking, but it's enough to convey the general idea.&lt;br /&gt;&lt;br /&gt;There are several other types of nodes as well. They all have a list of successors and at least one activation method (Join Nodes have two). When a fact is asserted, it is propagated through the Rete Network via the activation methods of each node.&lt;br /&gt;&lt;br /&gt;There are several problems with my current approach and it's no secret that I've been trying to clean up and simplify both the Rete Implementation, the Rule Parser and the Rule Compiler. However, a simple, efficient implementation using an object-based DAG have so far eluded me. So last week I tried to use functions (instead of objects) together with a hash table to provide, among other things, storage (it replaces the memory node objects).&lt;br /&gt;&lt;br /&gt;So far, I've only got some very simple tests working but the code is smaller, cleaner and faster. The ObjectTypeNode above is replaced by:&lt;pre&gt;&gt;&gt;&gt; class Rete(dict):&lt;br /&gt;...   def __init__(self):&lt;br /&gt;...     self["OBJ"] = {}&lt;br /&gt;...   def make_object_type_node(self, var, value):&lt;br /&gt;...     key = (var, '__class__.__name__', value)&lt;br /&gt;...     def activate(tag, o):&lt;br /&gt;...       if getattr(getattr(o, '__class__'), '__name__') == value:&lt;br /&gt;...         token = Token()&lt;br /&gt;...         token[var] = o&lt;br /&gt;...         if tag == '+':&lt;br /&gt;...           self["MEM"][key].append(token)&lt;br /&gt;...         elif tag == '-':&lt;br /&gt;...           self["MEM"][key].remove(token)&lt;br /&gt;...         self.propagate(tag, token, key)&lt;br /&gt;...     self["OBJ"][key] = activate&lt;br /&gt;...     self["MEM"][key] = []&lt;br /&gt;...     self["NEXT"][key] = []&lt;br /&gt;...     return key&lt;/pre&gt;What happens here is that the Rule Compiler calls the make_object_type_node instead of instantiating an object. The method calculates a unique key, defines the activate function and registers it in the common hash table. It also registers two empty lists which are used as output memory ("MEM") and as a list of succesor nodes ("NEXT").&lt;br /&gt;&lt;br /&gt;The object type test is performed within the activate method and uses a common propagate method, which looks like this:&lt;pre&gt;&gt;&gt;&gt; def propagate(self, tag, obj, key):&lt;br /&gt;...   [self[dictionary][_key](tag, obj) for _key, dictionary in self["NEXT"][key]]&lt;/pre&gt;One of the *really* good things with it is that those two lines can be used for all types of nodes. The Rule Compiler only needs to register which dictionary the next node is found in (usually "ALPHA" for Object type tests) and which key it has.&lt;br /&gt;&lt;br /&gt;One of the bad things with this approach is that all of the function calls are nested so it might not work too great for large Rete Networks.&lt;br /&gt;&lt;br /&gt;The idea was actually to generate one function that would have *all* tests (for one rule) laid out as a procedural function which would update a common (to all rules) storage of some sort. I'm not 100% sure of what it would look like and since Python doesn't have macros I can't really do it either so this will do for now.&lt;br /&gt;&lt;br /&gt;Once I get the more complicated things working (Not Nodes) I'll let you know if it's better or worse than my current implementation.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-4857822395625280331?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/4857822395625280331/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=4857822395625280331' title='2 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/4857822395625280331'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/4857822395625280331'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2007/07/experimental-rete-implementation.html' title='Experimental Rete implementation'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-4924384879704171351</id><published>2007-07-13T18:57:00.000+02:00</published><updated>2007-07-13T19:09:57.154+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pyRete'/><title type='text'>pyRete update</title><content type='html'>It's been awhile since I've mentioned what's happening with pyRete. I'm still in the middle of refactoring the Rete implementation and, more importantly, the rule compiler (which is a bloated mess at the moment). I wanted to make most of these changes in one fell swoop, but I soon realized that I'm not even half the programmer required to pull that off. So instead I'm slowly (but steadily) modifying Rete node implementations and the rule compiler. Among other things, the single Not node works as expected now but there's still lots of things to do...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-4924384879704171351?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/4924384879704171351/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=4924384879704171351' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/4924384879704171351'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/4924384879704171351'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2007/07/pyrete-update.html' title='pyRete update'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8366702.post-6592616368863962355</id><published>2007-07-05T21:45:00.000+02:00</published><updated>2007-07-05T22:39:57.512+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Free Software'/><category scheme='http://www.blogger.com/atom/ns#' term='Gnu'/><title type='text'>FSFE meeting in Gothenburg</title><content type='html'>I attended a local &lt;a href="http://fsfeurope.org/"&gt;FSFE&lt;/a&gt; meetup here in Gothenburg yesterday. There was some discussion about what the Swedish chapter (FSF-SE) should focus on and what activities to arrange.&lt;br /&gt;&lt;br /&gt;This was my first IRL contact with FSF people but I have previously considered becoming a &lt;a href="http://www.fsfe.org/"&gt;fellow&lt;/a&gt; because I whole-heartedly support the ideas behind Gnu and FSF. The reason I haven't bothered yet is because it's still quite unclear to me what the FSFE and FSF-SE are supposed to do. I know about all of the high profile things they're doing, such as &lt;a href="http://fsfeurope.org/projects/swpat/"&gt;the fight against software patents in Europe&lt;/a&gt;, &lt;a href="http://www.defectivebydesign.org/"&gt;the fight against DRM&lt;/a&gt; and &lt;a href="http://www.fsfe.org/fellows/greve/freedom_bits/six_questions_to_national_standardisation_bodies"&gt;the fight against Microsoft's attempt to get their OO-XML document format approved as an ISO standard&lt;/a&gt;... and that's the problem right there. I find it easier and more natural to define FSFE in terms of what they oppose than anything else, and I'd rather spend my time contributing than protesting.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.coyote.org/"&gt;Jonas&lt;/a&gt; and &lt;a href="http://www.sandklef.com/hesa/"&gt;Henrik&lt;/a&gt; (both are employed by the FSFE) mentioned the &lt;a href="http://selfproject.eu/"&gt;SELF project&lt;/a&gt; as an example of such an activity and I hope that I sometime soon will find it easier to talk about what the FSFE are supporting and contributing to when explaining the organisation and the philosophy to people who've never heard of them.&lt;br /&gt;&lt;br /&gt;At the meeting I suggested local (in and around Gothenburg) projects to support and help schools, libraries and other public institutions with both hard- and software. I believe that the content currently being developed for the &lt;a href="http://laptop.org/"&gt;OLPC&lt;/a&gt; could be real useful in Swedish schools as well and that would really be worth spending time on. I really hope that we can make something useful happen after summer. If you at least promise to try, I'll pay the fee for fellowship. That's a promise :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8366702-6592616368863962355?l=commentsarelies.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://commentsarelies.blogspot.com/feeds/6592616368863962355/comments/default' title='Kommentarer till inlägget'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8366702&amp;postID=6592616368863962355' title='0 kommentarer'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/6592616368863962355'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8366702/posts/default/6592616368863962355'/><link rel='alternate' type='text/html' href='http://commentsarelies.blogspot.com/2007/07/fsfe-meeting-in-gothenburg.html' title='FSFE meeting in Gothenburg'/><author><name>Johan Lindberg</name><uri>http://www.blogger.com/profile/13455767001846504270</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_3hqf70Lx0Mk/Sob8itUp2pI/AAAAAAAAAJQ/hUMomuRSv7M/S220/profile_image.jpg'/></author><thr:total>0</thr:total></entry></feed>
