<?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-2921404189269723440</id><updated>2011-08-21T06:12:52.958-07:00</updated><category term='math'/><category term='emacs'/><category term='squeak'/><category term='oscon'/><category term='smalltalk'/><category term='tutorial'/><category term='mindfulness'/><category term='perl'/><category term='hop'/><category term='brain games'/><category term='wii'/><category term='lisp'/><category term='pushups'/><category term='puzzle'/><category term='programming puzzles'/><category term='algorithms'/><category term='django'/><category term='larry wall'/><category term='life extension'/><category term='oscon09'/><category term='cs'/><category term='programming polyglot'/><category term='rationality'/><category term='meditation'/><category term='firefox'/><category term='compression'/><category term='higher order perl'/><category term='oscon08'/><category term='running'/><category term='babylon 5'/><category term='data structures'/><category term='intelligence'/><category term='git'/><category term='python'/><category term='compilation'/><category term='sushi'/><category term='emacslisp'/><category term='haskell'/><category term='functional programming'/><category term='source code'/><category term='damian conway'/><category term='seaside'/><category term='iq'/><category term='programming languages'/><category term='learning'/><category term='fitness'/><category term='99 problems'/><category term='focus'/><title type='text'>lair of the dustbunny</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default?start-index=101&amp;max-results=100'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>125</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-6096746818971907268</id><published>2010-07-10T22:40:00.000-07:00</published><updated>2010-07-11T09:49:27.373-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='compilation'/><category scheme='http://www.blogger.com/atom/ns#' term='source code'/><category scheme='http://www.blogger.com/atom/ns#' term='compression'/><title type='text'>Compilation and Compression</title><content type='html'>Quick, which is bigger in general: source code or the resulting compiled executable?&lt;br /&gt;&lt;br /&gt;Try to answer that before reading on. If you are being nit picky consider source code that has no comments and minimal whitespace. And consider *any* sort of language compilation process: python to bytecode, c to machine code, etc.&lt;br /&gt;&lt;br /&gt;Recently while reading "What is Thought?" by Eric Baum (a great mix of AI, computational theory, cognitive science, evolutionary theory) he stated matter-of-factly that when you compile a program it gets bigger. He was using this in analogy with how DNA is (relatively) concise but the things it builds (e.g. all the connections in the human brain) are much, much bigger (in the Shannon information sense). &lt;br /&gt;&lt;br /&gt;That seemed completely wrong to me. Humans are inefficient and verbose. Machines are efficient and concise. Surely some optimizing compiler (or even just a regular compiler) will show my code to be a joke and transform it into a small jewel-like essence that I wasn't capable of at my high level of abstraction.&lt;br /&gt;&lt;br /&gt;And so I started looking at examples. .py to .pyc files. c to binaries. .hs to binaries. And wouldn't you know it, more often then not the resulting "compiled" entity was larger. &lt;br /&gt;&lt;br /&gt;I was surprised that my intuition was so far off on this. I asked around with my co-workers and more often than not they had the same faulty intuition as me. So at least I'm not alone.  But it's strange that my intuition was so far off on this.&lt;br /&gt;&lt;br /&gt;Now I think my intuition is properly adjusted and here's how I think about it now. &lt;br /&gt;&lt;br /&gt;Humans aren't inefficient and verbose. Quite the opposite. Saying something like:&lt;br /&gt;&lt;blockquote&gt;print "hello world"&lt;/blockquote&gt;&lt;br /&gt;as a simple python program is a very terse way of making *lots* of low level details happen. In fact all programming languages (no matter how high or low you think of them) are very abstract (ie concise) compared to what is really going on. Even assembler is a massive collection of shortcuts and abstractions compared to the actual physics going on.&lt;br /&gt;&lt;br /&gt;So no matter what language you are working in you are essentially working with shortcuts that need to actually be fleshed out into more precise and verbosely specific instructions.&lt;br /&gt;&lt;br /&gt;What's really funny is that from reading "What Is Thought" I actually see a deep relationship between compilation and compression that I had never considered before. A little Information Theory is a dangerous thing...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-6096746818971907268?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/6096746818971907268/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=6096746818971907268' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/6096746818971907268'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/6096746818971907268'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2010/06/compilation-and-compression.html' title='Compilation and Compression'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-2879793435422770777</id><published>2010-07-08T21:28:00.000-07:00</published><updated>2010-07-11T09:20:41.605-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming puzzles'/><category scheme='http://www.blogger.com/atom/ns#' term='programming polyglot'/><category scheme='http://www.blogger.com/atom/ns#' term='programming languages'/><title type='text'>Programming Language Gymnasium</title><content type='html'>A list of sites/books/etc for practicing to be a programming language polyglot:&lt;ul&gt;&lt;li&gt;Books:&lt;ul&gt;    &lt;li&gt;Programming Challenges (Skienna)&lt;br /&gt;   &lt;li&gt;Data Structures and Algorithm Analysis in C (Weiss)&lt;br /&gt;   &lt;li&gt;SICP&lt;/ul&gt;&lt;br /&gt;&lt;li&gt;Problem Sets&lt;ul&gt;   &lt;li&gt; 99 problems &lt;a href="https://prof.ti.bfh.ch/hew1/informatik3/prolog/p-99/"&gt;prolog&lt;/a&gt;, &lt;a href="http://www.haskell.org/haskellwiki/99_Haskell_exercises"&gt;haskell&lt;/a&gt;, &lt;a href="http://wiki.python.org/moin/ProblemSets/99%20Prolog%20Problems%20Solutions"&gt;python&lt;/a&gt;, &lt;a href="http://lambda-the-ultimate.org/node/1982"&gt;lisp&lt;/a&gt;, &lt;a href="http://www.codepoetics.com/wiki/index.php?title=Topics:99_Problems_in_other_languages:Oz"&gt;oz&lt;/a&gt;, &lt;a href="http://aperiodic.net/phil/scala/s-99/"&gt;scala&lt;/a&gt;, &lt;a href="http://www.christiankissig.de/cms/index.php/en/programming/28-ocaml/28-99-problems-in-ocaml"&gt;ocaml&lt;/a&gt;, &lt;a href="http://github.com/perl6/perl6-examples"&gt;perl 6&lt;/a&gt;&lt;br /&gt;   &lt;li&gt; &lt;a href="http://projecteuler.net/"&gt;Project Euler&lt;/a&gt;&lt;br /&gt;   &lt;li&gt; &lt;a href="http://codingkata.org/"&gt;Coding Kata&lt;/a&gt;, &lt;a href="http://www.javascriptkata.com/"&gt;javascript&lt;/a&gt; specific&lt;br /&gt;   &lt;li&gt; &lt;a href="http://www.spoj.pl/"&gt;SPOJ&lt;/a&gt;&lt;br /&gt;   &lt;li&gt; &lt;a href="http://www.facebook.com/careers/puzzles.php"&gt;FaceBook puzzles&lt;/a&gt;&lt;br /&gt;   &lt;li&gt; &lt;a href="http://www.pythonchallenge.com/"&gt;Python Challenge&lt;/a&gt;&lt;br /&gt;   &lt;li&gt; &lt;a href="http://www.gowrikumar.com/c/index.html"&gt;C puzzles&lt;/a&gt;&lt;/ul&gt;&lt;br /&gt;&lt;li&gt;Contests&lt;ul&gt;   &lt;li&gt; &lt;a href="http://www.rubyquiz.com/"&gt;Ruby Quiz&lt;/a&gt;&lt;br /&gt;   &lt;li&gt; Perl Quiz of the Week (offline/defunct?)&lt;br /&gt;   &lt;li&gt; &lt;a href="http://programmingpraxis.com/"&gt;Programming Praxis&lt;/a&gt; &lt;br /&gt;   &lt;li&gt; &lt;a href="http://dtai.cs.kuleuven.be/ppcbook/"&gt;Prolog Programming Contests&lt;/a&gt; &lt;br /&gt;   &lt;li&gt; &lt;a href="http://online-judge.uva.es/problemset/"&gt;Valladolid Problem Server&lt;/a&gt; &lt;br /&gt;   &lt;li&gt; &lt;a href="http://www.topcoder.com/"&gt;TopCoder&lt;/a&gt;&lt;/ul&gt;&lt;br /&gt;&lt;li&gt;Comparisons&lt;ul&gt;   &lt;li&gt; &lt;a href="http://rosettacode.org/wiki/Main_Page"&gt;Rosetta Code&lt;/a&gt; &lt;br /&gt;&lt;li&gt;PLEAC&lt;/ul&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Links to check out: &lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.billthelizard.com/2009/06/programming-and-logic-puzzles.html"&gt;http://www.billthelizard.com/2009/06/programming-and-logic-puzzles.html&lt;/a&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://grok-code.com/214/practice-your-code-fu-programming-contests-and-puzzles-online/"&gt;http://grok-code.com/214/practice-your-code-fu-programming-contests-and-puzzles-online/&lt;/a&gt;&lt;br /&gt;&lt;li&gt; &lt;a href="http://stackoverflow.com/questions/90715/what-are-the-best-programming-puzzles-you-came-across"&gt;http://stackoverflow.com/questions/90715/what-are-the-best-programming-puzzles-you-came-across&lt;/a&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://groups.google.com/group/programming-puzzles"&gt;http://groups.google.com/group/programming-puzzles&lt;/a&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://stackoverflow.com/questions/325653/what-are-some-good-websites-for-programming-puzzles"&gt;http://stackoverflow.com/questions/325653/what-are-some-good-websites-for-programming-puzzles&lt;/a&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://stackoverflow.com/questions/2284425/what-are-ten-really-good-short-programs-you-can-write-to-help-become-fluent-with"&gt;http://stackoverflow.com/questions/2284425/what-are-ten-really-good-short-programs-you-can-write-to-help-become-fluent-with&lt;/a&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://stackoverflow.com/questions/24692/where-can-you-find-fun-educational-programming-challenges"&gt;http://stackoverflow.com/questions/24692/where-can-you-find-fun-educational-programming-challenges&lt;/a&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://pleac.sourceforge.net/"&gt;&lt;/a&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-2879793435422770777?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/2879793435422770777/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=2879793435422770777' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/2879793435422770777'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/2879793435422770777'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2010/07/programming-language-gymnasium.html' title='Programming Language Gymnasium'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-7589831822472872678</id><published>2010-07-03T15:49:00.001-07:00</published><updated>2010-07-05T21:38:10.927-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming puzzles'/><category scheme='http://www.blogger.com/atom/ns#' term='programming languages'/><title type='text'>My Plan for Programming Language World Domination</title><content type='html'>I've recently been playing around with prolog (of all things). This happened after reading in "Coders at Work" that Erlang was originally written in prolog. This blew my mind. My impression from a few weeks of prolog usage in a programming languages course and another few weeks in an AI course that it was a *cute* idea but not really anything like a general purpose language. I guess this just emphasizes the point that you don't really know enough to have a valid opinion after such a short exposure. FWIW, I've been pleasantly surprised at the feel of declarative programming.&lt;br /&gt;&lt;br /&gt;This sort of reactivated a crazy idea I had in my youth. Back in high school I had this notion of being a polyglot. At one point I had a different language for each day of the week (French on Mondays, German on Tuesdays, etc.). Needless to say this overwhelmed me and I never really mastered any of them (though I retained an interest in linguistics).&lt;br /&gt;&lt;br /&gt;But I still love this idea of learning lots of languages. But now it occurs to me that I *can* do this with programming languages. The main flaw (besides the audacity) of my original goal was that I didn't have access to any native speakers with which to really practice my language skills. But with programming languages you can always practice to your hearts content.&lt;br /&gt;&lt;br /&gt;So here is my plan. I will rotate through my list of "keeper" languages as I run through my usual diet of "99 problems", "euler problems", ACM contest problems and other puzzlers I come across. After I confirm that I can comfortably solve relatively interesting (though smallish) problems in my current list of "keeper" languages I'll start work on getting another language up to speed to add to my Bat utility belt.&lt;br /&gt;&lt;br /&gt;Part of my interest in such a plan is that I just love learning to think in new ways. OK, that's a lie. Sometimes it's interesting. Sometimes it is maddening. But in either case it feels important and useful like going for long runs and eating right. I don't love exercise, but sometimes it feels good and I'm more worried about what will happen if I *don't* do it than discomfort/nuisance of actually doing it.&lt;br /&gt;&lt;br /&gt;My other concern is related to the fact that I quite luckily have been able to program primarily in python for the last 9 years. This has been fun, but I worry that I could be missing out on other cool emerging technologies and that while python is a decent way to express your thoughts, it is not perfect and I shouldn't stop exercising different ways to bend computers to my will.&lt;br /&gt;&lt;br /&gt;So here is my initial list of languages that I presume that I can currently solve programming puzzles in: python, prolog and haskell. Haskell is my language of the year and prolog has been quickly ramping up again. After I verify that these guys are reliably at my fingertips the next few languages will likely be from this short list:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;oz: Gives me an excuse to read CTM and it feels like and important laboratory for programming ideas&lt;br /&gt;&lt;li&gt;c: I feel guilty that I've lost my fluency in this. I want to get comfortable with thinking at that level again.&lt;br /&gt;&lt;li&gt;java: this is a lingua franca that I should be comfortable with (though I think it has the least to teach me). I used to be paid to do this, but I don't miss it.&lt;br /&gt;&lt;li&gt;a lisp (commonlisp/scheme/clojure/emacslisp): Do I need a reason? OK, probably scheme so that I can go through SICP all the way and be able to wield the ninja star of continuations with confidence.&lt;br /&gt;&lt;li&gt;smalltalk: Is there a more direct road to OO mastery? I've spent some time before but probably didn't give it enough of a chance. Also being different is sort of the point of this exercise.&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;There are many more that seem worth cultivating, but I don't dare write them down for fear of overwhelming myself too early on.&lt;br /&gt;&lt;br /&gt;Any way, here we go again.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-7589831822472872678?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/7589831822472872678/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=7589831822472872678' title='27 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/7589831822472872678'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/7589831822472872678'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2010/07/my-plan-for-programming-language-world.html' title='My Plan for Programming Language World Domination'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>27</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-810755674869095014</id><published>2010-02-26T22:15:00.001-08:00</published><updated>2010-03-02T22:24:22.707-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='programming languages'/><title type='text'>One Language a Year: Haskell - update 2</title><content type='html'>Not much to report, except that I am still trucking along through Real World Haskell (currently chapter 7).  This is much better than last year (the year of smalltalk) where I was already having lots of trouble with motivation by this point.  &lt;br /&gt;&lt;br /&gt;I've been thinking alot about what the chances are that haskell will become one of my daily use power tools like python is.  Or what the chances are that haskell will "take off" like a python.  &lt;br /&gt;&lt;br /&gt;I've already (mostly) gotten over one of my haskell phobias: dealing with "do" blocks.  When I only had a passing understanding, I always got confused with "&lt;-" and "lets" and "return"s in do blocks (and nested do blocks) and the rules seemed rather arbitrary, etc.  Of course I'm no expert now, but at least I don't look at them as magical things.  They have a precise use and logic and now I (mostly) get them.&lt;br /&gt;&lt;br /&gt;A couple things from python I miss in haskell:  list access syntax (e.g. x[2:]), default args/kwargs, etc.  Both of these haven't really hurt me yet but just the thought that I don't have them available makes me sad.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-810755674869095014?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/810755674869095014/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=810755674869095014' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/810755674869095014'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/810755674869095014'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2010/02/one-language-year-haskell-update-2.html' title='One Language a Year: Haskell - update 2'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-6779353312533241580</id><published>2010-02-01T20:26:00.003-08:00</published><updated>2010-02-27T22:12:59.321-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='fitness'/><category scheme='http://www.blogger.com/atom/ns#' term='running'/><title type='text'>How to Run</title><content type='html'>I came across &lt;a href="http://www.barefootrunning.fas.harvard.edu/"&gt;this&lt;/a&gt; site a few weeks ago and it has really rehabilitated my capacity for running. I'm not a big time runner but I try to run at least once a week and once a month I like to go at least 5 miles. The problem is that my knees haven't been very happy about this project for quite some time. In fact this summer I was trying to ramp up for a half marathon and by the time I got to 8 mile training sessions my knees (one in particular) really laid down the law. Basically they said stop that. I would end up taking ibuprofen and doing hot/cold treatments for a few days. This did not seem like a very wise course to continue and so I backed off.&lt;br /&gt;&lt;br /&gt;So I've been trying the idea of barefoot running for the last month or so and the change is striking. I'm not actually going "barefoot". There is about a foot of snow in my front yard currently, but the running style is still the same. The idea is essentially to run on the front pads of your foot rather than landing on your heel.  Rather than sending a shock straight from your heel to you knees and hips, you absorb most of it in the front of your foot.&lt;br /&gt;&lt;br /&gt;I did my 5 mile route today and instead of finishing in agony and limping into my house I feel like I could easily go and do it again tomorrow. &lt;br /&gt;&lt;br /&gt;It definitely takes concentration and practice to run this way and it doesn't feel completely natural quite yet, but at this point I don't think I could go back to "normal" running again. The main difference currently is that my calves get much more of a workout and I can still feel that and the front pads of my feet are just a little tender. They are absorbing a little more impact so I'm not surprised. It's not the feeling of my foot being abused just the tenderness you feel as you are developing tougher skin.&lt;br /&gt;&lt;br /&gt;My wife has also adapted this style. She has suffered from plantar fasciitis for quite a while and she has noticed a huge reduction in foot pain. &lt;br /&gt;&lt;br /&gt;So the question is, how is this just now getting around? I sure wish I knew about this 20 years ago. I could have saved my body a lot of wear and tear (possibly removing the need for my knee surgery of 6 years ago). Has this been known forever and I was just oblivious? I'd honestly never heard of the idea but now it seems sort of obvious.]&lt;br /&gt;&lt;br /&gt;You can't help but wonder how many other easy fixes for modern problems there are out there.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-6779353312533241580?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/6779353312533241580/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=6779353312533241580' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/6779353312533241580'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/6779353312533241580'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2010/02/how-to-run.html' title='How to Run'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-784268436242666936</id><published>2010-02-01T20:04:00.000-08:00</published><updated>2010-02-01T20:25:31.874-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='programming languages'/><title type='text'>One Language a Year: Haskell - update 1</title><content type='html'>So one month in and so far Haskell is treating me much better than Smalltalk did a year ago. What is better? Well to start with my main learning period is a 20 to 30 minute window each morning before work. For whatever reason Smalltalk put me directly to sleep and I would often literally find my forehead mashed into my keyboard. I haven't had that problem at all yet with Haskell. One thing is for sure, if you want to learn a new programming language, you have to be awake.&lt;br /&gt;&lt;br /&gt;What is the difference? For one it may just be that I like functional rather than object oriented thinking better. I'm fairly comfortable with object design ideas, but things like design patterns more often than not seem like bandaids over language problems rather than powerful solution cookie cutters. Also I admit that I generally expect object based solution to be over engineered. There is such a thing as beautiful OO solutions, but they seem to be the exception in my experience.&lt;br /&gt;&lt;br /&gt;But probably the number one success factor for me is the fantastic book: Real World Haskell. 4 chapters in and I find the pacing quite nice. And there is no shying away from mundane things like reading and writing files and reading command line arguments. I swear I have a Haskell book that doesn't do any discussion of IO until chapter 17. I get that IO is about monads and monads are serious mind benders, but you still need to crank out a "Hello World" program early on. RWH gets that and many other things right. &lt;br /&gt;&lt;br /&gt;One of my main rules for making sure I understand everything as I'm going along is to retype in *all* code from scratch as I come across it and to do so without directly copying it. In other words I have to understand it enough to retype it in directly from memory. This is probably something I should always have done, but cutting and pasting is just so easy and there is so little time....&lt;br /&gt;&lt;br /&gt;In any case, the year of Haskell is doing quite nicely so far.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-784268436242666936?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/784268436242666936/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=784268436242666936' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/784268436242666936'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/784268436242666936'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2010/02/one-language-year-haskell-update-1.html' title='One Language a Year: Haskell - update 1'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-132857142142341499</id><published>2010-01-17T15:31:00.000-08:00</published><updated>2010-01-17T15:37:59.411-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='programming languages'/><title type='text'>Is anything new/unique to Python?</title><content type='html'>Looking at haskell I see some of my friends from python.  Significant whitespace, list comprehensions, interators (sort of like laziness), tuples.   And then it struck me that all of these features in python are derivative.  Not that that is a bad thing.  But I was surprised that I couldn't think of anything definitively new/unique to python.  (Obviously the way these things are combined/balanced is unique)&lt;br /&gt;&lt;br /&gt;Hey, internets, did python invent any language features/syntax?&lt;br /&gt;&lt;br /&gt;The only thing that I can come up with is maybe the __blah__ (unders before and after) style of exposing syntax overriding.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-132857142142341499?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/132857142142341499/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=132857142142341499' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/132857142142341499'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/132857142142341499'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2010/01/is-anything-newunique-to-python.html' title='Is anything new/unique to Python?'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-6357967048042122158</id><published>2010-01-04T22:25:00.000-08:00</published><updated>2010-01-04T22:30:55.915-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='functional programming'/><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='programming languages'/><title type='text'>One New Language a Year: Haskell</title><content type='html'>Another year starts and so I try again to do the "Language a Year" thing. Last year it was a close decision between smalltalk and haskell.  I went with smalltalk and for various reason it didn't really grab me and I ended up abandoning my efforts and switching to emacslisp.  &lt;br /&gt;&lt;br /&gt;So hopefully I've learned a little bit about the focus and dedication it takes to work on a new language and I'm ready to try this thing again.&lt;br /&gt;&lt;br /&gt;First of all you need a goal or you won't know if you've succeeded (or failed).  My goal is to essentially have a new python at my command. Another general purpose language that I can use as easily as I think for any sort of programming itch I happen to have.  I originally learned python because I was looking for a new language that was different from what I was using at work.  In 2000 or so I was mostly a Java and perl programmer.  Python seemed interesting and different so I dove in.  Relatively soon after that it became my full time work language and I sort of lost the idea of having a second language that I used in my free time.&lt;br /&gt;&lt;br /&gt;So here I am again looking for a "fun" language.  I've actually done occasional reading on haskell topics/blogs and have gone through a few short tutorials, but it's hardly a language I would say I'm at all comfortable in.  In fact, unlike most languages that I have some familiarity in, random haskell code more often than not seems to have some scary new function/datatype/etc that I can't even start to grok from context.&lt;br /&gt;&lt;br /&gt;The metaphor I have in mind as I dive in is that just as a professional builder uses tools that would be confusing and dangerous to a amateur builder there are programming tools/languages/concepts that are a level of effectiveness above the blub that I'm used to.  It will take time/energy to master the more powerful tools but once you have, you will be working at a more advanced level.&lt;br /&gt;&lt;br /&gt;I'm really curious to see if I can get a new language in my tool belt that is as concise (or more so) than python, faster, less prone to bugs, etc.  Haskell seems as likely to provide this as any language as I'm aware of.&lt;br /&gt;&lt;br /&gt;My plan is to use "Real World Haskell" as my main resource.  By the end of the year I'd like to have gotten comfortable enough that I could use it interchangeably with python for random fun projects at home and even better to create a project and/or get involved in an existing project so that I have a reason to keep my skills fresh (since it ain't too likely to become my work language any time soon).&lt;br /&gt;&lt;br /&gt;In any case I'll check back in a month or so and let you know how it's going.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-6357967048042122158?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/6357967048042122158/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=6357967048042122158' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/6357967048042122158'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/6357967048042122158'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2010/01/one-new-language-year-haskell.html' title='One New Language a Year: Haskell'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-3200711900597152008</id><published>2009-11-04T22:15:00.000-08:00</published><updated>2009-11-04T22:19:14.826-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pushups'/><category scheme='http://www.blogger.com/atom/ns#' term='fitness'/><title type='text'>100 pushups - DONE</title><content type='html'>OK, it took me about a year and a half.  And they weren't pretty.  But I finished it.&lt;br /&gt;&lt;br /&gt;Sort of anti-climactic somehow after all this time.  But kinda cool to have gotten there.&lt;br /&gt;&lt;br /&gt;2 questions:&lt;br /&gt;- do I bother maintaining this level?  (once a week refresher?)&lt;br /&gt;- what should I work on next? (chin ups probably)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-3200711900597152008?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/3200711900597152008/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=3200711900597152008' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/3200711900597152008'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/3200711900597152008'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2009/11/100-pushups-done.html' title='100 pushups - DONE'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-8779102666658415877</id><published>2009-10-01T21:41:00.000-07:00</published><updated>2009-10-02T21:29:40.698-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pushups'/><category scheme='http://www.blogger.com/atom/ns#' term='fitness'/><title type='text'>100 pushups milestone - 80</title><content type='html'>OK, so I'm 80% of the way there (&lt;a href="http://hundredpushups.com/"&gt;100 pushups&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;Just checked my records and I've been doing this for 1 year and 3 months.  That would be funny if it weren't so tragic.  How can it be taking so long?  Oh well, keep on trucking.  &lt;br /&gt;&lt;br /&gt;I better get a damn spiffy prize when I finish this.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-8779102666658415877?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/8779102666658415877/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=8779102666658415877' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/8779102666658415877'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/8779102666658415877'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2009/10/100-pushups-milestone-80.html' title='100 pushups milestone - 80'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-870727274131444002</id><published>2009-07-20T22:03:00.000-07:00</published><updated>2009-07-20T22:08:23.139-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='oscon09'/><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='oscon'/><title type='text'>Git 101 tutorial</title><content type='html'>&lt;a href="http://en.oreilly.com/oscon2009/public/schedule/detail/7953"&gt;http://en.oreilly.com/oscon2009/public/schedule/detail/7953&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This was a great tutorial.  It was as good of a brain dump as you could hope for in a half day session.  A good mix of theory and hands on examples.  This got me excited to get back to the office and push to get this adopted as soon as possible.  (It's already "in the air".  It just needs a nudge to get us converted over to it)&lt;br /&gt;&lt;br /&gt;Well done!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-870727274131444002?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/870727274131444002/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=870727274131444002' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/870727274131444002'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/870727274131444002'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2009/07/git-101-tutorial.html' title='Git 101 tutorial'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-1234040055713823610</id><published>2009-07-20T21:44:00.000-07:00</published><updated>2009-07-20T22:09:31.263-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='oscon09'/><category scheme='http://www.blogger.com/atom/ns#' term='oscon'/><category scheme='http://www.blogger.com/atom/ns#' term='squeak'/><title type='text'>Still Looking for the Swan in Squeak's Ugly Duckling</title><content type='html'>&lt;a href="http://en.oreilly.com/oscon2009/public/schedule/detail/8158"&gt;http://en.oreilly.com/oscon2009/public/schedule/detail/8158&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;So this is not exactly the tutorial I was expecting.  But it's also quite possible my expectations were a little skewed.&lt;br /&gt;&lt;br /&gt;Unfortunately Avi Bryant wasn't able to make the conference so Randal Schwartz stepped in.  And no disrespect to Randal but if I had known this substitution had occurred, I would have picked a different tutorial simply because I had seen his presentation last year and was interested in a different perspective.&lt;br /&gt;&lt;br /&gt;Partly I feel like I was expecting too much from this tutorial and part of me feels like I got something different than described.&lt;br /&gt;&lt;br /&gt;When I read the description in the link above I don't get the sense of a remedial no-background talk.   But like I said Randal took this over at the last minute for Avi so I have no complaints with him.&lt;br /&gt;&lt;br /&gt;I think I focused a little too much on the last paragraph of the description whereas I should realize that that is what usually gets covered least at the end (or not at all):&lt;br /&gt;&lt;br /&gt;"But we’ll also address the practical concerns that keep people away from Squeak: how to get rid of the pastel colors and bitmapped fonts so that you can stand to look at it; how to get your source code into version control so you can collaborate with others; how to find documentation and examples; how to integrate with the OS and with C libraries; how to manage deployment."&lt;br /&gt;&lt;br /&gt;I am relatively comfortable with the ideas of smalltalk and reading it's code, just not completely sold on the language and environment.  So what I was mostly looking for was the addressing of "practical concerns that keep people away from Squeak": &lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt; how to get rid of the pastel colors and bitmapped fonts so that you can stand to look at it; &lt;br /&gt;  &lt;ul&gt;&lt;li&gt;just a mention that this is possible.  I would have really liked a detailed course on how to customize the ugly duckling away&lt;/ul&gt;&lt;br /&gt;&lt;li&gt;how to get your source code into version control so you can collaborate with others; &lt;br /&gt;  &lt;ul&gt;&lt;li&gt;there was a pretty good description of version control and options for doing this&lt;/ul&gt;&lt;br /&gt;&lt;li&gt;how to find documentation and examples; &lt;br /&gt;  &lt;ul&gt;&lt;li&gt;this was well done and is a core part of the wonder of smalltalk&lt;/ul&gt;&lt;br /&gt;&lt;li&gt;how to integrate with the OS and with C libraries; &lt;br /&gt;  &lt;ul&gt;&lt;li&gt;I don't think there was any mention of this (unless I really zoned out)&lt;/ul&gt;&lt;br /&gt;&lt;li&gt;how to manage deployment.&lt;br /&gt;  &lt;ul&gt;&lt;li&gt;I don't recall anything like this&lt;/ul&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;But really it was a well done tutorial, just not what I was expecting.  But I think&lt;br /&gt;this was just a combination of over ambitious expectations and last minute teacher changes.&lt;br /&gt;&lt;br /&gt;Here is the smalltalk course I would love to have:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt; develop a real world useful app while I watch.  e.g.   take some unix sysadmin tasks and automate them and create a reporting system etc.  ie, show me that squeak can kick   python's ass at something where python excels&lt;br /&gt;&lt;li&gt;show me how to recover when my image crashes or I've accidentally broken things&lt;br /&gt;&lt;li&gt;show me how to customize my way from the default image to one of the premade developer  images.  then explain to me why these aren't already the defaults&lt;br /&gt;&lt;li&gt;show me how to convince my bosses that I should do a trial  project in smalltalk.  :)&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-1234040055713823610?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/1234040055713823610/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=1234040055713823610' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/1234040055713823610'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/1234040055713823610'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2009/07/still-looking-for-swan-in-squeaks-ugly.html' title='Still Looking for the Swan in Squeak&apos;s Ugly Duckling'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-4610649302793042771</id><published>2009-07-12T22:05:00.001-07:00</published><updated>2009-07-12T22:12:13.564-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pushups'/><category scheme='http://www.blogger.com/atom/ns#' term='fitness'/><title type='text'>Happy Birthday - My mission to get to 100 pushups is now one  year old!</title><content type='html'>So after a year of the 100 pushup plan, where am I?  75.&lt;br /&gt;&lt;br /&gt;Now if you are familiar with the 6 weeks to 100 pushup plan you might detect a slight disconnect between the plan and my success.  Just an eentsy bit off...&lt;br /&gt;&lt;br /&gt;On the one hand: holy crap I can do 75 pushups.  And also: wow, look at the determination and will power.  &lt;br /&gt;&lt;br /&gt;On the other hand:  what is wrong with me?  Why can't I finish this sucker?  And:  why am I wasting my time, focus on this?&lt;br /&gt;&lt;br /&gt;So there you go.  I'm part super man and part loser.  I knew that before I started.&lt;br /&gt;&lt;br /&gt;OK.  If I haven't gotten to 100 before another year passes I seriously have to reevaluate this whole thing.&lt;br /&gt;&lt;br /&gt;(Any tips from the pros out there how to get the last 25?)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-4610649302793042771?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/4610649302793042771/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=4610649302793042771' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/4610649302793042771'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/4610649302793042771'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2009/07/happy-birthday-my-mission-to-get-to-100.html' title='Happy Birthday - My mission to get to 100 pushups is now one  year old!'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-5282634755582881211</id><published>2009-05-22T21:19:00.000-07:00</published><updated>2009-05-22T21:29:31.369-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='emacslisp'/><category scheme='http://www.blogger.com/atom/ns#' term='programming languages'/><category scheme='http://www.blogger.com/atom/ns#' term='emacs'/><category scheme='http://www.blogger.com/atom/ns#' term='squeak'/><title type='text'>One New Language a Year:  (was) Smalltalk</title><content type='html'>&lt;p&gt;OK, so I had this crazy plan of doing the "one new language a year" thing.  I really like the idea of this.  Get out of your comfort zone, prevent settling for a "blub" language, learn new techniques.  So I choose smalltalk and thought this would fit the bill.  I was really excited to work with turtles all the way down.  "Real" object oriented programming. Etc.&lt;br /&gt;&lt;p&gt;And here it is May and I'm just not doing so well.  Best laid schemes and all that.  In fact I've decided to abandon it for, get this, emacslisp.  Only time will tell if this was a good decision or just my normal fickleness.&lt;br /&gt;&lt;p&gt;Part of this decision is just pure pragmatism.  My learning windows for this plan are of necessity going to be early in the morning before breakfast or late at night after the family's in bed.  I've tried both slots for a month or more and both put me to sleep.  You can't learn much while you are asleep so no matter the plan/desire I have to be doing something that engages my attention.&lt;br /&gt;&lt;p&gt;OK, I said it.  Smalltalk puts me to sleep.  And I feel bad for saying it. I really want to be the guy who likes smalltalk and wields it with authority. But there is just so much I don't enjoy about it and while I'm sure there  are some cool things to learn from it, it just doesn't entice me.  At least not at 6 am.  We'll see if emacslisp can do better.&lt;br /&gt;&lt;p&gt;Smalltalk is like this quirky, fairly attractive girl who on paper seems like a good match for you ("Wow, you like continuations?  Me too!") but for whatever reason just doesn't ignite that spark.  In addition, she has lots of eccentricities that if she was "the one" would be charming/braggable.  But if you are indifferent to her they just come off as irritations/oddities.&lt;br /&gt;&lt;p&gt;I also wonder if watching this video put the final nail in the coffin: &lt;a href="http://railsconf.blip.tv/file/2089545/"&gt;"what killed smalltalk"&lt;/a&gt; (&lt;a href="http://www.cincomsmalltalk.com/blog/blogView?showComments=true&amp;printTitle=Smalltalk:_Our_Death_has_been_Exaggerated&amp;entry=3419278263"&gt;also&lt;/a&gt;). Just hearing about smalltalk being dead (I'm not defending this thesis) sort of activated my pragmatism module which some how grabbed the reins of my brain and executed veto power.&lt;br /&gt;&lt;p&gt;The thing that bugs me is that I don't see myself as a practical dude. In fact, I find the thought of being practical a little bit horrifying.  I'm always doing things just because they are interesting, not because I'm trying to be more efficient, etc.  And here I am making a practical decision.  Ugg.&lt;br /&gt;&lt;p&gt;I wonder what the rules are about abandoning your "one new language a year" language halfway through the year.  Is there some governing body I should contact?  Do I need to ask permission first?&lt;br /&gt;&lt;p&gt;For what it's worth, smalltalk is not permanently abandoned; it is just pushed down on the list.  If I stick with the language a year program I will almost certainly head back to smalltalk at some point.  We'll see what happens next time around. &lt;br /&gt;&lt;p&gt;Does working with python as your primary language makes it harder to learn new languages?  Every language that I've learned so far has been a productivity boost or has some other sweetener (my language history: basic -&gt; pascal -&gt; c -&gt; c++ -&gt; java -&gt; perl -&gt; python).  emacslisp is a booster just because it is the only option in emacs (I will be looking into pymacs though).  haskell looks like it might be a next step in the awesomeness hierarchy.  But smalltalk doesn't *seem* (in my limited experience) like a significantly more productive tool past python.  smalltalk feels like a language from which many great ideas have been lifted.  And the ones that haven't yet are either in the process of being stolen (pypy - turtles all the way down) or aren't worth the trouble (image files, etc).&lt;br /&gt;&lt;p&gt;What's funny is that I've talked to others who have had the same initial fascination with smalltalk and then given up on it due various pain points or lack or practicality.&lt;br /&gt;&lt;p&gt;I guess in the end part of it is that smalltalk doesn't seem to solve a problem that I'm having in a vastly superior way and (surprisingly to me) isn't more fun.  On paper it seems it should be.  And I actually feel guilty for not liking smalltalk.  I've failed the gods of language geekdom.&lt;br /&gt;&lt;p&gt;Any way, on to the new hotness of, er, emacslisp. &lt;br /&gt;&lt;p&gt;So why emacslisp?  At the start of the year I was trying to decide between smalltalk and haskell.  I was in sort of an object-y mood at the time so smalltalk won out.  I'm not defaulting to haskell at this time primarily because I want to give it the full year (and it just feels like it has to be a calendar year starting in January). emacslisp works because I know it will be useful.  It will make a real difference to my productivity immediately (I mostly live in emacs) and I like lispy languages (though I've only dabbled and/or used them for a semester in an ai class).  *And* I don't feel too bad about giving it only half a year.&lt;br /&gt;&lt;p&gt;My goal with emasclisp is to get to the point where I can program in it as easily as I think (how it is for python with me now).  I want to be able to whip out useful helper functions like &lt;a href="http://steve.yegge.googlepages.com/saving-time"&gt;this&lt;/a&gt; and be able to write my own modes and/or hack on existing emacs packages.&lt;br /&gt;&lt;p&gt;Any way, smalltalk, it's not you it's me.  When I get my head right maybe we'll meet again and give it another shot.  I wish you all the best. (but please don't call - I'm not sure emacslisp would understand us "just being friends").&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-5282634755582881211?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/5282634755582881211/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=5282634755582881211' title='24 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/5282634755582881211'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/5282634755582881211'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2009/05/one-new-language-year-was-smalltalk.html' title='One New Language a Year:  (was) Smalltalk'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>24</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-7123784488585690438</id><published>2009-04-08T20:27:00.000-07:00</published><updated>2009-04-08T20:30:30.762-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='functional programming'/><category scheme='http://www.blogger.com/atom/ns#' term='higher order perl'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='hop'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><title type='text'>Higher Order Perl (Python Style) : Chapter 7 - Higher Order Functions And Currying</title><content type='html'>&lt;br&gt;&lt;br /&gt;&lt;br&gt;&lt;a href="http://dustbunnylair.blogspot.com/2009/01/higher-order-perl-python-style-toc.html"&gt;TOC&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;### 7.1 Currying&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# @tagged_texts = walk_html($tree, sub { ['MAYBE', $_[0]] },&lt;br /&gt;#                                  \&amp;promote_if_h1tag);&lt;br /&gt;# sub promote_if_h1tag {&lt;br /&gt;#   my $element = shift;&lt;br /&gt;#   if ($element-&gt;{_tag} eq 'h1') {&lt;br /&gt;#     return ['KEEPER', join '', map {$_-&gt;[1]} @_];&lt;br /&gt;#   } else {&lt;br /&gt;#     return @_;&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;# sub extract_headers {&lt;br /&gt;#   my $tree = shift;&lt;br /&gt;#   my @tagged_texts = walk_html($tree, sub { ['MAYBE', $_[0]] },&lt;br /&gt;#                                       \&amp;promote_if_h1tag);&lt;br /&gt;#   my @keepers = grep { $_-&gt;[0] eq 'KEEPER' } @tagged_texts;&lt;br /&gt;#   my @keeper_text = map { $_-&gt;[1] } @keepers;&lt;br /&gt;#   my $header_text = join '', @keeper_text;&lt;br /&gt;#   return $header_text;&lt;br /&gt;# }&lt;br /&gt;def extender(accum, item):&lt;br /&gt;    accum.extend(item)&lt;br /&gt;tagged_texts = walk_html(tree, lambda x: [["MAYBE", x]],&lt;br /&gt;                               promote_if_h1tag,&lt;br /&gt;                               extender)&lt;br /&gt;def promote_if_h1tag(element, results):&lt;br /&gt;    if element["_tag"] == "h1":&lt;br /&gt;        return [["KEEPER", "".join([ _x[1] for _x in results])]]&lt;br /&gt;    else:&lt;br /&gt;        return results&lt;br /&gt;&lt;br /&gt;def extract_headers(tree):&lt;br /&gt;    tagged_texts = walk_html(tree, lambda x: [["MAYBE", x]],&lt;br /&gt;                                   promote_if_h1tag,&lt;br /&gt;                                   extender)&lt;br /&gt;    keepers = [x for x in tagged_texts if x[0] == "KEEPER"]&lt;br /&gt;    keeper_text = [x[1] for x in keepers]&lt;br /&gt;    header_text = "".join(keeper_text)&lt;br /&gt;    return header_text&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub promote_if {&lt;br /&gt;#   my $is_interesting = shift;&lt;br /&gt;#   my $element = shift;&lt;br /&gt;#   if ($is_interesting-&gt;($element-&gt;{_tag}) {&lt;br /&gt;#     return ['keeper', join '', map {$_-&gt;[1]} @_];&lt;br /&gt;#   } else {&lt;br /&gt;#     return @_;&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;# my @tagged_texts = walk_html($tree,&lt;br /&gt;#                              sub { ['maybe', $_[0]] },&lt;br /&gt;#                              sub { promote_if(&lt;br /&gt;#                                      sub { $_[0] eq 'h1' },&lt;br /&gt;#                                      $_[0])&lt;br /&gt;#                              });&lt;br /&gt;def promote_if(is_interesting, element, results):&lt;br /&gt;    if is_interesting(element["_tag"]):&lt;br /&gt;        return [["KEEPER", "".join([ _x[1] for _x in results])]]&lt;br /&gt;    else:&lt;br /&gt;        return results&lt;br /&gt;tagged_texts = walk_html(tree, lambda x: [["MAYBE", x]],&lt;br /&gt;                               lambda y,z: (promote_if((lambda _y: _y == "h1"), y,z)),&lt;br /&gt;                               extender)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub promote_if {&lt;br /&gt;#   my $is_interesting = shift;&lt;br /&gt;#   return sub {&lt;br /&gt;#     my $element = shift;&lt;br /&gt;#     if ($is_interesting-&gt;($element-&gt;{_tag}) {&lt;br /&gt;#       return ['keeper', join '', map {$_-&gt;[1]} @_];&lt;br /&gt;#     } else {&lt;br /&gt;#       return @_;&lt;br /&gt;#     }&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def promote_if(is_interesting):&lt;br /&gt;    def _foo(element, results):&lt;br /&gt;        if is_interesting(element["_tag"]):&lt;br /&gt;            return [["KEEPER", "".join([ _x[1] for _x in results])]]&lt;br /&gt;        else:&lt;br /&gt;            return results&lt;br /&gt;    return _foo&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# my @tagged_texts = walk_html($tree,&lt;br /&gt;#                              sub { ['maybe', $_[0]] },&lt;br /&gt;#                              promote_if(sub { $_[0] eq 'h1' }),&lt;br /&gt;#                              );&lt;br /&gt;tagged_texts = walk_html(tree, lambda x: [["MAYBE", x]],&lt;br /&gt;                               promote_if(lambda y: y == "h1"),&lt;br /&gt;                               extender)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub add2 {&lt;br /&gt;#   my ($s, $t) = @_;&lt;br /&gt;#   return unless $s &amp;&amp; $t;&lt;br /&gt;#   node(head($s) + head($t),&lt;br /&gt;#        promise { add2(tail($s), tail($t)) });&lt;br /&gt;# }&lt;br /&gt;# sub mul2 {&lt;br /&gt;#   my ($s, $t) = @_;&lt;br /&gt;#   return unless $s &amp;&amp; $t;&lt;br /&gt;#   node(head($s) * head($t),&lt;br /&gt;#        promise { mul2(tail($s), tail($t)) });&lt;br /&gt;# }&lt;br /&gt;def add2(s,t):&lt;br /&gt;    if not (t and s):&lt;br /&gt;        return None&lt;br /&gt;    return node(head(s) + head(t),&lt;br /&gt;                promise(lambda: add2(tail(s),tail(t))))&lt;br /&gt;def mul2(s,t):&lt;br /&gt;    if not (s and t):&lt;br /&gt;        return None&lt;br /&gt;    return node(head(s)*head(t),&lt;br /&gt;                promise(lambda: mul2(tail(s),tail(t))))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub combine2 {&lt;br /&gt;#   my ($s, $t, $op) = @_;&lt;br /&gt;#   return unless $s &amp;&amp; $t;&lt;br /&gt;#   node($op-&gt;(head($s), head($t)),&lt;br /&gt;#        promise { combine2(tail($s), tail($t), $op) });&lt;br /&gt;# }&lt;br /&gt;def combine2(s,t,op):&lt;br /&gt;    if not (s and t):&lt;br /&gt;        return None&lt;br /&gt;    return node(op(head(s),head(t)),&lt;br /&gt;                promise(lambda: combine2(tail(s),tail(t), op)))&lt;br /&gt;&lt;br /&gt;# sub add2 { combine2(@_, sub { $_[0] + $_[1] }) }&lt;br /&gt;# sub mul2 { combine2(@_, sub { $_[0] * $_[1] }) }&lt;br /&gt;def add2(s,t):&lt;br /&gt;    return combine2(s, t, lambda _s, _t: _s + _t)&lt;br /&gt;def mul2(s,t):&lt;br /&gt;    return combine2(s, t, lambda _s, _t: _s * _t)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub combine2 {&lt;br /&gt;#    my $op = shift;&lt;br /&gt;#    return sub {&lt;br /&gt;#      my ($s, $t) = @_;&lt;br /&gt;#      return unless $s &amp;&amp; $t;&lt;br /&gt;#      node($op-&gt;(head($s), head($t)),&lt;br /&gt;#           promise { combine2($op)-&gt;(tail($s), tail($t))});&lt;br /&gt;#    };&lt;br /&gt;# }&lt;br /&gt;def combine2(op):&lt;br /&gt;    def _foo(s, t):&lt;br /&gt;        if not (s and t):&lt;br /&gt;            return None&lt;br /&gt;        return node(op(head(s),head(t)),&lt;br /&gt;                    promise(lambda: combine2(op(tail(s), tail(t)))))&lt;br /&gt;&lt;br /&gt;    return _foo&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# $add2 = combine2(sub { $_[0] + $_[1] });&lt;br /&gt;# $mul2 = combine2(sub { $_[0] * $_[1] });&lt;br /&gt;add2 = combine2(lambda x,y: x+y)&lt;br /&gt;mul2 = combine2(lambda x,y: x*y)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# my $catstrs = combine2(sub { "$_[0]$_[1]" })-&gt;($s, $t);&lt;br /&gt;catstrs = combine2(lambda x,y: "%s%s" % (x,y))(s,t)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub scale {&lt;br /&gt;#   my $s = shift;&lt;br /&gt;#   return sub {&lt;br /&gt;#     my $c = shift;&lt;br /&gt;#     return if $c == 0;&lt;br /&gt;#     transform { $_[0] * $c } $s;&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def scale(s):&lt;br /&gt;    def _foo(c):&lt;br /&gt;        if c == 0:&lt;br /&gt;            return None&lt;br /&gt;        return transform(lambda x: x*c, s)&lt;br /&gt;    return _foo&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub scale {&lt;br /&gt;#   my $c = shift;&lt;br /&gt;#   return sub {&lt;br /&gt;#     my $s = shift;&lt;br /&gt;#     transform { $_[0] * $c } $s;&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def scale(c):&lt;br /&gt;    def _foo(s):&lt;br /&gt;        return transform(lambda x: x*c, s)&lt;br /&gt;    return _foo&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub slope {&lt;br /&gt;#   my ($f, $x) = @_;&lt;br /&gt;#   my $e = 0.00000095367431640625;&lt;br /&gt;#   ($f-&gt;($x+$e) - $f-&gt;($x-$e)) / (2*$e);&lt;br /&gt;# }&lt;br /&gt;def slope(f, x):&lt;br /&gt;    e = 0.00000095367431640625&lt;br /&gt;    return (f(x+e) - f(x-e)) / (2*e)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub slope {&lt;br /&gt;#   my $f = shift;&lt;br /&gt;#   my $e = 0.00000095367431640625;&lt;br /&gt;#   return sub {&lt;br /&gt;#     my $x = shift;&lt;br /&gt;#     ($f-&gt;($x+$e) - $f-&gt;($x-$e)) / (2*$e);&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;def slope(f):&lt;br /&gt;    e = 0.00000095367431640625&lt;br /&gt;    def _foo(x):&lt;br /&gt;        return (f(x+e) - f(x-e)) / (2*e)&lt;br /&gt;    return _foo&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub slope {&lt;br /&gt;#   my $f = shift;&lt;br /&gt;#   my $e = 0.00000095367431640625;&lt;br /&gt;#   my $d = sub {&lt;br /&gt;#     my ($x) = shift;&lt;br /&gt;#     ($f-&gt;($x+$e) - $f-&gt;($x-$e)) / (2*$e);&lt;br /&gt;#   };&lt;br /&gt;#   return @_ ? $d-&gt;(shift) : $d;&lt;br /&gt;# }&lt;br /&gt;def slope(f, _x=None):&lt;br /&gt;    e = 0.00000095367431640625&lt;br /&gt;    def d(x):&lt;br /&gt;        return (f(x+e) - f(x-e)) / (2*e)&lt;br /&gt;    return d(_x) if _x != None else d&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub iterate_function {&lt;br /&gt;#   my ($f, $x) = @_;&lt;br /&gt;#   my $s;&lt;br /&gt;#   $s = node($x, promise { &amp;transform($f, $s) });&lt;br /&gt;# }&lt;br /&gt;def iterate_function(f, x):&lt;br /&gt;    s = node(x, promise(lambda: transform(f, s)))&lt;br /&gt;    return s&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub iterate_function {&lt;br /&gt;#   my $f = shift;&lt;br /&gt;#   return sub {&lt;br /&gt;#     my $x = shift;&lt;br /&gt;#     my $s;&lt;br /&gt;#     $s = node($x, promise { &amp;transform($f, $s) });&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;def iterate_function(f):&lt;br /&gt;    def foo(x):&lt;br /&gt;        s = node(x, promise(lambda: transform(f, s)))&lt;br /&gt;        return s&lt;br /&gt;    return foo&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# *upfrom = iterate_function(sub { $_[0] + 1 });&lt;br /&gt;upfrom = iterate_function(lambda x: x + 1)&lt;br /&gt;&lt;br /&gt;# *pow2_from = iterate_function(sub { $_[0] * 2 });&lt;br /&gt;pow2_from = iterate_function(lambda x: x * 2)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub combine2 {&lt;br /&gt;#   my $op = shift;&lt;br /&gt;#   return sub {&lt;br /&gt;#     my ($s, $t) = @_;&lt;br /&gt;#     return unless $s &amp;&amp; $t;&lt;br /&gt;#     node($op-&gt;(head($s), head($t)),&lt;br /&gt;#          promise { combine2($op)-&gt;(tail($s), tail($t)) });&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;def combine2(op):&lt;br /&gt;    def _foo(s, t):&lt;br /&gt;        if not (s and t):&lt;br /&gt;            return None&lt;br /&gt;        return node(op(head(s),head(t)),&lt;br /&gt;                    promise(lambda: combine2(op(tail(s), tail(t)))))&lt;br /&gt;&lt;br /&gt;    return _foo&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub combine2 {&lt;br /&gt;#   my $op = shift;&lt;br /&gt;#   my $r;&lt;br /&gt;#   $r = sub {&lt;br /&gt;#     my ($s, $t) = @_;&lt;br /&gt;#     return unless $s &amp;&amp; $t;&lt;br /&gt;#     node($op-&gt;(head($s), head($t)),&lt;br /&gt;#          promise { $r-&gt;(tail($s), tail($t)) });&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;def combine2(op):&lt;br /&gt;    def r(s,t):&lt;br /&gt;        if not (s and t):&lt;br /&gt;            return None&lt;br /&gt;        return node(op(head(s),head(t)),&lt;br /&gt;                    promise(lambda: r(tail(s),tail(t))))&lt;br /&gt;    return r&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;### 7.2 Common Higher-Order Functions&lt;br /&gt;&lt;br /&gt;# map { $_ * 2 } (1..5);        # returns 2, 4, 6, 8, 10&lt;br /&gt;# grep { $_ % 2 == 0 } (1..10); # returns 2, 4, 6, 8, 10&lt;br /&gt;## while map exists in python the more usual idiom&lt;br /&gt;## is to use list comprehensions/iterators&lt;br /&gt;[x*2 for x in range(1,6)]&lt;br /&gt;[x for x in range(1,11) if x % 2 == 0 ]&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub cmap (&amp;) {&lt;br /&gt;#   my $f = shift;&lt;br /&gt;#   my $r = sub {&lt;br /&gt;#     my @result;&lt;br /&gt;#     for (@_) {&lt;br /&gt;#       push @result, $f-&gt;($_);&lt;br /&gt;#     }&lt;br /&gt;#     @result;&lt;br /&gt;#   };&lt;br /&gt;#   return $r;&lt;br /&gt;# }&lt;br /&gt;# sub cgrep (&amp;) {&lt;br /&gt;#   my $f = shift;&lt;br /&gt;#   my $r = sub {&lt;br /&gt;#     my @result;&lt;br /&gt;#     for (@_ ) {&lt;br /&gt;#       push @result, $_ if $f-&gt;($_ );&lt;br /&gt;#     }&lt;br /&gt;#     @result;&lt;br /&gt;#   };&lt;br /&gt;#   return $r;&lt;br /&gt;# }&lt;br /&gt;## skipping the more obvious generator solution&lt;br /&gt;def cmap(f):&lt;br /&gt;    def r(items):&lt;br /&gt;        result = []&lt;br /&gt;        for i in items:&lt;br /&gt;            result.append(f(i))&lt;br /&gt;        return result&lt;br /&gt;    return r&lt;br /&gt;def cgrep(f):&lt;br /&gt;    def r(items):&lt;br /&gt;        result = []&lt;br /&gt;        for i in items:&lt;br /&gt;            if f(i):&lt;br /&gt;                result.append(i)&lt;br /&gt;        return result&lt;br /&gt;    return r&lt;br /&gt;&lt;br /&gt;# $double = cmap { $_ * 2 };&lt;br /&gt;# $find_slashdot = cgrep { $_-&gt;{referer} =~ /slashdot/i };&lt;br /&gt;double = cmap(lambda x: x*2)&lt;br /&gt;find_slashdot = cgrep(lambda x: "slashdot" in x["referer"].lower())&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub cmap (&amp;;@) {&lt;br /&gt;#   my $f = shift;&lt;br /&gt;#   my $r = sub {&lt;br /&gt;#     my @result;&lt;br /&gt;#     for (@_) {&lt;br /&gt;#       push @result, $f-&gt;($_);&lt;br /&gt;#     }&lt;br /&gt;#     @result;&lt;br /&gt;#   };&lt;br /&gt;#   return @_ ? $r-&gt;(@_) : $r;&lt;br /&gt;# }&lt;br /&gt;def cmap(f, lst=[]):&lt;br /&gt;    def r(_lst):&lt;br /&gt;        result = []&lt;br /&gt;        for item in _lst:&lt;br /&gt;            result.append(f(item))&lt;br /&gt;        return result&lt;br /&gt;    return r(lst) if lst != None else r&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# @doubles = cmap { $_ * 2 } (1..5);&lt;br /&gt;# @evens = cgrep { $_ % 2 == 0 } (1..10);&lt;br /&gt;doubles = cmap(lambda x: x*2, range(1,6))&lt;br /&gt;evens = cgrep(lambda x: x % 2 == 0, range(1,11))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub some_curried_function {&lt;br /&gt;#   my $first_arg = shift;&lt;br /&gt;#   my $r = sub {&lt;br /&gt;#     ...&lt;br /&gt;#   };&lt;br /&gt;#   return @_ ? $r-&gt;(@_) : $r;&lt;br /&gt;# }&lt;br /&gt;def some_curried_function(first_arg, lst=None):&lt;br /&gt;    def r(...):&lt;br /&gt;        ...&lt;br /&gt;    return r(lst) if lst != None else r&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# package Curry;&lt;br /&gt;# use base 'Exporter';&lt;br /&gt;# @EXPORT = ('curry');&lt;br /&gt;# @EXPORT_OK = qw(curry_listfunc curry_n);&lt;br /&gt;# sub curry {&lt;br /&gt;#   my $f = shift;&lt;br /&gt;#   return sub {&lt;br /&gt;#     my $first_arg = shift;&lt;br /&gt;#     my $r = sub { $f-&gt;($first_arg, @_) };&lt;br /&gt;#     return @_ ? $r-&gt;(@_) : $r;&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;# sub curry_listfunc {&lt;br /&gt;#   my $f = shift;&lt;br /&gt;#   return sub {&lt;br /&gt;#     my $first_arg = shift;&lt;br /&gt;#     return sub { $f-&gt;($first_arg, @_) };&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;# 1;&lt;br /&gt;## in python we have functools.partial&lt;br /&gt;## following the python way of doing this &lt;br /&gt;## we'll change this slightly and make it more&lt;br /&gt;## general but leave out the calling of function&lt;br /&gt;## if all parameters passed in&lt;br /&gt;def curry(func, *args, **kwargs):&lt;br /&gt;    def curried(*_args, **_kwargs):&lt;br /&gt;        return func(*(args+_args), **dict(kwargs.items() + _kwargs.items()))&lt;br /&gt;    return curried&lt;br /&gt;&lt;br /&gt;# sub imap (&amp;$) {&lt;br /&gt;#   my ($transform, $it) = @_;&lt;br /&gt;#   return sub {&lt;br /&gt;#     my $next = NEXTVAL($it);&lt;br /&gt;#     return unless defined $next;&lt;br /&gt;#     return $transform-&gt;($next);&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def imap(transform, it):&lt;br /&gt;    for next in it:&lt;br /&gt;        yield transform(next)&lt;br /&gt;&lt;br /&gt;# my $doubles_iterator = imap { $_[0] * 2 } $it;&lt;br /&gt;doubles_iterator = imap(lambda x: x*2, it)&lt;br /&gt;&lt;br /&gt;# my $doubles_a = imap { $_[0] * 2 } $it_a;&lt;br /&gt;# my $doubles_b = imap { $_[0] * 2 } $it_b;&lt;br /&gt;# my $doubles_c = imap { $_[0] * 2 } $it_c;&lt;br /&gt;doubles_a = imap(lambda x: x*2, it_a)&lt;br /&gt;doubles_b = imap(lambda x: x*2, it_b)&lt;br /&gt;doubles_c = imap(lambda x: x*2, it_c)&lt;br /&gt;&lt;br /&gt;# my $doubles_a = double $it_a;&lt;br /&gt;# my $doubles_b = double $it_b;&lt;br /&gt;# my $doubles_c = double $it_c;&lt;br /&gt;doubles_a = double(it_a)&lt;br /&gt;doubles_b = double(it_b)&lt;br /&gt;doubles_c = double(it_c)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub curry {&lt;br /&gt;#   my $f = shift;&lt;br /&gt;#   return sub (&amp;;@) {&lt;br /&gt;#     my $first_arg = shift;&lt;br /&gt;#     my $r = sub { $f-&gt;($first_arg, @_ ) };&lt;br /&gt;#     return @_ ? $r-&gt;(@_ ) : $r;&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;## this doesn't correspond with the python &lt;br /&gt;## way.  (skipping for now)&lt;br /&gt;&lt;br /&gt;## also skipping the method of currying more than&lt;br /&gt;## one arg since we get that for free&lt;br /&gt;&lt;br /&gt;# sub reduce { my $code = shift;&lt;br /&gt;#              my $val = shift;&lt;br /&gt;#              for (@_ ) { $val = $code-&gt;($val, $_ ) }&lt;br /&gt;#              return $val;&lt;br /&gt;#            }&lt;br /&gt;## reduce is a python builtin.  in python 3.x it will&lt;br /&gt;## live in functools&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# reduce(sub { $_[0] + $_[1] }, @VALUES) == sum(@VALUES)&lt;br /&gt;reduce(lambda x,y: x+y, VALUES) == sum(VALUES)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# reduce(sub { $_[0] &gt; $_[1] ? $_[0] : $_[1] }, @VALUES) == max(@VALUES)&lt;br /&gt;reduce(lambda x,y: x if x &gt; y else y, VALUES) == max(VALUES)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub fold {&lt;br /&gt;#   my $f = shift;&lt;br /&gt;#   my $fold;&lt;br /&gt;#   $fold = sub {&lt;br /&gt;#     my $x = shift;&lt;br /&gt;#     sub {&lt;br /&gt;#       return $x unless @_;&lt;br /&gt;#       my $first = shift;&lt;br /&gt;#       $fold-&gt;($f-&gt;($x, $first), @_ )&lt;br /&gt;#     }&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;## python's reduce already has the "initial"&lt;br /&gt;## parameter&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub interleave {&lt;br /&gt;#   my ($a, $b) = @_;&lt;br /&gt;#   return sub {&lt;br /&gt;#     my $next = $a-&gt;();&lt;br /&gt;#     unless (defined $next) {&lt;br /&gt;#       $a = $b;&lt;br /&gt;#       $next = $a-&gt;();&lt;br /&gt;#     }&lt;br /&gt;#     ($a, $b) = ($b, $a);&lt;br /&gt;#     $next;&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def interleave(a,b):&lt;br /&gt;    options = [a,b]&lt;br /&gt;    while options:&lt;br /&gt;        try:&lt;br /&gt;            yield options[0].next()&lt;br /&gt;            options = options[1:] + options[:1]&lt;br /&gt;        except StopIteration:&lt;br /&gt;            options.pop(0)&lt;br /&gt;        &lt;br /&gt;&lt;br /&gt;# package Iterator_Logic;&lt;br /&gt;# use base 'Exporter';&lt;br /&gt;# @EXPORT = qw(i_or_ i_or i_and_ i_and i_without_ i_without);&lt;br /&gt;# sub i_or_ {&lt;br /&gt;#   my ($cmp, $a, $b) = @_;&lt;br /&gt;#   my ($av, $bv) = ($a-&gt;(), $b-&gt;());&lt;br /&gt;#   return sub {&lt;br /&gt;#     my $rv;&lt;br /&gt;#     if (! defined $av &amp;&amp; ! defined $bv) { return }&lt;br /&gt;#     elsif (! defined $av) { $rv = $bv; $bv = $b-&gt;() }&lt;br /&gt;#     elsif (! defined $bv) { $rv = $av; $av = $a-&gt;() }&lt;br /&gt;#     else {&lt;br /&gt;#       my $d = $cmp-&gt;($av, $bv);&lt;br /&gt;#       if    ($d &lt; 0) { $rv = $av; $av = $a-&gt;() }&lt;br /&gt;#       elsif ($d &gt; 0) { $rv = $bv; $bv = $b-&gt;() }&lt;br /&gt;#       else           { $rv = $av; $av = $a-&gt;(); $bv = $b-&gt;() }&lt;br /&gt;#     }&lt;br /&gt;#     return $rv;&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;# use Curry;&lt;br /&gt;# BEGIN { *i_or = curry(\&amp;i_or_ ) }&lt;br /&gt;def i_or_(cmp, a, b):&lt;br /&gt;    notdefined = object()&lt;br /&gt;    def next_or_notdefined(x):&lt;br /&gt;        try: return x.next()&lt;br /&gt;        except StopIteration: return notdefined&lt;br /&gt;&lt;br /&gt;    av, bv = next_or_notdefined(a), next_or_notdefined(b)&lt;br /&gt;        &lt;br /&gt;    while 1:&lt;br /&gt;        if av == notdefined and bv == notdefined:&lt;br /&gt;            return&lt;br /&gt;        elif av == notdefined:&lt;br /&gt;            yield bv&lt;br /&gt;            bv = next_or_notdefined(b)&lt;br /&gt;        elif bv == notdefined:&lt;br /&gt;            yield av&lt;br /&gt;            av = next_or_notdefined(a)&lt;br /&gt;        else:&lt;br /&gt;            d = cmp(av,bv)&lt;br /&gt;            if d &lt; 0:&lt;br /&gt;                yield av&lt;br /&gt;                av = next_or_notdefined(a)&lt;br /&gt;            elif d &gt; 0:&lt;br /&gt;                yield bv&lt;br /&gt;                bv = next_or_notdefined(b)&lt;br /&gt;            else:&lt;br /&gt;                yield av&lt;br /&gt;                av = next_or_notdefined(a)&lt;br /&gt;                bv = next_or_notdefined(b)&lt;br /&gt;&lt;br /&gt;# sub i_and_ {&lt;br /&gt;#   my ($cmp, $a, $b) = @_;&lt;br /&gt;#   my ($av, $bv) = ($a-&gt;(), $b-&gt;());&lt;br /&gt;#   return sub {&lt;br /&gt;#     my $d;&lt;br /&gt;#     until (! defined $av || ! defined $bv ||&lt;br /&gt;#            ($d = $cmp-&gt;($av, $bv)) == 0) {&lt;br /&gt;#       if ($d &lt; 0) { $av = $a-&gt;() }&lt;br /&gt;#       else        { $bv = $b-&gt;() }&lt;br /&gt;#     }&lt;br /&gt;#     return unless defined $av &amp;&amp; defined $bv;&lt;br /&gt;#     my $rv = $av;&lt;br /&gt;#     ($av, $bv) = ($a-&gt;(), $b-&gt;());&lt;br /&gt;#     return $rv;&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;# BEGIN { *i_and = curry \&amp;i_and_ }&lt;br /&gt;def i_and_(cmp, a, b):&lt;br /&gt;    notdefined = object()&lt;br /&gt;    def next_or_notdefined(x):&lt;br /&gt;        try: return x.next()&lt;br /&gt;        except StopIteration: return notdefined&lt;br /&gt;&lt;br /&gt;    av, bv = next_or_notdefined(a), next_or_notdefined(b)&lt;br /&gt;        &lt;br /&gt;    while 1:&lt;br /&gt;        while not (av != notdefined or bv != notdefined &lt;br /&gt;                   or cmp(av,bv) == 0):&lt;br /&gt;            if cmp(av,bv) &lt; 0: &lt;br /&gt;                av = next_or_notdefined(a)&lt;br /&gt;            else:&lt;br /&gt;                bv = next_or_notdefined(b)&lt;br /&gt;        if av == notdefined and bv == notdefined:&lt;br /&gt;            return&lt;br /&gt;        rv = av&lt;br /&gt;        av, bv = next_or_notdefined(a), next_or_notdefined(b)&lt;br /&gt;        yield rv&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;### 7.4 Databases&lt;br /&gt;&lt;br /&gt;# package FlatDB_Compose;&lt;br /&gt;# use base 'FlatDB';&lt;br /&gt;# use base 'Exporter';&lt;br /&gt;# @EXPORT_OK = qw(query_or query_and query_not query_without);&lt;br /&gt;# use Iterator_Logic;&lt;br /&gt;# # usage: $dbh-&gt;query(fieldname, value)&lt;br /&gt;# # returns all records for which (fieldname) matches (value)&lt;br /&gt;# sub query {&lt;br /&gt;#   my $self = shift;&lt;br /&gt;#   my ($field, $value) = @_;&lt;br /&gt;#   my $fieldnum = $self-&gt;{FIELDNUM}{uc $field};&lt;br /&gt;#   return unless defined $fieldnum;&lt;br /&gt;#   my $fh = $self-&gt;{FH};&lt;br /&gt;#   seek $fh, 0, 0;&lt;br /&gt;#   &lt;$fh&gt;;                # discard header line&lt;br /&gt;#   my $position = tell $fh;&lt;br /&gt;#   my $recno = 0;&lt;br /&gt;#   return sub {&lt;br /&gt;#     local $_;&lt;br /&gt;#     seek $fh, $position, 0;&lt;br /&gt;#     while (&lt;$fh&gt;) {&lt;br /&gt;#       chomp;&lt;br /&gt;#       $recno++;&lt;br /&gt;#       $position = tell $fh;&lt;br /&gt;#       my @fields = split $self-&gt;{FIELDSEP};&lt;br /&gt;#       my $fieldval = $fields[$fieldnum];&lt;br /&gt;#       return [$recno, @fields] if $fieldval eq $value;&lt;br /&gt;#     }&lt;br /&gt;#     return;&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;def query(self, field, value):&lt;br /&gt;    fieldnum = self.fieldnum.get(field.upper())&lt;br /&gt;    if fieldnum == None:&lt;br /&gt;        return&lt;br /&gt;    fh = self.fh&lt;br /&gt;    fh.seek(0)&lt;br /&gt;    fh.readline() # discard schema line&lt;br /&gt;    recno = 0&lt;br /&gt;    while 1:&lt;br /&gt;        line = fh.readline()&lt;br /&gt;        recno += 1&lt;br /&gt;        if not line:&lt;br /&gt;            break&lt;br /&gt;        position = fh.tell()&lt;br /&gt;        fields = line.split(FlatDB.FIELDSEP)&lt;br /&gt;        fieldval = fields[fieldnum]&lt;br /&gt;        if fieldval == value:&lt;br /&gt;            yield recno, line.strip()&lt;br /&gt;        fh.seek(position)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub callbackquery {&lt;br /&gt;#   my $self = shift;&lt;br /&gt;#   my $is_interesting = shift;&lt;br /&gt;#   my $fh = $self-&gt;{FH};&lt;br /&gt;#   seek $fh, 0, SEEK_SET;&lt;br /&gt;#   &lt;$fh&gt;;                # discard header line&lt;br /&gt;#   my $position = tell $fh;&lt;br /&gt;#   my $recno = 0;&lt;br /&gt;#   return sub {&lt;br /&gt;#     local $_;&lt;br /&gt;#     seek $fh, $position, SEEK_SET;&lt;br /&gt;#     while (&lt;$fh&gt;) {&lt;br /&gt;#       $position = tell $fh;&lt;br /&gt;#       chomp;&lt;br /&gt;#       $recno++;&lt;br /&gt;#       my %F;&lt;br /&gt;#       my @fieldnames = @{$self-&gt;{FIELDS}};&lt;br /&gt;#       my @fields = split $self-&gt;{FIELDSEP};&lt;br /&gt;#       for (0 .. $#fieldnames) {&lt;br /&gt;#         $F{$fieldnames[$_]} = $fields[$_];&lt;br /&gt;#       }&lt;br /&gt;#       return [$recno, @fields] if $is_interesting-&gt;(%F);&lt;br /&gt;#     }&lt;br /&gt;#     return;&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;# 1;&lt;br /&gt;def callbackquery(self, is_interesting):&lt;br /&gt;    fh = self.fh&lt;br /&gt;    fh.seek(0)&lt;br /&gt;    fh.readline() # discard schema line&lt;br /&gt;    recno = 0&lt;br /&gt;    while 1:&lt;br /&gt;        line = fh.readline()&lt;br /&gt;        recno += 1&lt;br /&gt;        if not line:&lt;br /&gt;            break&lt;br /&gt;        position = fh.tell()&lt;br /&gt;        line = line.strip()&lt;br /&gt;        fieldnames = self.field&lt;br /&gt;        fields = line.split(FlatDB.FIELDSEP)&lt;br /&gt;        F = dict(zip(fieldnames, fields))&lt;br /&gt;        if is_interesting(F):&lt;br /&gt;            yield recno, line&lt;br /&gt;        fh.seek(position)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# # $a but not $b&lt;br /&gt;# sub i_without_ {&lt;br /&gt;#   my ($cmp, $a, $b) = @_;&lt;br /&gt;#   my ($av, $bv) = ($a-&gt;(), $b-&gt;());&lt;br /&gt;#   return sub {&lt;br /&gt;#     while (defined $av) {&lt;br /&gt;#       my $d;&lt;br /&gt;#       while (defined $bv &amp;&amp; ($d = $cmp-&gt;($av, $bv)) &gt; 0) {&lt;br /&gt;#         $bv = $b-&gt;();&lt;br /&gt;#       }&lt;br /&gt;#       if ( ! defined $bv || $d &lt; 0 ) {&lt;br /&gt;#         my $rv = $av; $av = $a-&gt;(); return $rv;&lt;br /&gt;#       } else {&lt;br /&gt;#         $bv = $b-&gt;();&lt;br /&gt;#         $av = $a-&gt;();&lt;br /&gt;#       }&lt;br /&gt;#     }&lt;br /&gt;#     return;&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;# BEGIN {&lt;br /&gt;#   *i_without = curry \&amp;i_without_;&lt;br /&gt;#   *query_without =&lt;br /&gt;#     i_without(sub { my ($a,$b) = @_; $a-&gt;[0] &lt;=&gt; $b-&gt;[0] });&lt;br /&gt;# }&lt;br /&gt;# 1;&lt;br /&gt;def i_without_(cmp, a, b):&lt;br /&gt;    notdefined = object()&lt;br /&gt;    def next_or_notdefined(x):&lt;br /&gt;        try: return x.next()&lt;br /&gt;        except StopIteration: return notdefined&lt;br /&gt;&lt;br /&gt;    av, bv = next_or_notdefined(a), next_or_notdefined(b)&lt;br /&gt;        &lt;br /&gt;    while av != notdefined:&lt;br /&gt;        d = cmp(av,bv)&lt;br /&gt;        while bv != notdefined and cmp(av,bv) &gt; 0:&lt;br /&gt;            bv = next_or_notdefined(b)&lt;br /&gt;            d = cmp(av,bv)&lt;br /&gt;        if bv == notdefined or d &lt; 0:&lt;br /&gt;            rv = av&lt;br /&gt;            av = next_or_notdefined(a)&lt;br /&gt;            yield rv&lt;br /&gt;        else:&lt;br /&gt;            bv = next_or_notdefined(b)&lt;br /&gt;            av = next_or_notdefined(a)&lt;br /&gt;query_without = functools.partial(i_without_, cmp)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub query_not {&lt;br /&gt;#   my $self = shift;&lt;br /&gt;#   my $q = shift;&lt;br /&gt;#   query_without($self-&gt;all, $q);&lt;br /&gt;# }&lt;br /&gt;def query_not(self, q):&lt;br /&gt;    return query_without(self.all(), q)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;### left off the last bit about operator overloading since it seems&lt;br /&gt;### orthogonal to how you'd do it in python&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-7123784488585690438?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/7123784488585690438/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=7123784488585690438' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/7123784488585690438'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/7123784488585690438'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2009/04/higher-order-perl-python-style-chapter.html' title='Higher Order Perl (Python Style) : Chapter 7 - Higher Order Functions And Currying'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-8601794864082378285</id><published>2009-04-04T21:30:00.000-07:00</published><updated>2009-04-04T21:46:37.714-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='squeak'/><title type='text'>Smalltalk for a Year - Status Report 2</title><content type='html'>So my goal was to do monthly updates on my smalltalk progress during this year.  For January I was going strong.  Learning my way around, getting comfortable with syntax deciding what would be the best way to spend my energies, getting over the newness/alien-ness.&lt;br /&gt;&lt;br /&gt;And then sometime in February I started losing momentum.  And then into March a complete and utter stop.  I even forgot I had this goal of learning smalltalk.   So March became the month of figuring out why things had gone so badly and trying to pick myself up again.&lt;br /&gt;&lt;br /&gt;There were a combination of factors that contributed to my &lt;a href="http://en.wikipedia.org/wiki/Akrasia"&gt;akrasia&lt;/a&gt; (hey, it's a word!).  Partly this was due to me being a little under the weather and trying to be the kind of person who actually listens to their body and goes to sleep when they are tired.  In the middle of this I got  bogged down in a seaside tutorial.  At some point the code I typed in to follow along with the example wasn't working the way it should.  I kept debugging this and that but looking at the same problem day after day when you are tired and new to a language is a bit of a lethal combination.&lt;br /&gt;&lt;br /&gt;And then at some point hibernate on my window's laptop came up as a blue screen of death and I forgot to reopen the tutorial and my squeak image to remind me to take a look.  At this point my amnesia was pretty much total.&lt;br /&gt;&lt;br /&gt;Another thing I realized is that learning smalltalk *and* a web framework at the same time is a lot to take in.  Especially if you don't really believe that you are going to use the web framework day to day.&lt;br /&gt;&lt;br /&gt;I'm actually really interested in seaside.  It seems fascinating.  I'm a little distressed that I've developed a streak of practicality that makes it hard to focus on things that aren't directly useful to me. That's just not the geek way.&lt;br /&gt;&lt;br /&gt;In any case March was the month of getting back on the horse.  So here is my plan now.   There are four things I'm going to work on in one order or another that I believe will be able to keep my attention.&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;convert Collective Intelligence code to smalltalk.    I've already read this through once and did all the examples in    python.  i find that converting examples to a new language really    makes it hard to skip over parts that you don't really understand.&lt;br /&gt;&lt;li&gt; read &lt;a href="http://homepages.ecs.vuw.ac.nz/~tk/fps/"&gt;Functional Pattern System for Object-Oriented Design&lt;/a&gt; and implement in smalltalk.    I've been really interested in the streams data model and how it solves    the hamming problem.  I've been surprised not to see an easily     found solution in smalltalk.  i'm hoping to be able to develop a solution    by reading this book.&lt;br /&gt;&lt;li&gt;investigate &lt;a href="http://smallwiki.unibe.ch/spy/"&gt;SPY&lt;/a&gt;.      I'm keenly interested in pypy and virtual machines.     also i never really seem to make progress on learning a language     until i start peeling back the curtains to see how things work under     the covers&lt;br /&gt;&lt;li&gt; read thru &lt;a href="http://news.squeak.org/2009/01/19/soup-for-squeak/"&gt;squeak soup&lt;/a&gt; code.    I really like using the python version of this and this is    a sort of problem I'm interested in and feel it would be useful to    understand how it is solved.&lt;br /&gt;&lt;/ul&gt;One other thing.  I'm purposely changing my learning slot from evening to morning.  I've traditionally been more likely to  keep promises to myself in the morning than the evening.&lt;br /&gt;&lt;br /&gt;OK, let's get that momentum going again.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-8601794864082378285?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/8601794864082378285/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=8601794864082378285' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/8601794864082378285'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/8601794864082378285'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2009/04/smalltalk-for-year-status-report-2.html' title='Smalltalk for a Year - Status Report 2'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-4506294623902791492</id><published>2009-03-25T21:00:00.000-07:00</published><updated>2009-03-25T21:07:45.053-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='functional programming'/><category scheme='http://www.blogger.com/atom/ns#' term='higher order perl'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='hop'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><title type='text'>Higher Order Perl (Python Style) : Chapter 6 - Infinite Streams</title><content type='html'>&lt;br&gt;&lt;br&gt;&lt;br /&gt;&lt;a href="http://dustbunnylair.blogspot.com/2009/01/higher-order-perl-python-style-toc.html"&gt;TOC&lt;/a&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;### Chapter 6 - Infinite Streams&lt;br /&gt;&lt;br /&gt;### 6.1 Linked Lists&lt;br /&gt;&lt;br /&gt;# sub node {&lt;br /&gt;#   my ($h, $t) = @_;&lt;br /&gt;#   [$h, $t];&lt;br /&gt;# }&lt;br /&gt;# sub head {&lt;br /&gt;#   my ($ls) = @_;&lt;br /&gt;#   $ls-&gt;[0];&lt;br /&gt;# }&lt;br /&gt;# sub tail {&lt;br /&gt;#   my ($ls) = @_;&lt;br /&gt;#   $ls-&gt;[1];&lt;br /&gt;# }&lt;br /&gt;# sub set_head {&lt;br /&gt;#   my ($ls, $new_head) = @_;&lt;br /&gt;#   $ls-&gt;[0] = $new_head;&lt;br /&gt;# }&lt;br /&gt;# sub set_tail {&lt;br /&gt;#   my ($ls, $new_tail) = @_;&lt;br /&gt;#   $ls-&gt;[1] = $new_tail;&lt;br /&gt;# }&lt;br /&gt;def node(h,t):&lt;br /&gt;    return [h,t]&lt;br /&gt;def head(ls):&lt;br /&gt;    return ls[0]&lt;br /&gt;def tail(ls):&lt;br /&gt;    return ls[1]&lt;br /&gt;def set_head(ls, new_head):&lt;br /&gt;    ls[0] = new_head&lt;br /&gt;def set_tail(ls, new_tail):&lt;br /&gt;    ls[1] = new_tail&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# $my_list = node($new_data, $my_list);&lt;br /&gt;my_list = node(new_data, my_list)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub insert_after {&lt;br /&gt;#   my ($node, $new_data) = @_;&lt;br /&gt;#   my $new_node = node($new_data, tail($node));&lt;br /&gt;#   set_tail($node, $new_node);&lt;br /&gt;# }&lt;br /&gt;def insert_after(node, new_data):&lt;br /&gt;    new_node = node(new_data, tail(node))&lt;br /&gt;    set_tail(node, new_node)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;### 6.2 Lazy Linked Lists&lt;br /&gt;&lt;br /&gt;# package Stream;&lt;br /&gt;&lt;br /&gt;# use base Exporter;&lt;br /&gt;# @EXPORT_OK = qw(node head tail drop upto upfrom show promise&lt;br /&gt;#                 filter transform merge list_to_stream cutsort&lt;br /&gt;#                 iterate_function cut_loops);&lt;br /&gt;# %EXPORT_TAGS = ('all' =&gt; \@EXPORT_OK);&lt;br /&gt;# sub node {&lt;br /&gt;#   my ($h, $t) = @_;&lt;br /&gt;#   [$h, $t];&lt;br /&gt;# }&lt;br /&gt;# sub head {&lt;br /&gt;#   my ($s) = @_;&lt;br /&gt;#   $s-&gt;[0];&lt;br /&gt;# }&lt;br /&gt;# sub tail {&lt;br /&gt;#   my ($s) = @_;&lt;br /&gt;#   if (is_promise($s-&gt;[1])) {&lt;br /&gt;#     return $s-&gt;[1]-&gt;();&lt;br /&gt;#   }&lt;br /&gt;#   $s-&gt;[1];&lt;br /&gt;# }&lt;br /&gt;# sub is_promise {&lt;br /&gt;#   UNIVERSAL::isa($_[0], 'CODE');&lt;br /&gt;# }&lt;br /&gt;def node(h,t):&lt;br /&gt;    return [h,t]&lt;br /&gt;def head(s):&lt;br /&gt;    return s[0]&lt;br /&gt;def tail(s):&lt;br /&gt;    if is_promise(s[1]):&lt;br /&gt;        return s[1]()&lt;br /&gt;    return s[1]&lt;br /&gt;def is_promise(s):&lt;br /&gt;    return callable(s)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub promise (&amp;) { $_[0] }&lt;br /&gt;# this is the silliest kind of syntactic sugar&lt;br /&gt;# but this will avoid some scoping confusion and &lt;br /&gt;# premature execution i was experiencing&lt;br /&gt;# in later examples and makes it look more like the perl example&lt;br /&gt;def promise(func):&lt;br /&gt;    return func&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub upto_list {&lt;br /&gt;#   my ($m, $n) = @_;&lt;br /&gt;#   return if $m &gt; $n;&lt;br /&gt;#   node($m, upto_list($m+1, $n));&lt;br /&gt;# }&lt;br /&gt;def upto_list(m, n):&lt;br /&gt;    if m &gt; n:&lt;br /&gt;        return None&lt;br /&gt;    return node(m, upto_list(m+1, n))&lt;br /&gt;&lt;br /&gt;# sub upto {&lt;br /&gt;#   my ($m, $n) = @_;&lt;br /&gt;#   return if $m &gt; $n;&lt;br /&gt;#   node($m, promise { upto($m+1, $n) } );&lt;br /&gt;# }&lt;br /&gt;def upto(m, n):&lt;br /&gt;    if m &gt; n:&lt;br /&gt;        return None&lt;br /&gt;    return node(m, promise(lambda: upto(m+1, n)))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub upfrom {&lt;br /&gt;#   my ($m) = @_;&lt;br /&gt;#   node($m, promise { upfrom($m+1) } );&lt;br /&gt;# }&lt;br /&gt;def upfrom(m):&lt;br /&gt;    return node(m, promise(lambda: upfrom(m+1)))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub upfrom_list {&lt;br /&gt;#   my ($m) = @_;&lt;br /&gt;#   node($m, upfrom_list($m+1) );&lt;br /&gt;# }&lt;br /&gt;def upfrom_list(m):&lt;br /&gt;    return node(m, upfrom_list(m+1))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub show {&lt;br /&gt;#   my $s = shift;&lt;br /&gt;#   while ($s) {&lt;br /&gt;#     print head($s), $";&lt;br /&gt;#     $s = tail($s);&lt;br /&gt;#   }&lt;br /&gt;#   print $/;&lt;br /&gt;# }&lt;br /&gt;def show(s):&lt;br /&gt;    while(s):&lt;br /&gt;        print head(s), &lt;br /&gt;        s = tail(s)&lt;br /&gt;    print&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub show {&lt;br /&gt;#   my ($s, $n) = @_;&lt;br /&gt;#   while ($s &amp;&amp; (! defined $n || $n-- &gt; 0)) {&lt;br /&gt;#     print head($s), $";&lt;br /&gt;#     $s = tail($s);&lt;br /&gt;#   }&lt;br /&gt;#   print $/;&lt;br /&gt;# }&lt;br /&gt;# NOTE: i'm following the perlish code here&lt;br /&gt;# but in real life i would use itertools&lt;br /&gt;def show(s, n=None):&lt;br /&gt;    while s and (n == None or n &gt; 0):&lt;br /&gt;        print head(s),&lt;br /&gt;        s = tail(s)&lt;br /&gt;        if n != None:&lt;br /&gt;            n -= 1&lt;br /&gt;    print&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub drop {&lt;br /&gt;#   my $h = head($_[0]);&lt;br /&gt;#   $_[0] = tail($_[0]);&lt;br /&gt;#   return $h;&lt;br /&gt;# }&lt;br /&gt;# to do this in python we'd either&lt;br /&gt;# need to make a stream class  or&lt;br /&gt;# return a pair (h,s) &lt;br /&gt;# where s is the stream in it's new state&lt;br /&gt;def drop(s):&lt;br /&gt;    h, tail = s&lt;br /&gt;    return h, tail # which really was a noop&lt;br /&gt;&lt;br /&gt;# OR something like:&lt;br /&gt;class Stream(object):&lt;br /&gt;    def __init__(self, contents):&lt;br /&gt;        self.contents = contents&lt;br /&gt;&lt;br /&gt;    def drop(self):&lt;br /&gt;        h = head(self.contents)&lt;br /&gt;        self.contents = tail(contents)&lt;br /&gt;        return h&lt;br /&gt;    &lt;br /&gt;&lt;br /&gt;# sub show {&lt;br /&gt;#   my ($s, $n) = @_;&lt;br /&gt;#   while ($s &amp;&amp; (! defined $n || $n-- &gt; 0)) {&lt;br /&gt;#     print drop($s), $";&lt;br /&gt;#   }&lt;br /&gt;#   print $/;&lt;br /&gt;# }&lt;br /&gt;### we don't really get any savings here so i'll skip this&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub transform (&amp;$) {&lt;br /&gt;#   my $f = shift;&lt;br /&gt;#   my $s = shift;&lt;br /&gt;#   return unless $s;&lt;br /&gt;#   node($f-&gt;(head($s)),&lt;br /&gt;#        promise { transform($f, tail($s)) });&lt;br /&gt;# }&lt;br /&gt;def transform(f, s):&lt;br /&gt;    if not s:&lt;br /&gt;        return None&lt;br /&gt;    return node(f(head(s)),&lt;br /&gt;                promise(lambda: transform(f, tail(s))))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# my $evens = transform { $_[0] * 2 } upfrom(1);&lt;br /&gt;evens = transform(lambda x: x * 2, upfrom(1))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub filter (&amp;$) {&lt;br /&gt;#   my $f = shift;&lt;br /&gt;#   my $s = shift;&lt;br /&gt;#   until (! $s || $f-&gt;(head($s))) {&lt;br /&gt;#     drop($s);&lt;br /&gt;#   }&lt;br /&gt;#   return if ! $s;&lt;br /&gt;#   node(head($s),&lt;br /&gt;#        promise { filter($f, tail($s)) });&lt;br /&gt;# }&lt;br /&gt;def filter(f, s):&lt;br /&gt;    while not (not s or f(head(s))):&lt;br /&gt;        s = tail(s)&lt;br /&gt;&lt;br /&gt;    if not s:&lt;br /&gt;        return None&lt;br /&gt;&lt;br /&gt;    return node(head(s), &lt;br /&gt;                promise(lambda: filter(f, tail(s))))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub iterate_function {&lt;br /&gt;#   my ($f, $x) = @_;&lt;br /&gt;#   node($x, promise { iterate_function($f, $f-&gt;($x)) });&lt;br /&gt;# }&lt;br /&gt;def iterate_function(f, x):&lt;br /&gt;    return node(x, promise(lambda: iterate_function(f, f(x))))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;### 6.3 Recursive Streams&lt;br /&gt;&lt;br /&gt;# sub carrots {&lt;br /&gt;#   node('carrot', promise { carrots() });&lt;br /&gt;# }&lt;br /&gt;# my $carrots = carrots();&lt;br /&gt;def carrots():&lt;br /&gt;    return node("carrot", promise(carrots))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# my $carrots = node('carrot', promise { carrots() });&lt;br /&gt;carrots = node("carrot", promise(carrots()))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# my $carrots = node('carrot', promise { $carrots });&lt;br /&gt;carrots = node("carrot", promise(lambda: carrots))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub pow2_from {&lt;br /&gt;#   my $n = shift;&lt;br /&gt;#   node($n, promise {pow2_from($n*2)})&lt;br /&gt;# }&lt;br /&gt;# my $powers_of_2 = pow2_from(1);&lt;br /&gt;def pow2_from(n):&lt;br /&gt;    return node(n, promise(lambda: pow2_from(n*2)))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# my $powers_of_2;&lt;br /&gt;# $powers_of_2 =&lt;br /&gt;#   node(1, promise { transform {$_[0]*2} $powers_of_2 });&lt;br /&gt;# def powers_of_2():&lt;br /&gt;#     return node(1, powers_of_2)&lt;br /&gt;powers_of_2 = node(1, promise(lambda: transform(lambda x: x*2, powers_of_2)))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub tail {&lt;br /&gt;#   my ($s) = @_;&lt;br /&gt;#   if (is_promise($s-&gt;[1])) {&lt;br /&gt;#     $s-&gt;[1] = $s-&gt;[1]-&gt;();&lt;br /&gt;#   }&lt;br /&gt;#   $s-&gt;[1];&lt;br /&gt;# }&lt;br /&gt;def tail(s):&lt;br /&gt;    if is_promise(s[1]):&lt;br /&gt;        s[1] =  s[1]()&lt;br /&gt;    return s[1]&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;### 6.4 The Hamming Problem&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub is_hamming {&lt;br /&gt;#   my $n = shift;&lt;br /&gt;#   $n/=2 while $n%2 == 0;&lt;br /&gt;#   $n/=3 while $n%3 == 0;&lt;br /&gt;#   $n/=5 while $n%5 == 0;&lt;br /&gt;#   return $n == 1;&lt;br /&gt;# }&lt;br /&gt;# # Return the first $N hamming numbers&lt;br /&gt;# sub hamming {&lt;br /&gt;#   my $N = shift;&lt;br /&gt;#   my @hamming;&lt;br /&gt;#   my $t = 1;&lt;br /&gt;#   until (@hamming == $N) {&lt;br /&gt;#     push @hamming, $t if is_hamming($t);&lt;br /&gt;#     $t++;&lt;br /&gt;#   }&lt;br /&gt;#   @hamming;&lt;br /&gt;# }&lt;br /&gt;def is_hamming(n):&lt;br /&gt;    while n % 2 == 0:&lt;br /&gt;        n /= 2&lt;br /&gt;    while n % 3 == 0:&lt;br /&gt;        n /= 3&lt;br /&gt;    while n % 5 == 0:&lt;br /&gt;        n /= 5&lt;br /&gt;&lt;br /&gt;    return n == 1&lt;br /&gt;def hamming(N):&lt;br /&gt;    result = []&lt;br /&gt;    t = 1&lt;br /&gt;    while len(result) &lt; N:&lt;br /&gt;        if is_hamming(t):&lt;br /&gt;            result.append(t)&lt;br /&gt;        t += 1&lt;br /&gt;    return result&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub merge {&lt;br /&gt;#   my ($S, $T) = @_;&lt;br /&gt;#   return $T unless $S;&lt;br /&gt;#   return $S unless $T;&lt;br /&gt;#   my ($s, $t) = (head($S), head($T));&lt;br /&gt;#   if ($s &gt; $t) {&lt;br /&gt;#      node($t, promise {merge(     $S,  tail($T))});&lt;br /&gt;#    } elsif ($s &lt; $t) {&lt;br /&gt;#      node($s, promise {merge(tail($S),      $T)});&lt;br /&gt;#    } else {&lt;br /&gt;#      node($s, promise {merge(tail($S), tail($T))});&lt;br /&gt;#    }&lt;br /&gt;# }&lt;br /&gt;def merge(S,T):&lt;br /&gt;    if not S:&lt;br /&gt;        return T&lt;br /&gt;    if not T:&lt;br /&gt;        return S&lt;br /&gt;    s, t = head(S), head(T)&lt;br /&gt;    if s &gt; t:&lt;br /&gt;        return node(t, promise(lambda: merge(S, tail(T))))&lt;br /&gt;    elif s &lt; t:&lt;br /&gt;        return node(s, promise(lambda: merge(tail(S), T)))&lt;br /&gt;    else:&lt;br /&gt;        return node(s, promise(lambda: merge(tail(S), tail(T))))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub scale {&lt;br /&gt;#   my ($s, $c) = @_;&lt;br /&gt;#   transform { $_[0]*$c } $s;&lt;br /&gt;# }&lt;br /&gt;def scale(s, c):&lt;br /&gt;    return transform(lambda x: x * c, s)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# my $hamming;&lt;br /&gt;# $hamming = node(1,&lt;br /&gt;#                 promise {&lt;br /&gt;#                   merge(scale($hamming, 2),&lt;br /&gt;#                   merge(scale($hamming, 3),&lt;br /&gt;#                         scale($hamming, 5),&lt;br /&gt;#                        ))&lt;br /&gt;#                 }&lt;br /&gt;#                );&lt;br /&gt;# show($hamming, 3000);&lt;br /&gt;hamming = node(1,&lt;br /&gt;               promise(lambda: merge(scale(hamming, 2),&lt;br /&gt;                                     merge(scale(hamming, 3),&lt;br /&gt;                                           scale(hamming, 5),&lt;br /&gt;                                     ))&lt;br /&gt;                         ))&lt;br /&gt;### just for kicks here are two solutions from the tubes:&lt;br /&gt;### - OO version : http://aspn.activestate.com/ASPN/Mail/Message/python-list/905315&lt;br /&gt;### - iterator verion (my preference) : http://mail.python.org/pipermail/python-list/2005-January/303480.html&lt;br /&gt;### There is also a couple variations of this in python's test suite: test_generators.py&lt;br /&gt;&lt;br /&gt;### but i'd wager that the haskell solution of this is still the king&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;### 6.5 Regex String Generation&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# package Regex;&lt;br /&gt;# use Stream ':all';&lt;br /&gt;# use base 'Exporter';&lt;br /&gt;# @EXPORT_OK = qw(literal union concat star plus charclass show&lt;br /&gt;#                 matches);&lt;br /&gt;&lt;br /&gt;# sub literal {&lt;br /&gt;#   my $string = shift;&lt;br /&gt;#   node($string, undef);&lt;br /&gt;# }&lt;br /&gt;# show(literal("foo"));&lt;br /&gt;# foo&lt;br /&gt;def literal(s):&lt;br /&gt;    return node(s, None)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub mingle2 {&lt;br /&gt;#   my ($s, $t) = @_;&lt;br /&gt;#   return $t unless $s;&lt;br /&gt;#   return $s unless $t;&lt;br /&gt;#   node(head($s),&lt;br /&gt;#        node(head($t),&lt;br /&gt;#                 promise { mingle2(tail($s),&lt;br /&gt;#                                   tail($t))&lt;br /&gt;#                                 }&lt;br /&gt;#       ));&lt;br /&gt;# }&lt;br /&gt;def mingle2(s, t):&lt;br /&gt;    if not s:&lt;br /&gt;        return t&lt;br /&gt;    if not t:&lt;br /&gt;        return s&lt;br /&gt;    return node(head(s),&lt;br /&gt;                node(head(t),&lt;br /&gt;                     promise(lambda: mingle2(tail(s), tail(t)))))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub union {&lt;br /&gt;#   my ($h, @s) = grep $_, @_;&lt;br /&gt;#   return unless $h;&lt;br /&gt;#   return $h unless @s;&lt;br /&gt;#   node(head($h),&lt;br /&gt;#        promise {&lt;br /&gt;#     union(@s, tail($h));&lt;br /&gt;#   });&lt;br /&gt;# }&lt;br /&gt;def union(*streams):&lt;br /&gt;    streams = [_s for _s in streams if _s != None]&lt;br /&gt;    if len(streams) == 0:&lt;br /&gt;        return None&lt;br /&gt;&lt;br /&gt;    if len(streams) == 1:&lt;br /&gt;        return streams[0]&lt;br /&gt;&lt;br /&gt;    return node(head(streams[0]),&lt;br /&gt;                promise(lambda: union(*(streams[1:]+[tail(streams[0])]))))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# # generate infinite stream ($k:1, $k:2, $k:3, ...)&lt;br /&gt;# sub constant {&lt;br /&gt;#   my $k = shift;&lt;br /&gt;#   my $i = shift || 1;&lt;br /&gt;#   my $s = node("$k:$i", promise { constant($k, $i+1) });&lt;br /&gt;# }&lt;br /&gt;# my $fish = constant('fish');&lt;br /&gt;# show($fish, 3);&lt;br /&gt;# fish:1 fish:2 fish:3&lt;br /&gt;# my $soup = union($fish, constant('dog'), constant('carrot'));&lt;br /&gt;# show($soup, 10);&lt;br /&gt;# fish:1 dog:1 carrot:1 fish:2 dog:2 carrot:2 fish:3 dog:3 carrot:3 fish:4&lt;br /&gt;def constant(k, i=1):&lt;br /&gt;    return node("%s:%s" % (k,i),&lt;br /&gt;                promise(lambda: constant(k, i+1)))&lt;br /&gt;fish = constant("fish")&lt;br /&gt;soup = union(fish, constant("dog"), constant("carrot"))&lt;br /&gt;show(soup, 10)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub concat {&lt;br /&gt;#   my ($S, $T) = @_;&lt;br /&gt;#   return unless $S &amp;&amp; $T;&lt;br /&gt;#   my ($s, $t) = (head($S), head($T));&lt;br /&gt;#   node("$s$t", promise {&lt;br /&gt;#     union(postcat(tail($S), $t),&lt;br /&gt;#            precat(tail($T), $s),&lt;br /&gt;#           concat(tail($S), tail($T)),&lt;br /&gt;#          )&lt;br /&gt;#   });&lt;br /&gt;# }&lt;br /&gt;# sub precat {&lt;br /&gt;#   my ($s, $c) = @_;&lt;br /&gt;#   transform {"$c$_[0]"} $s;&lt;br /&gt;# }&lt;br /&gt;# sub postcat {&lt;br /&gt;#   my ($s, $c) = @_;&lt;br /&gt;#   transform {"$_[0]$c"} $s;&lt;br /&gt;# }&lt;br /&gt;&lt;br /&gt;def concat(S,T):&lt;br /&gt;    if None in (S, T):&lt;br /&gt;        return None&lt;br /&gt;&lt;br /&gt;    s, t = head(S), head(T)&lt;br /&gt;    return node("%s%s" % (s,t),&lt;br /&gt;                promise(lambda: (union(postcat(tail(S), t),&lt;br /&gt;                                     precat(tail(T),s),&lt;br /&gt;                                     concat(tail(S),tail(T))))))&lt;br /&gt;&lt;br /&gt;def precat(s,c):&lt;br /&gt;    return transform(lambda x: "%s%s" % (c,x), s)&lt;br /&gt;&lt;br /&gt;def postcat(s,c):&lt;br /&gt;    return transform(lambda x: "%s%s" % (x,c), s)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# # Im /(a|b)(c|d)$/&lt;br /&gt;# my $z = concat(union(literal("a"), literal("b")),&lt;br /&gt;#                union(literal("c"), literal("d")),&lt;br /&gt;#               );&lt;br /&gt;# show($z);&lt;br /&gt;z = (concat(union(literal("a"), literal("b")),&lt;br /&gt;            union(literal("c"), literal("d")),&lt;br /&gt;            ))&lt;br /&gt;show(z)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub star {&lt;br /&gt;#   my $s = shift;&lt;br /&gt;#   my $r;&lt;br /&gt;#   $r = node("", promise { concat($s, $r) });&lt;br /&gt;# }&lt;br /&gt;# def star(s):&lt;br /&gt;#     _s = s[0]&lt;br /&gt;#     return iterate_function(lambda x: x + _s, "")&lt;br /&gt;def star(s):&lt;br /&gt;    r = node("", promise(lambda: concat(s, r)))&lt;br /&gt;    return r&lt;br /&gt;    &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub show {&lt;br /&gt;#   my ($s, $n) = @_;&lt;br /&gt;#   while ($s &amp;&amp; (! defined $n || $n-- &gt; 0)) {&lt;br /&gt;#     print qq{"}, drop($s), qq{"\n};&lt;br /&gt;#   }&lt;br /&gt;#   print "\n";&lt;br /&gt;# }&lt;br /&gt;def show(s, n=None):&lt;br /&gt;    while s and (n == None or n &gt; 0):&lt;br /&gt;        print repr(head(s))&lt;br /&gt;        s = tail(s)&lt;br /&gt;        if n != None:&lt;br /&gt;            n -= 1&lt;br /&gt;    print&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# # charclass('abc') = /[abc]$/&lt;br /&gt;# sub charclass {&lt;br /&gt;#   my $class = shift;&lt;br /&gt;#   union(map literal($_), split(//, $class));&lt;br /&gt;# }&lt;br /&gt;def charclass(_class):&lt;br /&gt;    return union(*[literal(c) for c in _class])&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# # plus($s) = /s+$/&lt;br /&gt;# sub plus {&lt;br /&gt;#   my $s = shift;&lt;br /&gt;#   concat($s, star($s));&lt;br /&gt;# }&lt;br /&gt;def plus(s):&lt;br /&gt;    return concat(s, star(s))&lt;br /&gt;&lt;br /&gt;# use Regex qw(concat star literal show);&lt;br /&gt;# # I represent /ab*$/&lt;br /&gt;# my $regex1 = concat(     literal("a"),&lt;br /&gt;#                     star(literal("b"))&lt;br /&gt;#                    );&lt;br /&gt;# show($regex1, 10);&lt;br /&gt;regex1 = concat(  literal("a"),&lt;br /&gt;                star(literal("b"))&lt;br /&gt;               )&lt;br /&gt;show(regex1, 10)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# # I represent /(aa|b)*$/&lt;br /&gt;# my $regex2 = star(union(literal("aa"),&lt;br /&gt;#                         literal("b"),&lt;br /&gt;#                        ));&lt;br /&gt;# show($regex2, 16);&lt;br /&gt;regex2 = star(union(literal("aa"),&lt;br /&gt;                        literal("b"),&lt;br /&gt;                       ))&lt;br /&gt;show(regex2, 16)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# # I represent /(ab+|c)*$/&lt;br /&gt;# my $regex3 = star(union(concat(      literal("a"),&lt;br /&gt;#                                plus(literal("b"))),&lt;br /&gt;#                         literal("c")&lt;br /&gt;#                        ));&lt;br /&gt;# show($regex3, 20);&lt;br /&gt;regex3 = star(union(concat(      literal("a"),&lt;br /&gt;                            plus(literal("b"))),&lt;br /&gt;                     literal("c")&lt;br /&gt;                    ))&lt;br /&gt;show(regex3, 20)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub union {&lt;br /&gt;#   my (@s) = grep $_, @_;&lt;br /&gt;#   return unless @s;&lt;br /&gt;#   return $s[0] if @s == 1;&lt;br /&gt;#   my $si = index_of_shortest(@s);&lt;br /&gt;#   node(head($s[$si]),&lt;br /&gt;#               promise {&lt;br /&gt;#                 union(map $_ == $si ? tail($s[$_]) : $s[$_],&lt;br /&gt;#                           0 .. $#s);&lt;br /&gt;#               });&lt;br /&gt;# }&lt;br /&gt;# curse python's lame lambdas!&lt;br /&gt;def union(*streams):&lt;br /&gt;    streams = [_s for _s in streams if _s != None]&lt;br /&gt;    if len(streams) == 0:&lt;br /&gt;        return None&lt;br /&gt;&lt;br /&gt;    if len(streams) == 1:&lt;br /&gt;        return streams[0]&lt;br /&gt;&lt;br /&gt;    si = index_of_shortest(streams)&lt;br /&gt;&lt;br /&gt;    return node(head(streams[si]),&lt;br /&gt;                promise(lambda: union(*[_s if _i != si else tail(_s) for (_i, _s) in enumerate(streams)])))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub index_of_shortest {&lt;br /&gt;#   my @s = @_;&lt;br /&gt;#   my $minlen = length(head($s[0]));&lt;br /&gt;#   my $si = 0;&lt;br /&gt;#   for (1 .. $#s) {&lt;br /&gt;#     my $h = head($s[$_]);&lt;br /&gt;#     if (length($h) &lt; $minlen) {&lt;br /&gt;#       $minlen = length($h);&lt;br /&gt;#       $si = $_;&lt;br /&gt;#     }&lt;br /&gt;#   }&lt;br /&gt;#   $si;&lt;br /&gt;# }&lt;br /&gt;def index_of_shortest(*streams):&lt;br /&gt;    minlin = len(head(streams[0]))&lt;br /&gt;    si = 0&lt;br /&gt;    for i in range(1,len(streams)):&lt;br /&gt;        h = head(streams[i])&lt;br /&gt;        if len(h) &lt; minlin:&lt;br /&gt;            minlen = len(h)&lt;br /&gt;            si = i&lt;br /&gt;    return si&lt;br /&gt;### ugg.  i can't seem to get correct ordering to work. :(&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub matches {&lt;br /&gt;#   my ($string, $regex) = @_;&lt;br /&gt;#   while ($regex) {&lt;br /&gt;#     my $s = drop($regex);&lt;br /&gt;#     return 1 if $s eq $string;&lt;br /&gt;#     return 0 if length($s) &gt; length($string);&lt;br /&gt;#   }&lt;br /&gt;#   return 0;&lt;br /&gt;# }&lt;br /&gt;def matches(string, regex):&lt;br /&gt;    while regex:&lt;br /&gt;        s = head(regex)&lt;br /&gt;        regex = tail(regex)&lt;br /&gt;        if s == string:&lt;br /&gt;            return True&lt;br /&gt;        if len(s) &gt; len(string):&lt;br /&gt;            return False&lt;br /&gt;    return False&lt;br /&gt;# ugg.  this fails when star is involved&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub bal {&lt;br /&gt;#   my $contents = shift;&lt;br /&gt;#   my $bal;&lt;br /&gt;#   $bal = node("", promise {&lt;br /&gt;#     concat($bal,&lt;br /&gt;#            union($contents,&lt;br /&gt;#                  transform {"($_[0])"} $bal,&lt;br /&gt;#                 )&lt;br /&gt;#           )&lt;br /&gt;#        });&lt;br /&gt;# }&lt;br /&gt;def bal(contents):&lt;br /&gt;    _bal = node("",&lt;br /&gt;                promise(lambda: union(contents, &lt;br /&gt;                                      transform(lambda x: "(%s)" % x, _bal))))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    return _bal&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub cut_bylen {&lt;br /&gt;#   my ($a, $b) = @_;&lt;br /&gt;#   # Its OK to emit item $a if the next item in the stream is $b&lt;br /&gt;#   length($a) &lt; length($b);&lt;br /&gt;# }&lt;br /&gt;def cut_bylen(a,b):&lt;br /&gt;    return len(a) &lt; len(b)&lt;br /&gt;&lt;br /&gt;# sub list_to_stream {&lt;br /&gt;#   my $node = pop;&lt;br /&gt;#   while (@_) {&lt;br /&gt;#     $node = node(pop, $node);&lt;br /&gt;#   }&lt;br /&gt;#   $node;&lt;br /&gt;# }&lt;br /&gt;def list_to_stream(nodes):&lt;br /&gt;    _nodes = nodes[:]&lt;br /&gt;    _node = _nodes.pop()&lt;br /&gt;    while _nodes:&lt;br /&gt;        _node = node(_nodes.pop(), _node)&lt;br /&gt;    return _node&lt;br /&gt;&lt;br /&gt;# sub insert (\@$$);&lt;br /&gt;# sub cutsort {&lt;br /&gt;#   my ($s, $cmp, $cut, @pending) = @_;&lt;br /&gt;#   my @emit;&lt;br /&gt;#   while ($s) {&lt;br /&gt;#     while (@pending &amp;&amp; $cut-&gt;($pending[0], head($s))) {&lt;br /&gt;#       push @emit, shift @pending;&lt;br /&gt;#     }&lt;br /&gt;#     if (@emit) {&lt;br /&gt;#       return list_to_stream(@emit,&lt;br /&gt;#                             promise { cutsort($s, $cmp, $cut, @pending) });&lt;br /&gt;#     } else {&lt;br /&gt;#       insert(@pending, head($s), $cmp);&lt;br /&gt;#       $s = tail($s);&lt;br /&gt;#     }&lt;br /&gt;#   }&lt;br /&gt;#   return list_to_stream(@pending, undef);&lt;br /&gt;# }&lt;br /&gt;def cutsort(s, _cmp, cut, _pending=[]):&lt;br /&gt;    pending = _pending[:]&lt;br /&gt;    emit = []&lt;br /&gt;    while s:&lt;br /&gt;        while pending and cut(pending[0],head(s)):&lt;br /&gt;            emit.append(pending.pop(0))&lt;br /&gt;        if emit:&lt;br /&gt;            return list_to_stream(emit,&lt;br /&gt;                                  promise(lambda: cutsort(s, _cmp, cut, pending)))&lt;br /&gt;        else:&lt;br /&gt;            insert(pending, head(s), _cmp)&lt;br /&gt;            s = tail(s)&lt;br /&gt;&lt;br /&gt;    return list_to_stream(pending+[None])&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# my $sorted =&lt;br /&gt;#   cutsort($regex3,&lt;br /&gt;#           sub { $_[0] cmp $_[1] }, # comparator&lt;br /&gt;#           \&amp;cut_bylen              # cutting function&lt;br /&gt;#           );&lt;br /&gt;sorted = cutsort(regex3,&lt;br /&gt;                 lambda x,y: cmp(x,y),&lt;br /&gt;                 cut_bylen,&lt;br /&gt;                 )&lt;br /&gt;&lt;br /&gt;# sub insert (\@$$) {&lt;br /&gt;#   my ($a, $e, $cmp) = @_;&lt;br /&gt;#   my ($lo, $hi) = (0, scalar(@$a));&lt;br /&gt;#   while ($lo &lt; $hi) {&lt;br /&gt;#     my $med = int(($lo + $hi) / 2);&lt;br /&gt;#     my $d = $cmp-&gt;($a-&gt;[$med], $e);&lt;br /&gt;#     if ($d &lt;= 0) {&lt;br /&gt;#       $lo = $med+1;&lt;br /&gt;#     } else {&lt;br /&gt;#       $hi = $med;&lt;br /&gt;#     }&lt;br /&gt;#   }&lt;br /&gt;#   splice(@$a, $lo, 0, $e);&lt;br /&gt;# }&lt;br /&gt;def insert(a, e, _cmp):&lt;br /&gt;    lo, hi = 0, len(a)&lt;br /&gt;    while lo &lt; hi:&lt;br /&gt;        med = int((lo+hi)/2)&lt;br /&gt;        d = _cmp(a[med],e)&lt;br /&gt;        if d &lt;= 0:&lt;br /&gt;            lo = med+1&lt;br /&gt;        else:&lt;br /&gt;            hi = med&lt;br /&gt;&lt;br /&gt;    splice(a, lo, 0, e)&lt;br /&gt;##&lt;br /&gt;## above not tested &lt;br /&gt;##&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub _devino {&lt;br /&gt;#   my $f = shift;&lt;br /&gt;#   my ($dev, $ino) = stat($f);&lt;br /&gt;#   return unless defined $dev;&lt;br /&gt;#   "$dev;$ino";&lt;br /&gt;# }&lt;br /&gt;def _devino(f):&lt;br /&gt;    stat_tuple = os.state(f)&lt;br /&gt;    dev, ino = stat_tuple.st_dev, stat_tuple.st_ino&lt;br /&gt;    if not dev:&lt;br /&gt;        return None&lt;br /&gt;    return "%s;%s" % (dev, ino)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;##&lt;br /&gt;## ugg...  sorry got lazy again and skipped the rest of this section&lt;br /&gt;##&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;### 6.6 The Newton-Raphson Method&lt;br /&gt;&lt;br /&gt;# sub sqrt2 {&lt;br /&gt;#   my $g = 2; # Initial guess&lt;br /&gt;#   until (close_enough($g*$g, 2)) {&lt;br /&gt;#     $g = ($g*$g + 2) / (2*$g);&lt;br /&gt;#   }&lt;br /&gt;#   $g;&lt;br /&gt;# }&lt;br /&gt;def sqrt2():&lt;br /&gt;    g = 2&lt;br /&gt;    while not close_enough(g*g, 2):&lt;br /&gt;        g = float(g*g + 2) / (2*g)&lt;br /&gt;&lt;br /&gt;    return g&lt;br /&gt;&lt;br /&gt;# sub close_enough {&lt;br /&gt;#   my ($a, $b) = @_;&lt;br /&gt;#   return abs($a - $b) &lt; 1e-12;&lt;br /&gt;# }&lt;br /&gt;def close_enough(a,b):&lt;br /&gt;    return abs(a-b) &lt; 1e-12&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub sqrtn {&lt;br /&gt;#   my $n = shift;&lt;br /&gt;#   my $g = $n;   # Initial guess&lt;br /&gt;#   until (close_enough($g*$g, $n)) {&lt;br /&gt;#     $g = ($g*$g + $n) / (2*$g);&lt;br /&gt;#   }&lt;br /&gt;#   $g;&lt;br /&gt;# }&lt;br /&gt;def sqrtn(n):&lt;br /&gt;    g = n&lt;br /&gt;    while not close_enough(g*g, n):&lt;br /&gt;        g = float(g*g + n) / (2*g)&lt;br /&gt;&lt;br /&gt;    return g&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# use Stream 'iterate_function';&lt;br /&gt;# sub sqrt_stream {&lt;br /&gt;#   my $n = shift;&lt;br /&gt;#   iterate_function (sub { my $g = shift;&lt;br /&gt;#                          ($g*$g + $n) / (2*$g);&lt;br /&gt;#                         },&lt;br /&gt;#                     $n);&lt;br /&gt;# }&lt;br /&gt;# 1;&lt;br /&gt;def sqrt_stream(n):&lt;br /&gt;    return iterate_function(lambda g: float(g*g + n)/(2*g),&lt;br /&gt;                            n)&lt;br /&gt;&lt;br /&gt;# sub iterate_function {&lt;br /&gt;#   my ($f, $x) = @_;&lt;br /&gt;#   my $s;&lt;br /&gt;#   $s = node($x, promise { &amp;transform($f, $s) });&lt;br /&gt;# }&lt;br /&gt;def iterate_function(f, x):&lt;br /&gt;    s = node(x, promise(lambda: transform(f, s)))&lt;br /&gt;    return s&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub slope {&lt;br /&gt;#   my ($f, $x) = @_;&lt;br /&gt;#   my $e = 0.00000095367431640625;&lt;br /&gt;#   ($f-&gt;($x+$e) - $f-&gt;($x-$e)) / (2*$e);&lt;br /&gt;# }&lt;br /&gt;def slope(f, x):&lt;br /&gt;    e = 0.00000095367431640625&lt;br /&gt;    return (f(x+e) - f(x-e)) / (2*e)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# # Return a stream of numbers $x that make $f-&gt;($x) close to 0&lt;br /&gt;# sub solve {&lt;br /&gt;#   my $f = shift;&lt;br /&gt;#   my $guess = shift || 1;&lt;br /&gt;#   iterate_function(sub { my $g = shift;&lt;br /&gt;#                          $g - $f-&gt;($g)/slope($f, $g);&lt;br /&gt;#                        },&lt;br /&gt;#                    $guess);&lt;br /&gt;# }&lt;br /&gt;def solve(f, guess=1):&lt;br /&gt;    return iterate_function(lambda g: g - f(g)/slope(f,g),&lt;br /&gt;                            guess)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# use Math::BigFloat;&lt;br /&gt;# my $sqrt2 = solve(sub { $_[0] * $_[0] - 2 },&lt;br /&gt;#                 Math::BigFloat-&gt;new(2));&lt;br /&gt;# if want to use Decimal then need to retrofit a few functions:&lt;br /&gt;import decimal&lt;br /&gt;decimal.getcontext().prec = 64&lt;br /&gt;def slope(f, x):&lt;br /&gt;    x = decimal.Decimal(x)&lt;br /&gt;    e = decimal.Decimal("0.00000095367431640625")&lt;br /&gt;    return (f(x+e) - f(x-e)) / (2*e)&lt;br /&gt;def solve(f, guess=1):&lt;br /&gt;    return iterate_function(lambda g: g - f(g)/slope(f,g),&lt;br /&gt;                            decimal.Decimal(guess))&lt;br /&gt;sqrt2 = solve(lambda x: x*x - decimal.Decimal(2),&lt;br /&gt;              decimal.Decimal(2))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub cut_loops {&lt;br /&gt;#   my $s = shift;&lt;br /&gt;#   return unless $s;&lt;br /&gt;#   my @previous_values = @_;&lt;br /&gt;#   for (@previous_values) {&lt;br /&gt;#     if (head($s) == $_ ) {&lt;br /&gt;#       return;&lt;br /&gt;#     }&lt;br /&gt;#   }&lt;br /&gt;#   node(head($s),&lt;br /&gt;#        promise { cut_loops(tail($s), head($s), @previous_values) });&lt;br /&gt;# }&lt;br /&gt;def cut_loops(s, *args):&lt;br /&gt;    if s == None:&lt;br /&gt;        return None&lt;br /&gt;    previous_values = list(args)&lt;br /&gt;    for v in previous_values:&lt;br /&gt;        if head(s) == v:&lt;br /&gt;            return None&lt;br /&gt;    return node(head(s),&lt;br /&gt;                promise(lambda: cut_loops(tail(s), *([head(s)] + previous_values))))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub cut_loops {&lt;br /&gt;#   my ($tortoise, $hare) = @_;&lt;br /&gt;#   return unless $tortoise;&lt;br /&gt;#   # The hare and tortoise start at the same place&lt;br /&gt;#   $hare = $tortoise unless defined $hare;&lt;br /&gt;#   # The hare moves two steps every time the tortoise moves one&lt;br /&gt;#   $hare = tail(tail($hare));&lt;br /&gt;#   # If the hare and the tortoise are in the same place, cut the loop&lt;br /&gt;#   return if head($tortoise) == head($hare);&lt;br /&gt;#   return node(head($tortoise),&lt;br /&gt;#               promise { cut_loops(tail($tortoise), $hare) });&lt;br /&gt;# }&lt;br /&gt;def cut_loops(tortoise, hare=None):&lt;br /&gt;    if tortoise == None:&lt;br /&gt;        return None&lt;br /&gt;    # The hare and tortoise start at the same place&lt;br /&gt;    if hare == None:&lt;br /&gt;        hare = tortoise&lt;br /&gt;    # The hare moves two steps every time the tortoise moves one&lt;br /&gt;    hare = tail(tail(hare))&lt;br /&gt;    # If the hare and the tortoise are in the same place, cut the loop&lt;br /&gt;    if head(tortoise) == head(hare):&lt;br /&gt;        return None&lt;br /&gt;    return node(head(tortoise),&lt;br /&gt;                promise(lambda: cut_loops(tail(tortoise), hare)))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub cut_loops2 {&lt;br /&gt;#   my ($tortoise, $hare, $n) = @_;&lt;br /&gt;#   return unless $tortoise;&lt;br /&gt;#   $hare = $tortoise unless defined $hare;&lt;br /&gt;#   $hare = tail(tail($hare));&lt;br /&gt;#   return if head($tortoise) == head($hare)&lt;br /&gt;#             &amp;&amp; $n++;&lt;br /&gt;#   return node(head($tortoise),&lt;br /&gt;#               promise { cut_loops(tail($tortoise), $hare, $n) });&lt;br /&gt;# }&lt;br /&gt;def cut_loops2(tortoise, hare=None, n=0):&lt;br /&gt;    if tortoise == None:&lt;br /&gt;        return None&lt;br /&gt;    if hare == None:&lt;br /&gt;        hare = tortoise&lt;br /&gt;    hare = tail(tail(hare))&lt;br /&gt;    if head(tortoise) == head(hare):&lt;br /&gt;        if n &gt; 0:&lt;br /&gt;            return None&lt;br /&gt;        else:&lt;br /&gt;            n += 1&lt;br /&gt;    return node(head(tortoise),&lt;br /&gt;                promise(lambda: cut_loops2(tail(tortoise), hare, n)))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub owed {&lt;br /&gt;#   my ($P, $N, $pmt, $i) = @_;&lt;br /&gt;#   my $payment_factor = 0;&lt;br /&gt;#   for (0 .. $N-1) {&lt;br /&gt;#     $payment_factor += (1+$i) ** $_;&lt;br /&gt;#   }&lt;br /&gt;#   return $P * (1+$i)**$N - $pmt * $payment_factor;&lt;br /&gt;# }&lt;br /&gt;def owed(P,N,pmt,i):&lt;br /&gt;    payment_factor = 0&lt;br /&gt;    for x in range(0,N):&lt;br /&gt;        payment_factor += (1+i) ** x&lt;br /&gt;    return P * (1+i)**N - pmt * payment_factor&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub owed {&lt;br /&gt;#   my ($P, $N, $pmt, $i) = @_;&lt;br /&gt;#   return $P * (1+$i)**$N - $pmt * ((1+$i)**$N - 1) / $i;&lt;br /&gt;# }&lt;br /&gt;def owed(P,N,pmt,i):&lt;br /&gt;    return P * (1+i)**N - pmt * ((1+i)**N - 1) / i&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub owed_after_n_months {&lt;br /&gt;#   my $N = shift;&lt;br /&gt;#   owed(100_000, $N, 1_000, 0.005);&lt;br /&gt;# }&lt;br /&gt;# my $stream = cut_loops(solve(\&amp;owed_after_n_months));&lt;br /&gt;# my $n;&lt;br /&gt;# $n = drop($stream) while $stream;&lt;br /&gt;# print "You will be paid off in only $n months!\n";&lt;br /&gt;def owed_after_n_months(N):&lt;br /&gt;    return owed(100000, N, 1000, 0.005)&lt;br /&gt;stream = cut_loops(solve(owed_after_n_months))&lt;br /&gt;n = head(stream)&lt;br /&gt;while stream:&lt;br /&gt;    n = head(stream)&lt;br /&gt;    stream = tail(stream)&lt;br /&gt;print "You will be paid off in only %s months!" % n&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub affordable_mortgage {&lt;br /&gt;#   my $mortgage = shift;&lt;br /&gt;#   owed($mortgage, 30, 15_600, 0.0675);&lt;br /&gt;# }&lt;br /&gt;# my $stream = cut_loops(solve(\&amp;affordable_mortgage));&lt;br /&gt;# my $n;&lt;br /&gt;# $n = drop($stream) while $stream;&lt;br /&gt;# print "You can afford a \$$n mortgage.\n";&lt;br /&gt;def affordable_mortgage(mortgage):&lt;br /&gt;    return owed(mortgage, 30, 15600, 0.0675)&lt;br /&gt;stream = cut_loops(solve(affordable_mortgage))&lt;br /&gt;n = head(stream)&lt;br /&gt;while stream:&lt;br /&gt;    n = head(stream)&lt;br /&gt;    stream = tail(stream)&lt;br /&gt;print "You can afford a $%s mortgage." % n&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;### 6.7 Power Series&lt;br /&gt;&lt;br /&gt;# # Approximate sin(x) using the first n terms of the power series&lt;br /&gt;# sub approx_sin {&lt;br /&gt;#   my $n = shift;&lt;br /&gt;#   my $x = shift;&lt;br /&gt;#   my ($denom, $c, $num, $total) = (1, 1, $x, 0);&lt;br /&gt;#   while ($n--) {&lt;br /&gt;#     $total += $num / $denom;&lt;br /&gt;#     $num *= $x*$x * -1;&lt;br /&gt;#     $denom *= ($c+1) * ($c+2);&lt;br /&gt;#     $c += 2;&lt;br /&gt;#   }&lt;br /&gt;#   $total;&lt;br /&gt;# }&lt;br /&gt;# 1;&lt;br /&gt;def approx_sin(n, x):&lt;br /&gt;    denom, c, num, total = 1,1,x,0&lt;br /&gt;    while n:&lt;br /&gt;        n -= 1&lt;br /&gt;        total += num / float(denom)&lt;br /&gt;        num *= x*x * - 1&lt;br /&gt;        denom *= (c+1) * (c+2)&lt;br /&gt;        c += 2&lt;br /&gt;    return total&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# package PowSeries;&lt;br /&gt;# use base 'Exporter';&lt;br /&gt;# @EXPORT_OK = qw(add2 mul2 partial_sums powers_of term_values&lt;br /&gt;#                 evaluate derivative multiply recip divide&lt;br /&gt;#                 $sin $cos $exp $log_ $tan);&lt;br /&gt;# use Stream ':all';&lt;br /&gt;# sub tabulate {&lt;br /&gt;#   my $f = shift;&lt;br /&gt;#   &amp;transform($f, upfrom(0));&lt;br /&gt;# }&lt;br /&gt;def tabulate(f):&lt;br /&gt;    return transform(f, upfrom(0))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# my @fact = (1);&lt;br /&gt;# sub factorial {&lt;br /&gt;#   my $n = shift;&lt;br /&gt;#   return $fact[$n] if defined $fact[$n];&lt;br /&gt;#   $fact[$n] = $n * factorial($n-1);&lt;br /&gt;# }&lt;br /&gt;&lt;br /&gt;# $sin = tabulate(sub { my $N = shift;&lt;br /&gt;#                       return 0 if $N % 2 == 0;&lt;br /&gt;#                       my $sign = int($N/2) % 2 ? -1 : 1;&lt;br /&gt;#                       $sign/factorial($N)&lt;br /&gt;#                     });&lt;br /&gt;&lt;br /&gt;# $cos = tabulate(sub { my $N = shift;&lt;br /&gt;#                       return 0 if $N % 2 != 0;&lt;br /&gt;#    my $sign = int($N/2) % 2 ? -1 : 1;&lt;br /&gt;#    $sign/factorial($N)&lt;br /&gt;# });&lt;br /&gt;fact = {0:1}&lt;br /&gt;def factorial(n):&lt;br /&gt;    if fact.get(n):&lt;br /&gt;        return fact[n]&lt;br /&gt;    fact[n] = n * factorial(n-1)&lt;br /&gt;    return fact[n]&lt;br /&gt;&lt;br /&gt;def sin_lambda(N):&lt;br /&gt;    if N % 2 == 0:&lt;br /&gt;        return 0&lt;br /&gt;    sign = 1&lt;br /&gt;    if (N/2) % 2 != 0:&lt;br /&gt;        -1&lt;br /&gt;    return sign / float(factorial(N))&lt;br /&gt;sin = tabulate(sin_lambda)&lt;br /&gt;&lt;br /&gt;def cos_lambda(N):&lt;br /&gt;    if N % 2 != 0:&lt;br /&gt;        return 0&lt;br /&gt;    sign = 1&lt;br /&gt;    if (N/2) % 2 != 0:&lt;br /&gt;        -1&lt;br /&gt;    return sign / float(factorial(N))&lt;br /&gt;cos = tabulate(cos_lambda)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub add2 {&lt;br /&gt;#   my ($s, $t) = @_;&lt;br /&gt;#   return $s unless $t;&lt;br /&gt;#   return $t unless $s;&lt;br /&gt;#   node(head($s) + head($t),&lt;br /&gt;#        promise { add2(tail($s), tail($t)) });&lt;br /&gt;# }&lt;br /&gt;def add2(s,t):&lt;br /&gt;    if not t:&lt;br /&gt;        return s&lt;br /&gt;    if not s:&lt;br /&gt;        return t&lt;br /&gt;    return node(head(s) + head(t),&lt;br /&gt;                promise(lambda: add2(tail(s),tail(t))))&lt;br /&gt;&lt;br /&gt;# sub mul2 {&lt;br /&gt;#   my ($s, $t) = @_;&lt;br /&gt;#   return unless $s &amp;&amp; $t;&lt;br /&gt;#   node(head($s) * head($t),&lt;br /&gt;#        promise { mul2(tail($s), tail($t)) });&lt;br /&gt;# }&lt;br /&gt;def mul2(s,t):&lt;br /&gt;    if not (s and t):&lt;br /&gt;        return None&lt;br /&gt;    return node(head(s)*head(t),&lt;br /&gt;                promise(lambda: mul2(tail(s),tail(t))))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub partial_sums {&lt;br /&gt;#   my $s = shift;&lt;br /&gt;#   my $r;&lt;br /&gt;#   $r = node(head($s), promise { add2($r, tail($s)) });&lt;br /&gt;# }&lt;br /&gt;def partial_sums(s):&lt;br /&gt;    r = node(head(s), promise(lambda: add2(r, tail(s))))&lt;br /&gt;    return r&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub powers_of {&lt;br /&gt;#   my $x = shift;&lt;br /&gt;#   iterate_function(sub {$_[0] * $x}, 1);&lt;br /&gt;# }&lt;br /&gt;def powers_of(x):&lt;br /&gt;    return iterate_function(lambda i: i*x, 1)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub term_values {&lt;br /&gt;#   my ($s, $x) = @_;&lt;br /&gt;#   mul2($s, powers_of($x));&lt;br /&gt;# }&lt;br /&gt;def term_values(s,x):&lt;br /&gt;    return mul2(s, powers_of(x))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub evaluate {&lt;br /&gt;#   my ($s, $x) = @_;&lt;br /&gt;#   partial_sums(term_values($s, $x));&lt;br /&gt;# }&lt;br /&gt;def evaluate(s,x):&lt;br /&gt;    return partial_sums(term_values(s,x))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# my $pi = 3.1415926535897932;&lt;br /&gt;# show(evaluate($cos, $pi/6), 20);&lt;br /&gt;pi = 3.1415926535897932&lt;br /&gt;show(evaluate(cos, pi/6.0), 20)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;### grrr.....  each of the pieces i test&lt;br /&gt;### above seems to be behaving correctly&lt;br /&gt;### but for some reason the cos(pi/6) example&lt;br /&gt;### is not coming out correctly.  no idea :(&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# # Get the n'th term from a stream&lt;br /&gt;# sub nth {&lt;br /&gt;#   my $s = shift;&lt;br /&gt;#   my $n = shift;&lt;br /&gt;#   return $n == 0 ? head($s) : nth(tail($s), $n-1);&lt;br /&gt;# }&lt;br /&gt;# # Calculate the approximate cosine of x&lt;br /&gt;# sub cosine {&lt;br /&gt;#   my $x = shift;&lt;br /&gt;#   nth(evaluate($cos, $x), 20);&lt;br /&gt;# }&lt;br /&gt;def nth(s,n):&lt;br /&gt;    if n == 0:&lt;br /&gt;        return head(s)&lt;br /&gt;    else:&lt;br /&gt;        return nth(tail(s), n-1)&lt;br /&gt;&lt;br /&gt;def cosine(x):&lt;br /&gt;    return nth(evaluate(cos, x), 20)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub is_zero_when_x_is_pi {&lt;br /&gt;#   my $x = shift;&lt;br /&gt;#   my $c = cosine($x/6);&lt;br /&gt;#   $c * $c - 3/4;&lt;br /&gt;# }&lt;br /&gt;# show(solve(\&amp;is_zero_when_x_is_pi), 20);&lt;br /&gt;def is_zero_when_x_is_pi(x):&lt;br /&gt;    c = cosine(x/6.0)&lt;br /&gt;    return c * c - 3/4.0&lt;br /&gt;&lt;br /&gt;show(solve(is_zero_when_x_is_pi), 20)&lt;br /&gt;&lt;br /&gt;# sub derivative {&lt;br /&gt;#   my $s = shift;&lt;br /&gt;#   mul2(upfrom(1), tail($s));&lt;br /&gt;# }&lt;br /&gt;def derivative(s):&lt;br /&gt;    return mul2(upfrom(1), tail(s))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# show(derivative($sin), 20);&lt;br /&gt;show(derivative(sin), 20)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# exp = tabulate(sub { my $N = shift; 1/factorial($N) });&lt;br /&gt;exp = tabulate(lambda N: 1.0/factorial(N))&lt;br /&gt;&lt;br /&gt;# $log_ = tabulate(sub { my $N = shift;&lt;br /&gt;#                        $N==0 ? 0 : (-1)**$N/-$N });&lt;br /&gt;log_ = tabulate(lambda N: N!=0 and (-1)**N/-N or 0)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub multiply {&lt;br /&gt;#   my ($S, $T) = @_;&lt;br /&gt;#   my ($s, $t) = (head($S), head($T));&lt;br /&gt;#   node($s*$t,&lt;br /&gt;#        promise { add2(scale(tail($T), $s),&lt;br /&gt;#                  add2(scale(tail($S), $t),&lt;br /&gt;#                       node(0,&lt;br /&gt;#                        promise {multiply(tail($S), tail($T))}),&lt;br /&gt;#                      ))&lt;br /&gt;#                }&lt;br /&gt;#        );&lt;br /&gt;# }&lt;br /&gt;def multiply(S,T):&lt;br /&gt;    s, t = head(S), head(T)&lt;br /&gt;    return node(s*t,&lt;br /&gt;                promise(add2(scale(tail(T),s),&lt;br /&gt;                             add2(scale(tail(S),t),&lt;br /&gt;                                  node(0,&lt;br /&gt;                                       promise(multiply(tail(S), tail(T))))))))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub scale {&lt;br /&gt;#   my ($s, $c) = @_;&lt;br /&gt;#   return    if $c == 0;&lt;br /&gt;#   return $s if $c == 1;&lt;br /&gt;#   transform { $_[0]*$c } $s;&lt;br /&gt;# }&lt;br /&gt;def scale(s, c):&lt;br /&gt;    if c == 0:&lt;br /&gt;        return None&lt;br /&gt;    if c == 1:&lt;br /&gt;        return s&lt;br /&gt;    return transform(lambda i: i*c, s)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# my $one = add2(multiply($cos, $cos), multiply($sin, $sin));&lt;br /&gt;# show($one, 20);&lt;br /&gt;one = add2(multiply(cos, cos), multiply(sin, sin))&lt;br /&gt;show(one, 20)&lt;br /&gt;### unfortunately i get:&lt;br /&gt;### OverflowError: long int too large to convert to float&lt;br /&gt;### perhaps come back later and figure this out&lt;br /&gt;&lt;br /&gt;# sub sum {&lt;br /&gt;#   my @s = grep $_, @_;&lt;br /&gt;#   my $total = 0;&lt;br /&gt;#   $total += head($_ ) for @s;&lt;br /&gt;#   node($total,&lt;br /&gt;#        promise { sum(map tail($_ ), @s) }&lt;br /&gt;#       );&lt;br /&gt;# }&lt;br /&gt;def sum_(_s):&lt;br /&gt;    s = [s_ for s_ in _s if s_]&lt;br /&gt;    total = 0&lt;br /&gt;    for x in s:&lt;br /&gt;        total += head(x)&lt;br /&gt;    return node(total, promise(lambda: sum_([tail(x) for x in s])))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub multiply {&lt;br /&gt;#   my ($S, $T) = @_;&lt;br /&gt;#   my ($s, $t) = (head($S), head($T));&lt;br /&gt;#   node($s*$t,&lt;br /&gt;#   promise { sum(scale(tail($T), $s),&lt;br /&gt;#                 scale(tail($S), $t),&lt;br /&gt;#                 node(0,&lt;br /&gt;#                   promise {multiply(tail($S), tail($T))}),&lt;br /&gt;#                )&lt;br /&gt;#           }&lt;br /&gt;#   );&lt;br /&gt;# }&lt;br /&gt;def multiply(S,T):&lt;br /&gt;    s,t = head(S), head(T)&lt;br /&gt;    return node(s*t,&lt;br /&gt;                promise(lambda: sum_(scale(tail(T),s), scale(tail(S),t), node(0, promise(lambda: multiply(tail(S), tail(T)))))))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# # Works only if head($s) = 1&lt;br /&gt;# sub recip {&lt;br /&gt;#   my ($s) = shift;&lt;br /&gt;#   my $r;&lt;br /&gt;#   $r = node(1,&lt;br /&gt;#             promise { scale(multiply($r, tail($s)), -1) });&lt;br /&gt;# }&lt;br /&gt; def recip(s):&lt;br /&gt;    r = node(1,&lt;br /&gt;             promise(lambda: scale(multiply(r,tail(s)), -1)))&lt;br /&gt;    return r&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# # Works only if head($t) = 1&lt;br /&gt;# sub divide {&lt;br /&gt;#   my ($s, $t) = @_;&lt;br /&gt;#   multiply($s, recip($t));&lt;br /&gt;# }&lt;br /&gt;# $tan = divide($sin, $cos);&lt;br /&gt;# show($tan, 10);&lt;br /&gt;def divide(s,t):&lt;br /&gt;    return multiply(s, recip(t))&lt;br /&gt;tan = divide(sin, cos)&lt;br /&gt;show(tan, 10)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# my @fact = (Math::BigRat-&gt;new(1));&lt;br /&gt;# sub factorial {&lt;br /&gt;#   my $n = shift;&lt;br /&gt;#   return $fact[$n] if defined $fact[$n];&lt;br /&gt;#   $fact[$n] = $n * factorial($n-1);&lt;br /&gt;# }&lt;br /&gt;# python 2.6 has "fractions" library&lt;br /&gt;fact = [fraction.Fraction(1,1)]&lt;br /&gt;def factorial(n):&lt;br /&gt;    if fact.get(n):&lt;br /&gt;        return fact[n]&lt;br /&gt;    fact[n] = n * factorial(n-1)&lt;br /&gt;    return fact[n]&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-4506294623902791492?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/4506294623902791492/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=4506294623902791492' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/4506294623902791492'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/4506294623902791492'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2009/03/higher-order-perl-python-style-chapter_25.html' title='Higher Order Perl (Python Style) : Chapter 6 - Infinite Streams'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-836193631742748110</id><published>2009-03-22T15:38:00.000-07:00</published><updated>2009-03-22T15:40:33.625-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='99 problems'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>99 problems - python - 58</title><content type='html'>Generate-and-test paradigm&lt;br /&gt;&lt;br /&gt;Apply the generate-and-test paradigm to construct all symmetric, completely balanced binary trees with a given number of nodes.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;# Example:&lt;br /&gt;&lt;br /&gt;# * sym-cbal-trees(5,Ts).&lt;br /&gt;# Ts = [t(x, t(x, nil, t(x, nil, nil)), t(x, t(x, nil, nil), nil)), t(x, t(x, t(x, nil, nil), nil), t(x, nil, t(x, nil, nil)))] &lt;br /&gt;def symmetric_completely_balanced_trees(node_count):&lt;br /&gt;    return [tree for tree in completely_balanced_tree(node_count) \&lt;br /&gt;                if symmetric_binary_tree(tree)]&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-836193631742748110?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/836193631742748110/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=836193631742748110' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/836193631742748110'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/836193631742748110'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2009/03/99-problems-python-58.html' title='99 problems - python - 58'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-7362298417202541401</id><published>2009-03-19T21:20:00.000-07:00</published><updated>2009-03-19T21:24:32.815-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pushups'/><category scheme='http://www.blogger.com/atom/ns#' term='fitness'/><title type='text'>100 Pushups: "week 6" milestone - 70</title><content type='html'>Holy crap.  I finally got to 70.  So the adventure continues on the hilariously optimistic "6 week" program to do &lt;a href="http://hundredpushups.com/"&gt;100 pushups&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The big question now is whether I can get to 100 before a year has elapsed.  I think there is a chance.  But honestly I'll just be happy to get there.  And to think I was going to skip tonight since I felt a little under the weather.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-7362298417202541401?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/7362298417202541401/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=7362298417202541401' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/7362298417202541401'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/7362298417202541401'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2009/03/100-pushups-week-6-milestone-70.html' title='100 Pushups: &quot;week 6&quot; milestone - 70'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-599367446988922028</id><published>2009-03-09T19:19:00.000-07:00</published><updated>2009-03-09T19:23:22.720-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='functional programming'/><category scheme='http://www.blogger.com/atom/ns#' term='higher order perl'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='hop'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><title type='text'>Higher Order Perl (Python Style) : Chapter 5 - From Recursion To Iterators</title><content type='html'>&lt;br&gt;&lt;br&gt;&lt;br /&gt;&lt;a href="http://dustbunnylair.blogspot.com/2009/01/higher-order-perl-python-style-toc.html"&gt;TOC&lt;/a&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;### Chapter 5 - From Recursion To Iterators&lt;br /&gt;&lt;br /&gt;### 5.1 The Partition Problem Revisited&lt;br /&gt;&lt;br /&gt;# sub find_share {&lt;br /&gt;#   my ($target, $treasures) = @_;&lt;br /&gt;#   return [] if $target == 0;&lt;br /&gt;#   return    if $target &lt; 0 || @$treasures == 0;&lt;br /&gt;#   my ($first, @rest) = @$treasures;&lt;br /&gt;#   my $solution = find_share($target-$first, \@rest);&lt;br /&gt;#   return [$first, @$solution] if $solution;&lt;br /&gt;#   return         find_share($target       , \@rest);&lt;br /&gt;# }&lt;br /&gt;def find_share(target, treasures):&lt;br /&gt;    if target == 0:&lt;br /&gt;        return []&lt;br /&gt;    if target &lt; 0 or len(treasures) == 0:&lt;br /&gt;        return&lt;br /&gt;    first, rest = treasures[0], treasures[1:]&lt;br /&gt;    solution = find_share(target-first, rest)&lt;br /&gt;    if solution != None:&lt;br /&gt;        return [first] + solution&lt;br /&gt;    return find_share(target, rest)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub partition {&lt;br /&gt;#   my ($target, $treasures) = @_;&lt;br /&gt;#   return [] if $target == 0;&lt;br /&gt;#   return () if $target &lt; 0 || @$treasures == 0;&lt;br /&gt;#   my ($first, @rest) = @$treasures;&lt;br /&gt;#   my @solutions = partition($target-$first, \@rest);&lt;br /&gt;#   return ((map {[$first, @$_]} @solutions),&lt;br /&gt;#          partition($target, \@rest));&lt;br /&gt;# }&lt;br /&gt;def partition(target, treasures):&lt;br /&gt;    if target == 0:&lt;br /&gt;        return [[]]&lt;br /&gt;    if target &lt; 0 or len(treasures) == 0:&lt;br /&gt;        return []&lt;br /&gt;    first, rest = treasures[0], treasures[1:]&lt;br /&gt;    solutions = partition(target-first, rest)&lt;br /&gt;&lt;br /&gt;    return [[first] + solution for solution in solutions] + partition(target, rest)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub make_partitioner {&lt;br /&gt;#   my ($n, $treasures) = @_;&lt;br /&gt;#   my @todo = [$n, $treasures, []];&lt;br /&gt;#   sub {&lt;br /&gt;#     while (@todo) {&lt;br /&gt;#       my $cur = pop @todo;&lt;br /&gt;#       my ($target, $pool, $share) = @$cur;&lt;br /&gt;#       if ($target == 0) { return $share }&lt;br /&gt;&lt;br /&gt;#       next if $target &lt; 0 || @$pool == 0;&lt;br /&gt;&lt;br /&gt;#       my ($first, @rest) = @$pool;&lt;br /&gt;#       push @todo, [$target-$first, \@rest, [@$share, $first]],&lt;br /&gt;#                   [$target       , \@rest,   $share         ];&lt;br /&gt;#     }&lt;br /&gt;#     return undef;&lt;br /&gt;#   } # end of anonymous iterator function&lt;br /&gt;# } # end of make_partitioner&lt;br /&gt;def make_partitioner(n, treasures):&lt;br /&gt;    todo = [[n, treasures, []]]&lt;br /&gt;&lt;br /&gt;    while todo:&lt;br /&gt;        target, pool, share = todo.pop()&lt;br /&gt;        if target == 0:&lt;br /&gt;            yield share&lt;br /&gt;            continue&lt;br /&gt;&lt;br /&gt;        if target &lt; 0 or len(pool) == 0:&lt;br /&gt;            continue&lt;br /&gt;&lt;br /&gt;        first, rest = pool[0], pool[1:]&lt;br /&gt;        todo.append([target-first, rest, share+[first]])&lt;br /&gt;        todo.append([target,       rest, share])&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub make_partitioner {&lt;br /&gt;#   my ($n, $treasures) = @_;&lt;br /&gt;#   my @todo = [$n, $treasures, []];&lt;br /&gt;#   sub {&lt;br /&gt;#     while (@todo) {&lt;br /&gt;#       my $cur = pop @todo;&lt;br /&gt;#       my ($target, $pool, $share) = @$cur;&lt;br /&gt;#       if ($target == 0) { return $share }&lt;br /&gt;#       next if $target &lt; 0 || @$pool == 0;&lt;br /&gt;#       my ($first, @rest) = @$pool;&lt;br /&gt;#       push @todo, [$target, \@rest, $share ] if @rest;&lt;br /&gt;#       if ($target == $first) {&lt;br /&gt;#          return [@$share, $first];&lt;br /&gt;#       } elsif ($target &gt; $first &amp;&amp; @rest) {&lt;br /&gt;#          push @todo, [$target-$first, \@rest, [@$share, $first]],&lt;br /&gt;#       }&lt;br /&gt;#     }&lt;br /&gt;#     return undef;&lt;br /&gt;#   } # end of anonymous iterator function&lt;br /&gt;# } # end of make_partitioner&lt;br /&gt;def make_partitioner(n, treasures):&lt;br /&gt;    todo = [[n, treasures, []]]&lt;br /&gt;&lt;br /&gt;    while todo:&lt;br /&gt;        target, pool, share = todo.pop()&lt;br /&gt;        if target == 0:&lt;br /&gt;            yield share&lt;br /&gt;            continue&lt;br /&gt;&lt;br /&gt;        if target &lt; 0 or len(pool) == 0:&lt;br /&gt;            continue&lt;br /&gt;&lt;br /&gt;        first, rest = pool[0], pool[1:]&lt;br /&gt;        if rest:&lt;br /&gt;            todo.append([target, rest, share])&lt;br /&gt;        if target == first:&lt;br /&gt;            yield share+[first]&lt;br /&gt;        elif (target &gt; first and rest):&lt;br /&gt;            todo.append([target-first, rest, share+[first]])&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;### 5.2 How to Convert a Recursive Function to an Iterator&lt;br /&gt;&lt;br /&gt;# sub rec {&lt;br /&gt;#   my ($n, $k) = @_;&lt;br /&gt;#   print $k x $n, "\n";&lt;br /&gt;#   for (1 .. $n-1) {&lt;br /&gt;#     rec($n-$_, $_);&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def rec(n, k):&lt;br /&gt;    print str(k) * n&lt;br /&gt;    for i in range(1,n):&lt;br /&gt;        rec(n-i, i)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub partition {&lt;br /&gt;#   print "@_\n";&lt;br /&gt;#   my ($n, @parts) = @_;&lt;br /&gt;#   for (1 .. $n-1) {&lt;br /&gt;#     partition($n-$_, $_, @parts);&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def partition(n, parts):&lt;br /&gt;    print [n] +parts&lt;br /&gt;    for i in range(1,n):&lt;br /&gt;        partition(n-i, [i]+parts)&lt;br /&gt;&lt;br /&gt;# for (@words) { $seen{$_}++ }&lt;br /&gt;# @repeats = grep $seen{$_} &gt; 1, keys %seen;&lt;br /&gt;seen = {}&lt;br /&gt;for word in words:&lt;br /&gt;    seen.setdefault(word,[0])[0] += 1&lt;br /&gt;repeats = [word for word in seen if seen[word][0] &gt; 1]&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# for (@words) { $seen{lc $_}++ }&lt;br /&gt;# @repeats = grep $seen{$_} &gt; 1, keys %seen;&lt;br /&gt;seen = {}&lt;br /&gt;for word in words:&lt;br /&gt;    seen.setdefault(word.lower(),[0])[0] += 1&lt;br /&gt;repeats = [word for word in seen if seen[word][0] &gt; 1]&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub partition {&lt;br /&gt;#   print "@_\n" if decreasing_order(@_);&lt;br /&gt;#   my ($n, @parts) = @_;&lt;br /&gt;#   for (1 .. $n-1) {&lt;br /&gt;#     partition($n-$_, $_, @parts);&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def partition(n, parts):&lt;br /&gt;    if decreasing_order([n] +parts):&lt;br /&gt;        print [n] + parts&lt;br /&gt;    for i in range(1,n):&lt;br /&gt;        partition(n-i, [i]+parts)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub partition {&lt;br /&gt;#   print "@_\n";&lt;br /&gt;#   my ($largest, @rest) = @_;&lt;br /&gt;#   my $min = $rest[0] || 1;&lt;br /&gt;#   my $max = int($largest/2);&lt;br /&gt;#   for ($min .. $max) {&lt;br /&gt;#     partition($largest-$_, $_, @rest);&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def partition(largest, rest):&lt;br /&gt;    print [largest] + rest&lt;br /&gt;    min = (rest + [1])[0]&lt;br /&gt;    max = largest / 2&lt;br /&gt;    for i in range(min, max+1):&lt;br /&gt;        partition(largest-i, [i]+rest)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub make_partition {  &lt;br /&gt;#   my $n = shift;&lt;br /&gt;#   my @agenda = ([$n,            # $largest&lt;br /&gt;#                  [],            # \@rest&lt;br /&gt;#                  1,             # $min&lt;br /&gt;#                  int($n/2),     # $max&lt;br /&gt;#                 ]);&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     while (@agenda) {&lt;br /&gt;#       my $item = pop @agenda;&lt;br /&gt;#       my ($largest, $rest, $min, $max) = @$item;&lt;br /&gt;#       for ($min .. $max) {&lt;br /&gt;#         push @agenda, [$largest - $_,          # $largest&lt;br /&gt;#                        [$_, @$rest],           # \@rest&lt;br /&gt;#                        $_,                     # $min&lt;br /&gt;#                        int(($largest - $_)/2), # $max&lt;br /&gt;#                       ];&lt;br /&gt;#       }&lt;br /&gt;#       return [$largest, @$rest];&lt;br /&gt;#     }&lt;br /&gt;#     return;&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;def make_partition(n):&lt;br /&gt;    agenda = [(n,   # largest&lt;br /&gt;               [],  # rest&lt;br /&gt;               1,   # min&lt;br /&gt;               n/2) # max&lt;br /&gt;              ]&lt;br /&gt;&lt;br /&gt;    while agenda:&lt;br /&gt;        (largest, rest, min, max) = agenda.pop()&lt;br /&gt;        for i in range(min, max+1):&lt;br /&gt;            agenda.append((largest - i,    # largest&lt;br /&gt;                           [i]+rest,       # rest&lt;br /&gt;                           i,              # min&lt;br /&gt;                           (largest-i)/2   # max&lt;br /&gt;                           ))&lt;br /&gt;        yield [largest]+rest&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub partition {&lt;br /&gt;#   my ($largest, $rest, $min, $max) = @_;&lt;br /&gt;#   for ($min .. $max) {&lt;br /&gt;#     partition($largest-$_, [$_, @$rest], $_, int(($largest - $_)/2));&lt;br /&gt;#   }&lt;br /&gt;#   return [$largest, @$rest];&lt;br /&gt;# }&lt;br /&gt;def partition(largest, rest, min, max):&lt;br /&gt;    for i in range(min, max+1):&lt;br /&gt;        partition(largest-i, [i]+rest, i, (largest-i)/2)&lt;br /&gt;    return [largest] + rest&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub make_partition {&lt;br /&gt;#   my $n = shift;&lt;br /&gt;#   my @agenda = [$n];&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     while (@agenda) {&lt;br /&gt;#       my $item = pop @agenda;&lt;br /&gt;#       my ($largest, @rest) = @$item;&lt;br /&gt;#       my $min = $rest[0] || 1;&lt;br /&gt;#       my $max  = int($largest/2);&lt;br /&gt;#       for ($min .. $max) {&lt;br /&gt;#         push @agenda, [$largest-$_, $_, @rest];&lt;br /&gt;#       }&lt;br /&gt;#       return $item;&lt;br /&gt;#     }&lt;br /&gt;#     return;&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;def make_partition(n):&lt;br /&gt;    agenda = [[n,[]]]&lt;br /&gt;    while agenda:&lt;br /&gt;        largest, rest = agenda.pop()&lt;br /&gt;        min = (rest + [1])[0]&lt;br /&gt;        max = largest / 2&lt;br /&gt;        for i in range(min, max+1):&lt;br /&gt;            agenda.append([largest-i, [i]+rest])&lt;br /&gt;        yield [largest] + rest&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub make_partition {&lt;br /&gt;#   my $n = shift;&lt;br /&gt;#   my @agenda = [$n];&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     return unless @agenda;&lt;br /&gt;#     my $item = pop @agenda;&lt;br /&gt;#     my ($largest, @rest) = @$item;&lt;br /&gt;#     my $min = $rest[0] || 1;&lt;br /&gt;#     my $max  = int($largest/2);&lt;br /&gt;#     for ($min .. $max) {&lt;br /&gt;#       push @agenda, [$largest-$_, $_, @rest];&lt;br /&gt;#     }&lt;br /&gt;#     return $item;&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;### we *do* keep the while loop in the python version&lt;br /&gt;### and it looks just like the previous solution&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# # Compare two partitions for preferred order&lt;br /&gt;# sub partitions {&lt;br /&gt;#   for my $i (0 .. $#$a) {&lt;br /&gt;#     my $cmp = $b-&gt;[$i] &lt;=&gt; $a-&gt;[$i];&lt;br /&gt;#     return $cmp if $cmp;&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;# lists and tuples will compare naturally &lt;br /&gt;# in the correct way (unless i'm missing something here)&lt;br /&gt;def partitions(a,b):&lt;br /&gt;    return cmp(a,b)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub make_partition {&lt;br /&gt;#   my $n = shift;&lt;br /&gt;#   my @agenda = [$n];&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     return unless @agenda;&lt;br /&gt;#     my $item = pop @agenda;&lt;br /&gt;#     my ($largest, @rest) = @$item;&lt;br /&gt;#     my $min = $rest[0] || 1;&lt;br /&gt;#     my $max  = int($largest/2);&lt;br /&gt;#     for ($min .. $max) {&lt;br /&gt;#       push @agenda, [$largest-$_, $_, @rest];&lt;br /&gt;#     }&lt;br /&gt;#     @agenda = sort partitions @agenda;&lt;br /&gt;#     return $item;&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;def make_partition(n):&lt;br /&gt;    agenda = [[n,[]]]&lt;br /&gt;    while agenda:&lt;br /&gt;        largest, rest = agenda.pop()&lt;br /&gt;        min = (rest + [1])[0]&lt;br /&gt;        max = largest / 2&lt;br /&gt;        for i in range(min, max+1):&lt;br /&gt;            agenda.append([largest-i, [i]+rest])&lt;br /&gt;        agenda.sort()&lt;br /&gt;        yield [largest] + rest&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;### 5.3 A Generic Search Iterator&lt;br /&gt;&lt;br /&gt;# use Iterator_Utils 'Iterator';&lt;br /&gt;# sub make_dfs_search {&lt;br /&gt;#   my ($root, $children) = @_;&lt;br /&gt;#   my @agenda = $root;&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     return unless @agenda;&lt;br /&gt;#     my $node = pop @agenda;&lt;br /&gt;#     push @agenda, $children-&gt;($node);&lt;br /&gt;#     return $node;&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;def make_dfs_search(root, children):&lt;br /&gt;    agenda = [root]&lt;br /&gt;    &lt;br /&gt;    while agenda:&lt;br /&gt;        node = agenda.pop()&lt;br /&gt;        agenda.extend(children(node))&lt;br /&gt;        yield node&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub make_partition {&lt;br /&gt;#   my $n = shift;&lt;br /&gt;#   my $root = [$n];&lt;br /&gt;#   my $children = sub {&lt;br /&gt;#     my ($largest, @rest) = @{shift()};&lt;br /&gt;#     my $min = $rest[0] || 1;&lt;br /&gt;#     my $max  = int($largest/2);&lt;br /&gt;#     map [$largest-$_, $_, @rest], ($min .. $max);&lt;br /&gt;#   };&lt;br /&gt;#   make_dfs_search($root, $children);&lt;br /&gt;# }&lt;br /&gt;def make_partition(n):&lt;br /&gt;    root = [n]&lt;br /&gt;    def children(largest, rest):&lt;br /&gt;        min = (rest + [1])[0]&lt;br /&gt;        max = largest / 2&lt;br /&gt;        return [(largest-i, [i]+rest) for i in range(min, max+1)]&lt;br /&gt;&lt;br /&gt;    return make_dfs_search(root, children)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub make_dfs_search {&lt;br /&gt;#    my ($root, $children, $is_interesting) = @_;&lt;br /&gt;#    my @agenda = $root;&lt;br /&gt;#    return Iterator {&lt;br /&gt;#      while (@agenda) {&lt;br /&gt;#        my $node = pop @agenda;&lt;br /&gt;#        push @agenda, $children-&gt;($node);&lt;br /&gt;#        return $node if !$is_interesting || $is_interesting-&gt;($node);&lt;br /&gt;#      }&lt;br /&gt;#      return;&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;def make_dfs_search(root, children, is_interesting=None):&lt;br /&gt;    agenda = [root]&lt;br /&gt;    &lt;br /&gt;    while agenda:&lt;br /&gt;        node = agenda.pop()&lt;br /&gt;        agenda.extend(children(node))&lt;br /&gt;        if is_interesting and is_interesting(node):&lt;br /&gt;            yield node&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub make_dfs_value_search {&lt;br /&gt;#   my ($root, $children, $is_interesting, $evaluate) = @_;&lt;br /&gt;#   $evaluate = memoize($evaluate);&lt;br /&gt;#   my @agenda = $root;&lt;br /&gt;#   return Iterator {&lt;br /&gt;#      while (@agenda) {&lt;br /&gt;#        my $best_node_so_far = 0;&lt;br /&gt;#        my $best_node_value = $evaluate-&gt;($agenda[0]);&lt;br /&gt;#        for (0 .. $#agenda) {&lt;br /&gt;#          my $val = $evaluate-&gt;($agenda[$_]);&lt;br /&gt;#          next unless $val &gt; $best_node_value;&lt;br /&gt;#          $best_node_value = $val;&lt;br /&gt;#          $best_node_so_far = $_;&lt;br /&gt;#       }&lt;br /&gt;#       my $node = splice @agenda, $best_node_so_far, 1;&lt;br /&gt;#       push @agenda, $children-&gt;($node);&lt;br /&gt;#       return $node if !$is_interesting || $is_interesting-&gt;($node);&lt;br /&gt;#     }&lt;br /&gt;#     return;&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;def make_dfs_value_search(root, children, is_interesting=None, evaluate=None):&lt;br /&gt;    if not is_interesting:&lt;br /&gt;        is_interesting = (lambda x: True)&lt;br /&gt;&lt;br /&gt;    if not evaluate:&lt;br /&gt;        evaluate = (lambda x: x)&lt;br /&gt;&lt;br /&gt;    agenda = [root]&lt;br /&gt;    &lt;br /&gt;    while agenda:&lt;br /&gt;        node = agenda.pop()&lt;br /&gt;        agenda.extend(children(node))&lt;br /&gt;        if is_interesting(node):&lt;br /&gt;            yield node&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub make_dfs_search {&lt;br /&gt;#   my ($root, $children, $is_interesting) = @_;&lt;br /&gt;#   my @agenda = $root;&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     while (@agenda) {&lt;br /&gt;#       my $node = pop @agenda;&lt;br /&gt;#       push @agenda, reverse $children-&gt;($node);&lt;br /&gt;#       return $node if !$is_interesting || $is_interesting-&gt;($node);&lt;br /&gt;#     }&lt;br /&gt;#     return;&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;def make_dfs_search(root, children, is_interesting=None):&lt;br /&gt;    agenda = [root]&lt;br /&gt;    &lt;br /&gt;    while agenda:&lt;br /&gt;        node = agenda.pop()&lt;br /&gt;        agenda.extend(reversed(children(node)))&lt;br /&gt;        if is_interesting and is_interesting(node):&lt;br /&gt;            yield node&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;### 5.4 Other General Techniques for Eliminating Recursion&lt;br /&gt;&lt;br /&gt;# sub gcd {&lt;br /&gt;#   my ($m, $n) = @_;&lt;br /&gt;#   if ($n == 0) {&lt;br /&gt;#     return $m;&lt;br /&gt;#   }&lt;br /&gt;#   return gcd($n, $m % $n);&lt;br /&gt;# }&lt;br /&gt;def gcd(m,n):&lt;br /&gt;    if n == 0:&lt;br /&gt;        return m&lt;br /&gt;    return gcd(n, m % n)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub gcd {&lt;br /&gt;#   my ($m, $n) = @_;&lt;br /&gt;#   until ($n == 0) {&lt;br /&gt;#     ($m, $n) = ($n, $m % $n);&lt;br /&gt;#   }&lt;br /&gt;#   return $m;&lt;br /&gt;# }&lt;br /&gt;def gcd(m,n):&lt;br /&gt;    while n != 0:&lt;br /&gt;        m, n = n, m % n&lt;br /&gt;    return m&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub print_tree {&lt;br /&gt;#   my $t = shift;&lt;br /&gt;#   return unless $t;  # Null tree&lt;br /&gt;#   print_tree($t-&gt;left);&lt;br /&gt;#   print $t-&gt;root, "\n";&lt;br /&gt;#   print_tree($t-&gt;right);&lt;br /&gt;# }&lt;br /&gt;def print_tree(t):&lt;br /&gt;    if not t:&lt;br /&gt;        return&lt;br /&gt;    print_tree(t.left)&lt;br /&gt;    print t.root&lt;br /&gt;    print_tree(t.right)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub print_tree {&lt;br /&gt;#   my $t = shift;&lt;br /&gt;#   while ($t) {&lt;br /&gt;#     print_tree($t-&gt;left);&lt;br /&gt;#     print $t-&gt;root, "\n";&lt;br /&gt;#     $t = $t-&gt;right;&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def print_tree(t):&lt;br /&gt;    while t:&lt;br /&gt;        print_tree(t.left)&lt;br /&gt;        print t.root&lt;br /&gt;        t = t.right&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub print_tree {&lt;br /&gt;#   my $t = shift;&lt;br /&gt;#   print_tree($t-&gt;left) if $t-&gt;left;&lt;br /&gt;#   print $t-&gt;root, "\n";&lt;br /&gt;#   print_tree($t-&gt;right) if $t-&gt;right;&lt;br /&gt;# }&lt;br /&gt;def print_tree(t):&lt;br /&gt;    if t.left:&lt;br /&gt;        print_tree(t.left)&lt;br /&gt;    print t.root&lt;br /&gt;    if t.right:&lt;br /&gt;        print_tree(t.right)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub print_tree {&lt;br /&gt;#   my $t = shift;&lt;br /&gt;#   do {&lt;br /&gt;#     print_tree($t-&gt;left) if $t-&gt;left;&lt;br /&gt;#     print $t-&gt;root, "\n";&lt;br /&gt;#     $t = $t-&gt;right;&lt;br /&gt;#   } while $t;&lt;br /&gt;# }&lt;br /&gt;def print_tree(t):&lt;br /&gt;    while t:&lt;br /&gt;        if t.left:&lt;br /&gt;            print_tree(t.left)&lt;br /&gt;        print t.root&lt;br /&gt;        t = t.right&lt;br /&gt;    &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub powerset_recurse ($;@) {&lt;br /&gt;#     my ( $set, $powerset, $keys, $values, $n, $i ) = @_;&lt;br /&gt;#     if ( @_ == 1 ) { # Initialize.&lt;br /&gt;#         my $null   = { };&lt;br /&gt;#         $powerset  = { $null, $null };&lt;br /&gt;#         $keys      = [ keys    %{ $set } ];&lt;br /&gt;#         $values    = [ values %{ $set } ];&lt;br /&gt;#         $nmembers  = keys %{ $set };    # This many rounds.&lt;br /&gt;#         $i         = 0;                  # The current round.&lt;br /&gt;#     }&lt;br /&gt;#     # Ready?&lt;br /&gt;#     return $powerset if $i == $nmembers;&lt;br /&gt;#     # Remap.&lt;br /&gt;#     my @powerkeys    = keys   %{ $powerset };&lt;br /&gt;#     my @powervalues = values %{ $powerset };&lt;br /&gt;#     my $powern      = @powerkeys;&lt;br /&gt;#     my $j;&lt;br /&gt;#     for ( $j = 0; $j &lt; $powern; $j++ ) {&lt;br /&gt;#         my %subset = ( );&lt;br /&gt;#         # Copy the old set to the subset.&lt;br /&gt;#         @subset{keys    %{ $powerset-&gt;{ $powerkeys  [ $j ] } }} =&lt;br /&gt;#                 values %{ $powerset-&gt;{ $powervalues[ $j ] } };&lt;br /&gt;#         # Add the new member to the subset.&lt;br /&gt;#         $subset{$keys-&gt;[ $i ]} = $values-&gt;[ $i ];&lt;br /&gt;#         # Add the new subset to the powerset.&lt;br /&gt;#         $powerset-&gt;{ \%subset } = \%subset;&lt;br /&gt;#     }&lt;br /&gt;#     # Recurse.&lt;br /&gt;#   powerset_recurse( $set, $powerset, $keys, $values, $nmembers, $i+1 );&lt;br /&gt;# }&lt;br /&gt;# we are stuck since python won't permit keys that aren't hashable&lt;br /&gt;# so instead of dicts of dicts i'll use set() with tuples.&lt;br /&gt;# we can easily go back to dicts from a set of tuples &lt;br /&gt;# but i may be missing some subtly important feature of this&lt;br /&gt;# algorithm.  but in any case it works.&lt;br /&gt;# also: this algorithm *seems* ridiculously unpythonic&lt;br /&gt;# _set: [(key,val)] # list of &lt;br /&gt;# powerset: [set([(key, val)])] # list of sets of tuple&lt;br /&gt;def powerset_recurse(_set, powerset=None, i=0):&lt;br /&gt;    # set up init stuff&lt;br /&gt;    if powerset == None:&lt;br /&gt;        powerset = [set()]&lt;br /&gt;&lt;br /&gt;    if i == len(_set):&lt;br /&gt;        return powerset&lt;br /&gt;&lt;br /&gt;    powerset_copy = copy.deepcopy(powerset)&lt;br /&gt;    ith_item = _set[i]&lt;br /&gt;    for subset in powerset_copy:&lt;br /&gt;       subset.add(ith_item)&lt;br /&gt;&lt;br /&gt;    powerset += powerset_copy&lt;br /&gt;&lt;br /&gt;    return powerset_recurse(_set, powerset, i+1)&lt;br /&gt;### uggg.  so that works, and *seems* very close&lt;br /&gt;### in principle to the original but I feel like&lt;br /&gt;### i didn't try hard enough to match the original style&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub powerset_recurse ($) {&lt;br /&gt;#   my ( $set ) = @_;&lt;br /&gt;#   my $null = { };&lt;br /&gt;#   my $powerset  = { $null, $null };&lt;br /&gt;#   my $keys      = [ keys    %{ $set } ];&lt;br /&gt;#   my $values    = [ values %{ $set } ];&lt;br /&gt;#   my $nmembers  = keys %{ $set };     # This many rounds.&lt;br /&gt;#   my $i         = 0;                  # The current round.&lt;br /&gt;#   until ($i == $nmembers) {&lt;br /&gt;#     # Remap.&lt;br /&gt;#     my @powerkeys    = keys   %{ $powerset };&lt;br /&gt;#     my @powervalues = values %{ $powerset };&lt;br /&gt;#     my $powern       = @powerkeys;&lt;br /&gt;#     my $j;&lt;br /&gt;#     for ( $j = 0; $j &lt; $powern; $j++ ) {&lt;br /&gt;#         my %subset = ( );&lt;br /&gt;#         # Copy the old set to the subset.&lt;br /&gt;#         @subset{keys    %{ $powerset-&gt;{ $powerkeys  [ $j ] } }} =&lt;br /&gt;#                 values %{ $powerset-&gt;{ $powervalues[ $j ] } };&lt;br /&gt;#         # Add the new member to the subset.&lt;br /&gt;#         $subset{$keys-&gt;[ $i ]} = $values-&gt;[ $i ];&lt;br /&gt;#         # Add the new subset to the powerset.&lt;br /&gt;#         $powerset-&gt;{ \%subset } = \%subset;&lt;br /&gt;#     }&lt;br /&gt;#      $i++;&lt;br /&gt;#   }&lt;br /&gt;#   return $powerset;&lt;br /&gt;# }&lt;br /&gt;def powerset_recurse(_set, powerset=None, i=0):&lt;br /&gt;    # set up init stuff&lt;br /&gt;    if powerset == None:&lt;br /&gt;        powerset = [set()]&lt;br /&gt;    &lt;br /&gt;    while i &lt; len(_set):&lt;br /&gt;        powerset_copy = copy.deepcopy(powerset)&lt;br /&gt;        ith_item = _set[i]&lt;br /&gt;        for subset in powerset_copy:&lt;br /&gt;           subset.add(ith_item)&lt;br /&gt;&lt;br /&gt;        powerset += powerset_copy&lt;br /&gt;&lt;br /&gt;        i += 1&lt;br /&gt;&lt;br /&gt;    return powerset&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub powerset_recurse ($) {&lt;br /&gt;#     my ( $set ) = @_;&lt;br /&gt;#     my $null = { };&lt;br /&gt;#     my $powerset  = { $null, $null };&lt;br /&gt;#     my $keys      = [ keys    %{ $set } ];&lt;br /&gt;#     my $values    = [ values %{ $set } ];&lt;br /&gt;#     my $nmembers  = keys %{ $set };     # This many rounds.&lt;br /&gt;#     for my $i (0 .. $nmembers-1) {&lt;br /&gt;#       #  Remap.&lt;br /&gt;#       my @powerkeys    = keys   %{ $powerset };&lt;br /&gt;#       my @powervalues = values %{ $powerset };&lt;br /&gt;#       my $powern      = @powerkeys;&lt;br /&gt;#       my $j;&lt;br /&gt;#       for ( $j = 0; $j &lt; $powern; $j++ ) {&lt;br /&gt;#           my %subset = ( );&lt;br /&gt;#           # Copy the old set to the subset.&lt;br /&gt;#           @subset{keys    %{ $powerset-&gt;{ $powerkeys  [ $j ] } }} =&lt;br /&gt;#                   values %{ $powerset-&gt;{ $powervalues[ $j ] } };&lt;br /&gt;#           # Add the new member to the subset.&lt;br /&gt;#           $subset{$keys-&gt;[ $i ]} = $values-&gt;[ $i ];&lt;br /&gt;#           # Add the new subset to the powerset.&lt;br /&gt;#           $powerset-&gt;{ \%subset } = \%subset;&lt;br /&gt;#       }&lt;br /&gt;#     }&lt;br /&gt;#     return $powerset;&lt;br /&gt;# }&lt;br /&gt;def powerset_recurse(_set, powerset=None, i=0):&lt;br /&gt;    # set up init stuff&lt;br /&gt;    if powerset == None:&lt;br /&gt;        powerset = [set()]&lt;br /&gt;    &lt;br /&gt;    for i in range(len(_set)):&lt;br /&gt;        powerset_copy = copy.deepcopy(powerset)&lt;br /&gt;        ith_item = _set[i]&lt;br /&gt;        for subset in powerset_copy:&lt;br /&gt;           subset.add(ith_item)&lt;br /&gt;&lt;br /&gt;        powerset += powerset_copy&lt;br /&gt;&lt;br /&gt;    return powerset&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub powerset_recurse ($) {&lt;br /&gt;#     my ( $set ) = @_;&lt;br /&gt;#     my $null = { };&lt;br /&gt;#     my $powerset  = { $null, $null };&lt;br /&gt;#     while (my ($key, $value) = each %$set) {&lt;br /&gt;#       # Remap.&lt;br /&gt;#       my @powerkeys    = keys   %{ $powerset };&lt;br /&gt;#       my @powervalues = values %{ $powerset };&lt;br /&gt;#       my $powern      = @powerkeys;&lt;br /&gt;#       my $j;&lt;br /&gt;#       for ( $j = 0; $j &lt; $powern; $j++ ) {&lt;br /&gt;#           my %subset = ( );&lt;br /&gt;#           # Copy the old set to the subset.&lt;br /&gt;#           @subset{keys    %{ $powerset-&gt;{ $powerkeys [ $j ] } }} =&lt;br /&gt;#                   values %{ $powerset-&gt;{ $powervalues[ $j ] } };&lt;br /&gt;#           # Add the new member to the subset.&lt;br /&gt;#           $subset{$key} = $value;&lt;br /&gt;#           # Add the new subset to the powerset.&lt;br /&gt;#           $powerset-&gt;{ \%subset } = \%subset;&lt;br /&gt;#       }&lt;br /&gt;#     }&lt;br /&gt;#     return $powerset;&lt;br /&gt;# }&lt;br /&gt;def powerset_recurse(_set):&lt;br /&gt;    powerset = [set()]&lt;br /&gt;    &lt;br /&gt;    for ith_item in (_set):&lt;br /&gt;        powerset_copy = copy.deepcopy(powerset)&lt;br /&gt;        for subset in powerset_copy:&lt;br /&gt;           subset.add(ith_item)&lt;br /&gt;&lt;br /&gt;        powerset += powerset_copy&lt;br /&gt;&lt;br /&gt;    return powerset&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub binary {&lt;br /&gt;#   my ($n) = @_;&lt;br /&gt;#   return $n if $n == 0 || $n == 1;&lt;br /&gt;#   my $k = int($n/2);&lt;br /&gt;#   my $b = $n % 2;&lt;br /&gt;#   my $E = binary($k);&lt;br /&gt;#   return $E . $b;&lt;br /&gt;# }&lt;br /&gt;def binary(n):&lt;br /&gt;    if n in (0,1):&lt;br /&gt;        return str(n)&lt;br /&gt;&lt;br /&gt;    k = n / 2&lt;br /&gt;    b = n % 2&lt;br /&gt;&lt;br /&gt;    return binary(k) + str(b)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub binary {&lt;br /&gt;#   my ($n, $RETVAL) = @_;&lt;br /&gt;#   $RETVAL = "" unless defined $RETVAL;&lt;br /&gt;#   my $k = int($n/2);&lt;br /&gt;#   my $b = $n % 2;&lt;br /&gt;#   $RETVAL = "$b$RETVAL";&lt;br /&gt;#   return $RETVAL if $n == 0 || $n == 1;&lt;br /&gt;#   binary($k, $RETVAL);&lt;br /&gt;# }&lt;br /&gt;def binary(n, retval=""):&lt;br /&gt;    k = n / 2&lt;br /&gt;    b = n % 2&lt;br /&gt;&lt;br /&gt;    retval = str(b)+retval&lt;br /&gt;    if n in (0,1):&lt;br /&gt;        return retval&lt;br /&gt;&lt;br /&gt;    return binary(k, retval)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub binary {&lt;br /&gt;#    my ($n, $RETVAL) = @_;&lt;br /&gt;#    $RETVAL = "";&lt;br /&gt;#    while (1) {&lt;br /&gt;#      my $k = int($n/2);&lt;br /&gt;#      my $b = $n % 2;&lt;br /&gt;#      $RETVAL = "$b$RETVAL";&lt;br /&gt;#      return $RETVAL if $n == 0 || $n == 1;&lt;br /&gt;#     $n = $k;&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def binary(n, retval=""):&lt;br /&gt;    while 1:&lt;br /&gt;        k = n / 2&lt;br /&gt;        b = n % 2&lt;br /&gt;        retval = str(b)+retval&lt;br /&gt;        if n in (0,1):&lt;br /&gt;            return retval&lt;br /&gt;        n = k&lt;br /&gt;&lt;br /&gt;# sub binary {&lt;br /&gt;#   my ($n, $RETVAL) = @_;&lt;br /&gt;#   $RETVAL = "";&lt;br /&gt;#   while (1) {&lt;br /&gt;#     my $b = $n % 2;&lt;br /&gt;#     $RETVAL = "$b$RETVAL";&lt;br /&gt;#     return $RETVAL if $n == 0 || $n == 1;&lt;br /&gt;#     $n = int($n/2);&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def binary(n, retval=""):&lt;br /&gt;    while 1:&lt;br /&gt;        b = n % 2&lt;br /&gt;        retval = str(b)+retval&lt;br /&gt;        if n in (0,1):&lt;br /&gt;            return retval&lt;br /&gt;        n = n/2&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub factorial {&lt;br /&gt;#   my ($n) = @_;&lt;br /&gt;#   return 1 if $n == 0;&lt;br /&gt;#   return factorial($n-1) * $n;&lt;br /&gt;# }&lt;br /&gt;def factorial(n):&lt;br /&gt;    if n == 0:&lt;br /&gt;        return 1&lt;br /&gt;    return factorial(n-1)*n&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub factorial {&lt;br /&gt;#   my ($n, $product) = @_;&lt;br /&gt;#   $product = 1 unless defined $product;&lt;br /&gt;#   return $product if $n == 0;&lt;br /&gt;#   return factorial($n-1, $n * $product);&lt;br /&gt;# }&lt;br /&gt;def factorial(n, product=1):&lt;br /&gt;    if n == 0:&lt;br /&gt;        return product&lt;br /&gt;    return factorial(n-1, n*product)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub factorial {&lt;br /&gt;#   my ($n) = @_;&lt;br /&gt;#   my $product = 1;&lt;br /&gt;#   until ($n == 0) {&lt;br /&gt;#     $product *= $n;&lt;br /&gt;#     $n--;&lt;br /&gt;#   }&lt;br /&gt;#   return $product;&lt;br /&gt;# }&lt;br /&gt;def factorial(n):&lt;br /&gt;    product = 1&lt;br /&gt;    while n != 0:&lt;br /&gt;        product *= n&lt;br /&gt;        n -= 1&lt;br /&gt;    return product&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub print_tree {&lt;br /&gt;#   my $t = shift;&lt;br /&gt;#   do {&lt;br /&gt;#     print_tree($t-&gt;left) if $t-&gt;left;&lt;br /&gt;#     print $t-&gt;root, "\n";&lt;br /&gt;#     $t = $t-&gt;right;&lt;br /&gt;#   } while $t;&lt;br /&gt;# }&lt;br /&gt;def print_tree(t):&lt;br /&gt;    while t:&lt;br /&gt;        if t.left:&lt;br /&gt;            print_tree(t.left)&lt;br /&gt;        print t.root&lt;br /&gt;        t = t.right&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub print_tree {&lt;br /&gt;#   my $t = shift;&lt;br /&gt;#   my @STACK;&lt;br /&gt;#   do {&lt;br /&gt;#     push(@STACK, $t), $t = $t-&gt;left if $t-&gt;left;&lt;br /&gt;#     RETURN:&lt;br /&gt;#        print $t-&gt;root, "\n";&lt;br /&gt;#     $t = $t-&gt;right;&lt;br /&gt;#   } while $t;&lt;br /&gt;#   return unless @STACK;&lt;br /&gt;#   $t = pop @STACK;&lt;br /&gt;#   goto RETURN;&lt;br /&gt;# }&lt;br /&gt;### this and the next couple examples&lt;br /&gt;### use GOTOs.  not much i can do about that.&lt;br /&gt;### if there is a way to simulate gotos in python&lt;br /&gt;### i'd be interested to hear it.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub fib {&lt;br /&gt;#   my $n = shift;&lt;br /&gt;#   if ($n &lt; 2) { return $n }&lt;br /&gt;#   fib($n-2) + fib($n-1);&lt;br /&gt;# }&lt;br /&gt;def fib(n):&lt;br /&gt;    if n &lt; 2:&lt;br /&gt;        return n&lt;br /&gt;    return fib(n-2) + fib(n-1)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub fib {&lt;br /&gt;#   my $n = shift;&lt;br /&gt;#   if ($n &lt; 2) {&lt;br /&gt;#     return $n;&lt;br /&gt;#   } else {&lt;br /&gt;#     my $s1 = fib($n-2);&lt;br /&gt;#     my $s2 = fib($n-1);&lt;br /&gt;#     return $s1 + $s2;&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def fib(n):&lt;br /&gt;    if n &lt; 2:&lt;br /&gt;        return n&lt;br /&gt;    else:&lt;br /&gt;        s1 = fib(n-2)&lt;br /&gt;        s2 = fib(n-1)&lt;br /&gt;        return s1 + s2&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub fib {&lt;br /&gt;#   my $n = shift;&lt;br /&gt;#   while (1) {&lt;br /&gt;#     if ($n &lt; 2) {&lt;br /&gt;#       return $n;&lt;br /&gt;#     } else {&lt;br /&gt;#       my $s1 = fib($n-2);&lt;br /&gt;#       my $s2 = fib($n-1);&lt;br /&gt;#       return $s1 + $s2;&lt;br /&gt;#     }&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def fib(n):&lt;br /&gt;    while 1:&lt;br /&gt;        if n &lt; 2:&lt;br /&gt;            return n&lt;br /&gt;        else:&lt;br /&gt;            s1 = fib(n-2)&lt;br /&gt;            s2 = fib(n-1)&lt;br /&gt;            return s1 + s2&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub fib { &lt;br /&gt;#   my $n = shift;&lt;br /&gt;#   my ($s1, $s2, $return);&lt;br /&gt;#   while (1) {&lt;br /&gt;#     if ($n &lt; 2) {&lt;br /&gt;#       return $n;&lt;br /&gt;#     } else {&lt;br /&gt;#       if ($BRANCH == 0) {&lt;br /&gt;#         $return = fib($n-2);&lt;br /&gt;#       } elsif ($BRANCH == 1) {&lt;br /&gt;#         $s1 = $return;&lt;br /&gt;#         $return = fib($n-1);&lt;br /&gt;#       } elsif ($BRANCH == 2) {&lt;br /&gt;#         $s2 = $return;&lt;br /&gt;#         $return = $s1 + $s2;&lt;br /&gt;#       }&lt;br /&gt;#     }&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def fib(n):&lt;br /&gt;    BRANCH = 0&lt;br /&gt;    while 1:&lt;br /&gt;        if n &lt; 2:&lt;br /&gt;            return n&lt;br /&gt;        else:&lt;br /&gt;            if BRANCH == 0:&lt;br /&gt;                _return = fib(n-2)&lt;br /&gt;            elif BRANCH == 1:&lt;br /&gt;                s1 = _return&lt;br /&gt;                _retrun = fib(n-2)&lt;br /&gt;            elif BRANCH == 2:&lt;br /&gt;                s2 = _return&lt;br /&gt;                _return = s1 + s2&lt;br /&gt;    return _return&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub fib {&lt;br /&gt;#   my $n = shift;&lt;br /&gt;#   my ($s1, $s2, $return);&lt;br /&gt;#   my $BRANCH = 0;&lt;br /&gt;#   while (1) {&lt;br /&gt;#     if ($n &lt; 2) {&lt;br /&gt;#       return $n;&lt;br /&gt;#     } else {&lt;br /&gt;#       if ($BRANCH == 0) {&lt;br /&gt;#          $return = fib($n-2);&lt;br /&gt;#       } elsif ($BRANCH == 1) {&lt;br /&gt;#          $s1 = $return;&lt;br /&gt;#          $return = fib($n-1);&lt;br /&gt;#       } elsif ($BRANCH == 2) {&lt;br /&gt;#          $s2 = $return;&lt;br /&gt;#          $return = $s1 + $s2;&lt;br /&gt;#       }&lt;br /&gt;#     }&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def fib(n):&lt;br /&gt;    BRANCH = 0&lt;br /&gt;    while 1:&lt;br /&gt;        if n &lt; 2:&lt;br /&gt;            return n&lt;br /&gt;        else:&lt;br /&gt;            if BRANCH == 0:&lt;br /&gt;                _return = fib(n-2)&lt;br /&gt;            elif BRANCH == 1:&lt;br /&gt;                s1 = _return&lt;br /&gt;                _return = fib(n-1)&lt;br /&gt;            elif BRANCH == 2:&lt;br /&gt;                s2 = _return&lt;br /&gt;                _return = s1 + s2&lt;br /&gt;    return _return&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub fib {&lt;br /&gt;#   my $n = shift;&lt;br /&gt;#   my ($s1, $s2, $return);&lt;br /&gt;#   my $BRANCH = 0;&lt;br /&gt;#   while (1) {&lt;br /&gt;#     if ($n &lt; 2) {&lt;br /&gt;#       $return = $n;&lt;br /&gt;#     } else {&lt;br /&gt;#       if ($BRANCH == 0) {&lt;br /&gt;#         $return = fib($n-2);&lt;br /&gt;#       } elsif ($BRANCH == 1) {&lt;br /&gt;#         $s1 = $return;&lt;br /&gt;#         $return = fib($n-1);&lt;br /&gt;#       } elsif ($BRANCH == 2) {&lt;br /&gt;#         $return = $s1 + $s2;&lt;br /&gt;#       }&lt;br /&gt;#     }&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def fib(n):&lt;br /&gt;    BRANCH = 0&lt;br /&gt;    while 1:&lt;br /&gt;        if n &lt; 2:&lt;br /&gt;            _return = n&lt;br /&gt;        else:&lt;br /&gt;            if BRANCH == 0:&lt;br /&gt;                _return = fib(n-2)&lt;br /&gt;            elif BRANCH == 1:&lt;br /&gt;                s1 = _return&lt;br /&gt;                _return = fib(n-1)&lt;br /&gt;            elif BRANCH == 2:&lt;br /&gt;                _return = s1 + s2&lt;br /&gt;    return _return&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub fib {&lt;br /&gt;#   my $n = shift;&lt;br /&gt;#   my ($s1, $s2, $return);&lt;br /&gt;#   my $BRANCH = 0;&lt;br /&gt;#   my @STACK;&lt;br /&gt;#   while (1) {&lt;br /&gt;#     if ($n &lt; 2) {&lt;br /&gt;#       $return = $n;&lt;br /&gt;#     } else {&lt;br /&gt;#       if ($BRANCH == 0) {&lt;br /&gt;#         push @STACK, [ $BRANCH, $s1, $s2, $n ];&lt;br /&gt;#         $n -= 2;&lt;br /&gt;#         $BRANCH = 0;&lt;br /&gt;#         next;&lt;br /&gt;#       } elsif ($BRANCH == 1) {&lt;br /&gt;#         $s1 = $return;&lt;br /&gt;#         push @STACK, [ $BRANCH, $s1, $s2, $n ];&lt;br /&gt;#         $n -= 1;&lt;br /&gt;#         $BRANCH = 0;&lt;br /&gt;#         next;&lt;br /&gt;#       } elsif ($BRANCH == 2) {&lt;br /&gt;#         $s2 = $return;&lt;br /&gt;#         $return = $s1 + $s2;&lt;br /&gt;#       }&lt;br /&gt;#     }&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def fib(n):&lt;br /&gt;    BRANCH = 0&lt;br /&gt;    STACK = []&lt;br /&gt;    while 1:&lt;br /&gt;        if n &lt; 2:&lt;br /&gt;            _return = n&lt;br /&gt;        else:&lt;br /&gt;            if BRANCH == 0:&lt;br /&gt;                STACK.append([BRANCH, s1, s2, n])&lt;br /&gt;                n -= 2&lt;br /&gt;                BRANCH = 0&lt;br /&gt;                continue&lt;br /&gt;            elif BRANCH == 1:&lt;br /&gt;                s1 = _return&lt;br /&gt;                STACK.append([BRANCH, s1, s2, n])&lt;br /&gt;                n -= 1&lt;br /&gt;                BRANCH = 0&lt;br /&gt;                continue&lt;br /&gt;            elif BRANCH == 2:&lt;br /&gt;                s2 = _return&lt;br /&gt;                _return = s1 + s2&lt;br /&gt;    return _return&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub fib {&lt;br /&gt;#   my $n = shift;&lt;br /&gt;#   my ($s1, $s2, $return);&lt;br /&gt;#   my $BRANCH = 0;&lt;br /&gt;#   my @STACK;&lt;br /&gt;#   while (1) {&lt;br /&gt;#     if ($n &lt; 2) {&lt;br /&gt;#       $return = $n;&lt;br /&gt;#     } else {&lt;br /&gt;#       if ($BRANCH == 0) {&lt;br /&gt;#         push @STACK, [ $BRANCH, $s1, $s2, $n ];&lt;br /&gt;#         $n -= 2;&lt;br /&gt;#         $BRANCH = 0;&lt;br /&gt;#         next;&lt;br /&gt;#       } elsif ($BRANCH == 1) {&lt;br /&gt;#         $s1 = $return;&lt;br /&gt;#         push @STACK, [ $BRANCH, $s1, $s2, $n ];&lt;br /&gt;#         $n -= 1;&lt;br /&gt;#         $BRANCH = 0;&lt;br /&gt;#         next;&lt;br /&gt;#       } elsif ($BRANCH == 2) {&lt;br /&gt;#         $s2 = $return;&lt;br /&gt;#         $return = $s1 + $s2;&lt;br /&gt;#     }&lt;br /&gt;#   }&lt;br /&gt;#   return $return unless @STACK;&lt;br /&gt;#   ($BRANCH, $s1, $s2, $n) = @{pop @STACK};&lt;br /&gt;#   $BRANCH++;&lt;br /&gt;# }&lt;br /&gt;def fib(n):&lt;br /&gt;    BRANCH = 0&lt;br /&gt;    STACK = []&lt;br /&gt;    while 1:&lt;br /&gt;        if n &lt; 2:&lt;br /&gt;            _return = n&lt;br /&gt;        else:&lt;br /&gt;            if BRANCH == 0:&lt;br /&gt;                STACK.append([BRANCH, s1, s2, n])&lt;br /&gt;                n -= 2&lt;br /&gt;                BRANCH = 0&lt;br /&gt;                continue&lt;br /&gt;            elif BRANCH == 1:&lt;br /&gt;                s1 = _return&lt;br /&gt;                STACK.append([BRANCH, s1, s2, n])&lt;br /&gt;                n -= 1&lt;br /&gt;                BRANCH = 0&lt;br /&gt;                continue&lt;br /&gt;            elif BRANCH == 2:&lt;br /&gt;                s2 = _return&lt;br /&gt;                _return = s1 + s2&lt;br /&gt;    if not STACK:&lt;br /&gt;        return _return&lt;br /&gt;    (BRANCH, s1, s2, n) = STACK.pop()&lt;br /&gt;    BRANCH += 1&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub fib {&lt;br /&gt;#   my $n = shift;&lt;br /&gt;#   my ($s1, $s2, $return);&lt;br /&gt;#   my $BRANCH = 0;&lt;br /&gt;#   my @STACK;&lt;br /&gt;#   while (1) {&lt;br /&gt;#     if ($n &lt; 2) {&lt;br /&gt;#       $return = $n;&lt;br /&gt;#     } else {&lt;br /&gt;#       if ($BRANCH == 0) {&lt;br /&gt;#         push @STACK, [ $BRANCH, 0, $s2, $n ];&lt;br /&gt;#         $n -= 2;&lt;br /&gt;#         next;&lt;br /&gt;#       } elsif ($BRANCH == 1) {&lt;br /&gt;#         push @STACK, [ $BRANCH, $return, $s2, $n ];&lt;br /&gt;#         $n -= 1;&lt;br /&gt;#         $BRANCH = 0;&lt;br /&gt;#         next;&lt;br /&gt;#       } elsif ($BRANCH == 2) {&lt;br /&gt;#         $s2 = $return;&lt;br /&gt;#       $return = $s1 + $s2;&lt;br /&gt;#     }&lt;br /&gt;#   }&lt;br /&gt;#   return $return unless @STACK;&lt;br /&gt;#   ($BRANCH, $s1, $s2, $n) = @{pop @STACK};&lt;br /&gt;#   $BRANCH++;&lt;br /&gt;# }&lt;br /&gt;def fib(n):&lt;br /&gt;    BRANCH = 0&lt;br /&gt;    STACK = []&lt;br /&gt;    while 1:&lt;br /&gt;        if n &lt; 2:&lt;br /&gt;            _return = n&lt;br /&gt;        else:&lt;br /&gt;            if BRANCH == 0:&lt;br /&gt;                STACK.append([BRANCH, 0, s2, n])&lt;br /&gt;                n -= 2&lt;br /&gt;                BRANCH = 0&lt;br /&gt;                continue&lt;br /&gt;            elif BRANCH == 1:&lt;br /&gt;                s1 = _return&lt;br /&gt;                STACK.append([BRANCH, _return, s2, n])&lt;br /&gt;                n -= 1&lt;br /&gt;                BRANCH = 0&lt;br /&gt;                continue&lt;br /&gt;            elif BRANCH == 2:&lt;br /&gt;                s2 = _return&lt;br /&gt;                _return = s1 + s2&lt;br /&gt;    if not STACK:&lt;br /&gt;        return _return&lt;br /&gt;    (BRANCH, s1, s2, n) = STACK.pop()&lt;br /&gt;    BRANCH += 1&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub fib {&lt;br /&gt;#   my $n = shift;&lt;br /&gt;#   my ($s1, $return);&lt;br /&gt;#   my $BRANCH = 0;&lt;br /&gt;#   my @STACK;&lt;br /&gt;#   while (1) {&lt;br /&gt;#     if ($n &lt; 2) {&lt;br /&gt;#       $return = $n;&lt;br /&gt;#     } else {&lt;br /&gt;#       if ($BRANCH == 0) {&lt;br /&gt;#         push @STACK, [ $BRANCH, 0, $n ];&lt;br /&gt;#         $n -= 2;&lt;br /&gt;#         next;&lt;br /&gt;#       } elsif ($BRANCH == 1) {&lt;br /&gt;#         push @STACK, [ $BRANCH, $return, $n ];&lt;br /&gt;#         $n -= 1;&lt;br /&gt;#         $BRANCH = 0;&lt;br /&gt;#         next;&lt;br /&gt;#       } elsif ($BRANCH == 2) {&lt;br /&gt;#         $return += $s1;&lt;br /&gt;#       }&lt;br /&gt;#     }&lt;br /&gt;#     return $return unless @STACK;&lt;br /&gt;#     ($BRANCH, $s1, $n) = @{pop @STACK};&lt;br /&gt;#     $BRANCH++;&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def fib(n):&lt;br /&gt;    BRANCH = 0&lt;br /&gt;    STACK = []&lt;br /&gt;    while 1:&lt;br /&gt;        if n &lt; 2:&lt;br /&gt;            _return = n&lt;br /&gt;        else:&lt;br /&gt;            if BRANCH == 0:&lt;br /&gt;                STACK.append([BRANCH, 0, n])&lt;br /&gt;                n -= 2&lt;br /&gt;                continue&lt;br /&gt;            elif BRANCH == 1:&lt;br /&gt;                STACK.append([BRANCH, _return, n])&lt;br /&gt;                n -= 1&lt;br /&gt;                BRANCH = 0&lt;br /&gt;                continue&lt;br /&gt;            elif BRANCH == 2:&lt;br /&gt;                _return += s1&lt;br /&gt;        if not STACK:&lt;br /&gt;            return _return&lt;br /&gt;        (BRANCH, s1, s2, n) = STACK.pop()&lt;br /&gt;        BRANCH += 1&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub fib {&lt;br /&gt;#   my $n = shift;&lt;br /&gt;#   my ($s1, $return);&lt;br /&gt;#   my $BRANCH = 0;&lt;br /&gt;#   my @STACK;&lt;br /&gt;#   while (1) {&lt;br /&gt;#   if ($n &lt; 2) {&lt;br /&gt;#     $return = $n;&lt;br /&gt;#   } else {&lt;br /&gt;#     if ($BRANCH == 0) {&lt;br /&gt;#       push (@STACK, [ $BRANCH, 0, $n ]), $n -= 1 while $n &gt;= 2;&lt;br /&gt;#       $return = $n;&lt;br /&gt;#     } elsif ($BRANCH == 1) {&lt;br /&gt;#       push @STACK, [ $BRANCH, $return, $n ];&lt;br /&gt;#       $n -= 2;&lt;br /&gt;#       $BRANCH = 0;&lt;br /&gt;#       next;&lt;br /&gt;#     } elsif ($BRANCH == 2) {&lt;br /&gt;#       $return += $s1;&lt;br /&gt;#     }&lt;br /&gt;#   }&lt;br /&gt;#   return $return unless @STACK;&lt;br /&gt;#   ($BRANCH, $s1, $n) = @{pop @STACK};&lt;br /&gt;#   $BRANCH++;&lt;br /&gt;# }&lt;br /&gt;def fib(n):&lt;br /&gt;    BRANCH = 0&lt;br /&gt;    STACK = []&lt;br /&gt;    while 1:&lt;br /&gt;        if n &lt; 2:&lt;br /&gt;            _return = n&lt;br /&gt;        else:&lt;br /&gt;            if BRANCH == 0:&lt;br /&gt;                STACK.append([BRANCH, 0, n])&lt;br /&gt;                while n &gt;= 2:&lt;br /&gt;                    n =- 1&lt;br /&gt;                _return = n&lt;br /&gt;            elif BRANCH == 1:&lt;br /&gt;                STACK.append([BRANCH, _return, n])&lt;br /&gt;                n -= 2&lt;br /&gt;                BRANCH = 0&lt;br /&gt;                continue&lt;br /&gt;            elif BRANCH == 2:&lt;br /&gt;                _return += s1&lt;br /&gt;        if not STACK:&lt;br /&gt;            return _return&lt;br /&gt;        (BRANCH, s1, n) = STACK.pop()&lt;br /&gt;        BRANCH += 1&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub fib {&lt;br /&gt;#   my $n = shift;&lt;br /&gt;#   my ($s1, $return);&lt;br /&gt;#   my $BRANCH = 0;&lt;br /&gt;#   my @STACK;&lt;br /&gt;#   while (1) {&lt;br /&gt;#     if ($n &lt; 2) {&lt;br /&gt;#       $return = $n;&lt;br /&gt;#     } else {&lt;br /&gt;#       if ($BRANCH == 0) {&lt;br /&gt;#         push (@STACK, [ 1, 0, $n ]), $n -= 1 while $n &gt;= 2;&lt;br /&gt;#         $return = $n;&lt;br /&gt;#       } elsif ($BRANCH == 1) {&lt;br /&gt;#         push @STACK, [ 2, $return, $n ];&lt;br /&gt;#         $n -= 2;&lt;br /&gt;#         $BRANCH = 0;&lt;br /&gt;#         next;&lt;br /&gt;#       } elsif ($BRANCH == 2) {&lt;br /&gt;#         $return += $s1;&lt;br /&gt;#       }&lt;br /&gt;#     }&lt;br /&gt;#     return $return unless @STACK;&lt;br /&gt;#     ($BRANCH, $s1, $n) = @{pop @STACK};&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;# NOTE: I got lazy/confused by the end of this chapter so&lt;br /&gt;#       so these last few "fib" functions are not tested&lt;br /&gt;#       But it was an interesting discussion on "unfolding"&lt;br /&gt;#       recursion.&lt;br /&gt;def fib(n):&lt;br /&gt;    BRANCH = 0&lt;br /&gt;    STACK = []&lt;br /&gt;    while 1:&lt;br /&gt;        if n &lt; 2:&lt;br /&gt;            _return = n&lt;br /&gt;        else:&lt;br /&gt;            if BRANCH == 0:&lt;br /&gt;                while n &gt;= 2:&lt;br /&gt;                    STACK.append([1, 0, n])&lt;br /&gt;                    n =- 1&lt;br /&gt;                _return = n&lt;br /&gt;            elif BRANCH == 1:&lt;br /&gt;                STACK.append([2, _return, n])&lt;br /&gt;                n -= 2&lt;br /&gt;                BRANCH = 0&lt;br /&gt;                continue&lt;br /&gt;            elif BRANCH == 2:&lt;br /&gt;                _return += s1&lt;br /&gt;        if not STACK:&lt;br /&gt;            return _return&lt;br /&gt;        (BRANCH, s1, n) = STACK.pop()&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-599367446988922028?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/599367446988922028/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=599367446988922028' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/599367446988922028'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/599367446988922028'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2009/03/higher-order-perl-python-style-chapter.html' title='Higher Order Perl (Python Style) : Chapter 5 - From Recursion To Iterators'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-3410921877017382874</id><published>2009-02-28T21:47:00.000-08:00</published><updated>2009-02-28T22:48:58.206-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='programming languages'/><title type='text'>My Dangerous New Obsession</title><content type='html'>I think if I had to blame anyone, I'd start with Alan Kay.  I recently read the following quote by him:&lt;blockquote&gt;&lt;br /&gt;I think it is safe to say that most of the Squeak community is dedicated to making this Smalltalk more useful and accessible, and not devoted to making something so much better as to render Smalltalk obsolete (a fate I would dearly love to see happen).&lt;/blockquote&gt;&lt;br /&gt;Alan Kay wants smalltalk to go away?  Well, if I'm going to use the man's language I should also have the decency to try to improve upon it and replace it.&lt;br /&gt;&lt;br /&gt;So that was the start of a tiny little snow ball.  And the snow ball grew.  And now I find myself keeping notebooks full of ideas for creating a new programming language.  &lt;br /&gt;&lt;br /&gt;And that is my obsession.  I've got it in my head that I could create a new language.  And that this is somehow a good use of my time.  So I'm constantly comparing and contrasting the various features that different languages have.  Looking for a good idea to steal or a wart to avoid.  I've been shopping around for language parsers and VMs to use.  &lt;br /&gt;&lt;br /&gt;And you know what?  It's fun as hell.  I actually don't have any illusions that I'm going to set the world on fire or that anyone but me will ever use my language.  Or, let's be serious, that there is much likelihood that I will get past the vaporware stage.  But I really feel like I'm seeing the landscape of languages with new eyes.  Sort of like when you take a class on drawing and your brain starts learning how to do the "switch".  And you can almost magically just draw things.  I feel like my eyes are really seeing languages and language features for the first time.&lt;br /&gt;&lt;br /&gt;So what is my language like?  Not much.  I've actually been avoiding trying to commit to any specific syntax.  Which is probably going to make it lisp like if I'm not careful. (Not that there's anything wrong with that).  When I start committing to various features it's been coming out something like a pythonized haskell (or a haskellized python) with strong nods to smalltalk minimalism.  In a word its sort of an incoherent jumble.  But I keep circling around and trying new things.  &lt;br /&gt;&lt;br /&gt;And like I said it's really fun.  And I'm learning a lot.  (And I've become addicted to starting sentences with "and").&lt;br /&gt;&lt;br /&gt;I wonder if this is a common or rare affliction.  I've never met anyone who said that they were trying to create their own language.  Perhaps others are too smart to go down that road in the first place or too ashamed to admit they did and failed.&lt;br /&gt;&lt;br /&gt;In any case, be prepared for the next big thing.  Any decade now....&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-3410921877017382874?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/3410921877017382874/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=3410921877017382874' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/3410921877017382874'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/3410921877017382874'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2009/02/my-dangerous-new-obsession.html' title='My Dangerous New Obsession'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-9002174236756382419</id><published>2009-02-25T20:53:00.000-08:00</published><updated>2009-03-09T19:22:40.803-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='functional programming'/><category scheme='http://www.blogger.com/atom/ns#' term='higher order perl'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='hop'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><title type='text'>Higher Order Perl (Python Style) : Chapter 4 - Iterators</title><content type='html'>&lt;br&gt;&lt;br&gt;&lt;br /&gt;&lt;a href="http://dustbunnylair.blogspot.com/2009/01/higher-order-perl-python-style-toc.html"&gt;TOC&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;### CHAPTER 4 Iterators&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;### 4.1 introduction&lt;br /&gt;&lt;br /&gt;# @lines = open('filename'); # alternate universe interface&lt;br /&gt;lines = open("filename") # a less alternate universe interface&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# open(FILEHANDLE, 'filename');&lt;br /&gt;# while (&lt;FILEHANDLE&gt;) {&lt;br /&gt;#   last if /Plutonium/;&lt;br /&gt;# }&lt;br /&gt;# close FILEHANDLE;&lt;br /&gt;# # do something with $_;&lt;br /&gt;fh = open("filename")&lt;br /&gt;for line in fh:&lt;br /&gt;    if "Plutonium" in line:&lt;br /&gt;        break&lt;br /&gt;fh.close()&lt;br /&gt;# do something with line&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# # alternate universe interface&lt;br /&gt;# @lines = open('filename');&lt;br /&gt;# for (@lines) {&lt;br /&gt;#   last if /Plutonium/;&lt;br /&gt;# }&lt;br /&gt;# # do something with $_;&lt;br /&gt;lines = open("filename").readlines()&lt;br /&gt;for line in lines:&lt;br /&gt;    if "Plutonium" in line:&lt;br /&gt;        break&lt;br /&gt;fh.close()&lt;br /&gt;# do something with line&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# @lines = open("yes |"); # alternate universe interface&lt;br /&gt;lines = os.popen("yes").readlines() # alternate universes interface&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub parse_section {&lt;br /&gt;#   my $fh = shift;&lt;br /&gt;#   my $title = parse_section_title($fh);&lt;br /&gt;#   my %variables = parse_variables($fh);&lt;br /&gt;#   return [$title, \%variables];&lt;br /&gt;# }&lt;br /&gt;def parse_section(fh):&lt;br /&gt;    title = parse_section_title(fh)&lt;br /&gt;    variables = parse_variables(fh)&lt;br /&gt;    return title, variables&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub parse_section {&lt;br /&gt;#   my @lines = @_;&lt;br /&gt;#   my $title = parse_section_title(@lines);&lt;br /&gt;#   my %variables = parse_variables(@lines);&lt;br /&gt;#   return [$title, \%variables];&lt;br /&gt;# }&lt;br /&gt;# In the python case we *could* easily&lt;br /&gt;# pop values from the list (but really you &lt;br /&gt;# could in perl as well)&lt;br /&gt;def parse_section(lines):&lt;br /&gt;    title = parse_section_title(lines)&lt;br /&gt;    variables = parse_variables(lines)&lt;br /&gt;    return title, variables&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# opendir D, "/tmp";&lt;br /&gt;# @entries = readdir D;&lt;br /&gt;# In python we get a list instead of a iterator&lt;br /&gt;entries = os.listdir("/tmp")&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# opendir D, "/tmp";&lt;br /&gt;# while (my $entry = readdir D) {&lt;br /&gt;#   # Do something with $entry&lt;br /&gt;# }&lt;br /&gt;# Python doesn't have "scalar" mode type behavior changes&lt;br /&gt;for entry in os.listdir("/tmp"):&lt;br /&gt;  # Do something with $entry&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# while (my $file = glob("/tmp/*.[ch]")) {&lt;br /&gt;#   # Do something with $file&lt;br /&gt;# }&lt;br /&gt;# glob in python is also not an iterator&lt;br /&gt;for _file in glob.glob("/tmp/*.[ch]"):&lt;br /&gt;    # Do something with $file&lt;br /&gt;&lt;br /&gt;# while (my $key = each %hash) {&lt;br /&gt;#   # Do something with $key&lt;br /&gt;# }&lt;br /&gt;# depending on version of python &lt;br /&gt;# hash may automagically be an iterator&lt;br /&gt;# (maybe all versions...)&lt;br /&gt;for key in hash.iterkeys():&lt;br /&gt;    # Do something with key&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# @matches = ("12:34:56" =~ m/(\d+)/g);&lt;br /&gt;matches = re.findall("(\d+)", "12:34:56")&lt;br /&gt;&lt;br /&gt;# while ("12:34:56" = ̃ m/(\d+)/g) {&lt;br /&gt;#   # do something with $1&lt;br /&gt;# }&lt;br /&gt;for m in re.finditer("(\d+)", "12:34:56"):&lt;br /&gt;    # do something with m (where m is a "match" object)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;### 4.2 Homemade Iterators&lt;br /&gt;&lt;br /&gt;# sub dir_walk {&lt;br /&gt;#   my ($dir, $filefunc, $dirfunc, $user) = @_;&lt;br /&gt;#   my $iterator = make_iterator($dir);&lt;br /&gt;#   while (my $filename = NEXTVAL($iterator)) {&lt;br /&gt;#     if (-f $filename) { $filefunc-&gt;($filename, $user) }&lt;br /&gt;#     else              {  $dirfunc-&gt;($filename, $user) }&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;# In python os.walk returns an iterator as is&lt;br /&gt;def dir_walk(dir, filefunc, dirfunc, user):&lt;br /&gt;    iterator = make_iterator(dir)&lt;br /&gt;    for filename in iterator:&lt;br /&gt;        if os.path.isfile(filename):&lt;br /&gt;            filefunc(filename, user)&lt;br /&gt;        else:&lt;br /&gt;            dirfunc(filename, user)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub upto {&lt;br /&gt;#   my ($m, $n) = @_;&lt;br /&gt;#   return sub {&lt;br /&gt;#     return $m &lt;= $n ? $m++ : undef;&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;# my $it = upto(3, 5);&lt;br /&gt;def upto(m,n):&lt;br /&gt;    _i = [m]&lt;br /&gt;    def foo():&lt;br /&gt;        val = _i[0]&lt;br /&gt;        _i[0] += 1&lt;br /&gt;        if val &gt; n:&lt;br /&gt;            return None&lt;br /&gt;        return val&lt;br /&gt;&lt;br /&gt;    return foo&lt;br /&gt;&lt;br /&gt;it = upto(3,5)&lt;br /&gt;## of course in python it's more natural to do the following:&lt;br /&gt;# def upto(m,n):&lt;br /&gt;#     for x in range(m,n+1):&lt;br /&gt;#         yield x&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# my $nextval = $it-&gt;();&lt;br /&gt;nextval = it()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# while (defined(my $val = $it-&gt;())) {&lt;br /&gt;#   # now do something with $val, such as:&lt;br /&gt;#   print "$val\n";&lt;br /&gt;# }&lt;br /&gt;# this doesn't translate in a pretty way to python&lt;br /&gt;# since we can't have statements in a while &lt;br /&gt;# context&lt;br /&gt;val = it()&lt;br /&gt;while val != None:&lt;br /&gt;    # now do something with val, such as:&lt;br /&gt;    print val&lt;br /&gt;    val = it()&lt;br /&gt;# but of course we'd just use:&lt;br /&gt;for val in it:&lt;br /&gt;    print val&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# for my $val (1 .. 10000000) {&lt;br /&gt;#    # now do something with $val&lt;br /&gt;# }&lt;br /&gt;for val in range(1, 10000000):&lt;br /&gt;    # now do something with val&lt;br /&gt;&lt;br /&gt;# package Iterator_Utils;&lt;br /&gt;# use base Exporter;&lt;br /&gt;# @EXPORT_OK = qw(NEXTVAL Iterator&lt;br /&gt;#                 append imap igrep&lt;br /&gt;#                 iterate_function filehandle_iterator list_iterator);&lt;br /&gt;# %EXPORT_TAGS = ('all' =&gt; \@EXPORT_OK);&lt;br /&gt;# sub NEXTVAL { $_[0]-&gt;() }&lt;br /&gt;&lt;br /&gt;# my $nextval = NEXTVAL($it);&lt;br /&gt;&lt;br /&gt;# while (defined(my $val = NEXTVAL($it))) {&lt;br /&gt;#   # now do something with $val&lt;br /&gt;# }&lt;br /&gt;&lt;br /&gt;# No need to do these machinations since this is already built into&lt;br /&gt;# python except we'd do this with "for"&lt;br /&gt;for val in it:&lt;br /&gt;    # not do something with val&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub upto {&lt;br /&gt;#   my ($m, $n) = @_;&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     return $m &lt;= $n ? $m++ : undef;&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;# sub Iterator (&amp;) { return $_[0] }&lt;br /&gt;# in python we just do this with a yield&lt;br /&gt;def upto(m, n):&lt;br /&gt;    i = m&lt;br /&gt;    while i &lt;= n:&lt;br /&gt;        yield i&lt;br /&gt;        i += 1&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# # iterator version&lt;br /&gt;# sub dir_walk {&lt;br /&gt;#   my @queue = shift;&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     while (@queue) {&lt;br /&gt;#       my $file = shift @queue;&lt;br /&gt;#       if (-d $file) {&lt;br /&gt;#         opendir my $dh, $file or next;&lt;br /&gt;#         my @newfiles = grep {$_ ne "." &amp;&amp; $_ ne ".."} readdir $dh;&lt;br /&gt;#         push @queue, map "$file/$_", @newfiles;&lt;br /&gt;#       }&lt;br /&gt;#       return $file;&lt;br /&gt;#     } else {&lt;br /&gt;#       return;&lt;br /&gt;#     }&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;def dir_walk(root):&lt;br /&gt;    queue = [root]&lt;br /&gt;    while queue:&lt;br /&gt;        _file = queue.pop(0)&lt;br /&gt;        if os.path.isdir(_file):&lt;br /&gt;            for newfile in os.listdir(_file):&lt;br /&gt;                queue.append(os.path.join(_file, newfile))&lt;br /&gt;        yield _file&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;                &lt;br /&gt;                                &lt;br /&gt;# sub dir_walk {&lt;br /&gt;#   my ($top, $code) = @_;&lt;br /&gt;#   my $DIR;&lt;br /&gt;#   $code-&gt;($top);&lt;br /&gt;#   if (-d $top) {&lt;br /&gt;#     my $file;&lt;br /&gt;#     unless (opendir $DIR, $top) {&lt;br /&gt;#       warn "Couldn’t open directory $top: $!; skipping.\n";&lt;br /&gt;#       return;&lt;br /&gt;#     }&lt;br /&gt;#     while ($file = readdir $DIR) {&lt;br /&gt;#       next if $file eq '.'|| $file eq '..'&lt;br /&gt;#       dir_walk("$top/$file", $code);&lt;br /&gt;#     }&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def dir_walk(top, code):&lt;br /&gt;    code(top)&lt;br /&gt;    if os.path.isdir(top):&lt;br /&gt;        try:&lt;br /&gt;            for _file in os.listdir(top):&lt;br /&gt;                dir_walk(os.path.join(top,_file), code)&lt;br /&gt;        except StandardError, why:&lt;br /&gt;            print "Couldn't open directory %s: %s" % (top, why)&lt;br /&gt;            return&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;### 4.3 Examples&lt;br /&gt;&lt;br /&gt;# sub interesting_files {&lt;br /&gt;#   my $is_interesting = shift;&lt;br /&gt;#   my @queue = @_;&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     while (@queue) {&lt;br /&gt;#       my $file = shift @queue;&lt;br /&gt;#       if (-d $file) {&lt;br /&gt;#         opendir my $dh, $file or next;&lt;br /&gt;#         my @newfiles = grep {$_ ne "." &amp;&amp; $_ ne ".."} readdir $dh;&lt;br /&gt;#         push @queue, map "$file/$_", @newfiles;&lt;br /&gt;#       }&lt;br /&gt;#       return $file if $is_interesting-&gt;($file);&lt;br /&gt;#     }&lt;br /&gt;#     return;&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;def interesting_files(is_interesting, *top_dirs):&lt;br /&gt;    queue = list(top_dirs)&lt;br /&gt;    &lt;br /&gt;    while queue:&lt;br /&gt;        _file = queue.pop(0)&lt;br /&gt;        if os.path.isdir(_file):&lt;br /&gt;            for newfile in os.listdir(_file):&lt;br /&gt;                queue.append(os.path.join(_file, newfile))&lt;br /&gt;        if is_interesting(_file):&lt;br /&gt;            yield _file        &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# # Files are deemed to be interesting if they mention octopuses&lt;br /&gt;# sub contains_octopuses {&lt;br /&gt;#   my $file = shift;&lt;br /&gt;#   return unless -T $file &amp;&amp; open my($fh), "&lt;", $file;&lt;br /&gt;#   while (&lt;$fh&gt;) {&lt;br /&gt;#     return 1 if /octopus/i;&lt;br /&gt;#   }&lt;br /&gt;#   return;&lt;br /&gt;# }&lt;br /&gt;# my $octopus_file =&lt;br /&gt;#   interesting_files(\&amp;contains_octopuses, 'uploads', 'downloads');&lt;br /&gt;# while ($file = NEXTVAL($octopus_file)) {&lt;br /&gt;#   # do something with the file&lt;br /&gt;# }&lt;br /&gt;# if (NEXTVAL($next_octopus)) {&lt;br /&gt;#   # yes, there is an interesting file&lt;br /&gt;# } else {&lt;br /&gt;#   # no, there isn’t.&lt;br /&gt;# }&lt;br /&gt;# undef $next_octopus;&lt;br /&gt;def contains_octopuses(_file):&lt;br /&gt;    if not os.path.isfile(_file):&lt;br /&gt;        return False&lt;br /&gt;    for line in file(_file):&lt;br /&gt;        if "octopus" in line:&lt;br /&gt;            return True&lt;br /&gt;    return False&lt;br /&gt;octopus_file = interesting_files(contains_octopuses, "uploads", "downloads")&lt;br /&gt;for _octopus_file in octopus_file:&lt;br /&gt;    # do something with the file&lt;br /&gt;try:&lt;br /&gt;    next_octopus.next()&lt;br /&gt;    # yes, there is an interesting file&lt;br /&gt;except StopIteration:&lt;br /&gt;    # no there isn't&lt;br /&gt;del next_octopus&lt;br /&gt;    &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub permute {&lt;br /&gt;#     my @items = @{ $_[0] };&lt;br /&gt;#     my @perms = @{ $_[1] };&lt;br /&gt;#     unless (@items) {&lt;br /&gt;#         print "@perms\n";&lt;br /&gt;#     } else {&lt;br /&gt;#         my(@newitems,@newperms,$i);&lt;br /&gt;#         foreach $i (0 .. $#items) {&lt;br /&gt;#             @newitems = @items;&lt;br /&gt;#             @newperms = @perms;&lt;br /&gt;#             unshift(@newperms, splice(@newitems, $i, 1));&lt;br /&gt;#             permute([@newitems], [@newperms]);&lt;br /&gt;#         }&lt;br /&gt;#     }&lt;br /&gt;# }&lt;br /&gt;# # sample call:&lt;br /&gt;# permute([qw(red yellow blue green)], []);&lt;br /&gt;# I suspect I don't have this quite right&lt;br /&gt;# it produces the permuations but doesn't&lt;br /&gt;# have the problem of waiting for the end to&lt;br /&gt;# start showing the permutations&lt;br /&gt;def permute(items, perms):&lt;br /&gt;    if not items:&lt;br /&gt;        print perms&lt;br /&gt;    else:&lt;br /&gt;        for i in range(len(items)):&lt;br /&gt;            newitems = items[:]&lt;br /&gt;            newitem = newitems.pop(i)&lt;br /&gt;            newperms = [newperm+[newitem] for newperm in perms] or [[newitem]]&lt;br /&gt;            permute(newitems, newperms)&lt;br /&gt;# sample call&lt;br /&gt;permute(["red", "yello", "blue", "green"], [])&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# my $it = permute('A'..'D');&lt;br /&gt;# while (my @p = NEXTVAL($it)) {&lt;br /&gt;#   print "@p\n";&lt;br /&gt;# }&lt;br /&gt;it = permute(["A","B","C","D"])&lt;br /&gt;for p in it:&lt;br /&gt;    print p&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub permute {&lt;br /&gt;#   my @items = @_;&lt;br /&gt;#   my @pattern = (0) x @items;&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     return unless @pattern;&lt;br /&gt;#     my @result = pattern_to_permutation(\@pattern, \@items);&lt;br /&gt;#     @pattern = increment_pattern(@pattern);&lt;br /&gt;#     return @result;&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;def permute(items):&lt;br /&gt;    pattern = [0] * len(items)&lt;br /&gt;    &lt;br /&gt;    while pattern:&lt;br /&gt;        result = pattern_to_permutation(pattern, items)&lt;br /&gt;        pattern = increment_pattern(pattern)&lt;br /&gt;        yield result&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub pattern_to_permutation {&lt;br /&gt;#   my $pattern = shift;&lt;br /&gt;#   my @items = @{shift()};&lt;br /&gt;#   my @r;&lt;br /&gt;#   for (@$pattern) {&lt;br /&gt;#     push @r, splice(@items, $_, 1);&lt;br /&gt;#   }&lt;br /&gt;#   @r;&lt;br /&gt;# }&lt;br /&gt;def pattern_to_permutation(pattern, items):&lt;br /&gt;    items = items[:]&lt;br /&gt;    r = []&lt;br /&gt;    for _x in pattern:&lt;br /&gt;        r.append(items.pop(_x))&lt;br /&gt;    return r &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub increment_odometer {&lt;br /&gt;#   my @odometer = @_;&lt;br /&gt;#   my $wheel = $#odometer;    # start at rightmost wheel&lt;br /&gt;#   until ($odometer[$wheel] &lt; 9 || $wheel &lt; 0) {&lt;br /&gt;#     $odometer[$wheel] = 0;&lt;br /&gt;#     $wheel--; # next wheel to the left&lt;br /&gt;#   }&lt;br /&gt;#   if ($wheel &lt; 0) {&lt;br /&gt;#     return;   # fell off the left end; no more sequences&lt;br /&gt;#   } else {&lt;br /&gt;#     $odometer[$wheel]++;  # this wheel now turns one notch&lt;br /&gt;#     return @odometer;&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def increment_odometer(odometer):&lt;br /&gt;    wheel = len(odometer) - 1&lt;br /&gt;    while not (odometer[wheel] &lt; 9 or wheel &lt; 0):&lt;br /&gt;        odometer[wheel] = 0&lt;br /&gt;        wheel -= 1&lt;br /&gt;    if wheel &lt; 0:&lt;br /&gt;        return&lt;br /&gt;    else:&lt;br /&gt;        odometer[wheel] += 1&lt;br /&gt;        return odometer&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub increment_pattern {&lt;br /&gt;#   my @odometer = @_;&lt;br /&gt;#   my $wheel = $#odometer;    # start at rightmost wheel&lt;br /&gt;#   until ($odometer[$wheel] &lt; $#odometer-$wheel || $wheel &lt; 0) {&lt;br /&gt;#     $odometer[$wheel] = 0;&lt;br /&gt;#     $wheel--; # next wheel to the left&lt;br /&gt;#   }&lt;br /&gt;#   if ($wheel &lt; 0) {&lt;br /&gt;#     return;   # fell off the left end; no more sequences&lt;br /&gt;#   } else {&lt;br /&gt;#     $odometer[$wheel]++; # this wheel now turns one notch&lt;br /&gt;#     return @odometer;&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def increment_pattern(odometer):&lt;br /&gt;    wheel = len(odometer) - 1&lt;br /&gt;    while not (odometer[wheel] &lt; (len(odometer)-1-wheel) or wheel &lt; 0):&lt;br /&gt;        odometer[wheel] = 0&lt;br /&gt;        wheel -= 1&lt;br /&gt;    if wheel &lt; 0:&lt;br /&gt;        return&lt;br /&gt;    else:&lt;br /&gt;        odometer[wheel] += 1&lt;br /&gt;        return odometer&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub n_to_pat {&lt;br /&gt;#   my @odometer;&lt;br /&gt;#   my ($n, $length) = @_;&lt;br /&gt;#   for my $i (1 .. $length) {&lt;br /&gt;#     unshift @odometer, $n % $i;&lt;br /&gt;#     $n = int($n/$i);&lt;br /&gt;#   }&lt;br /&gt;#   return $n ? () : @odometer;&lt;br /&gt;# }&lt;br /&gt;def n_to_pat(n, length):&lt;br /&gt;    odometer = []&lt;br /&gt;    for i in range(1, length+1):&lt;br /&gt;        odometer.insert(0, n % i)&lt;br /&gt;        n = n / i&lt;br /&gt;    return not n and odometer or []&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub permute {&lt;br /&gt;#   my @items = @_;&lt;br /&gt;#   my $n = 0;&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     my @pattern = n_to_pat($n, scalar(@items));&lt;br /&gt;#     my @result = pattern_to_permutation(\@pattern, \@items);&lt;br /&gt;#     $n++;&lt;br /&gt;#     return @result;&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;def permute(items):&lt;br /&gt;    n = 0&lt;br /&gt;    while 1:&lt;br /&gt;        pattern = n_to_pat(n, len(items))&lt;br /&gt;        if not pattern:&lt;br /&gt;            break&lt;br /&gt;        result = pattern_to_permutation(pattern, items)&lt;br /&gt;        yield result&lt;br /&gt;        n += 1&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub iterate_function {&lt;br /&gt;#   my $n = 0;&lt;br /&gt;#   my $f = shift;&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     return $f-&gt;($n++);&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;def iterate_function(f):&lt;br /&gt;    n = 0&lt;br /&gt;    while 1:&lt;br /&gt;        yield f(n)&lt;br /&gt;        n += 1&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub permute {&lt;br /&gt;#   my @items = @_;&lt;br /&gt;#   my $n = 0;&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     $n++, return @items if $n==0;&lt;br /&gt;#     my $i;&lt;br /&gt;#     my $p = $n;&lt;br /&gt;#     for ($i=1; $i&lt;=@items &amp;&amp; $p%$i==0; $i++) {&lt;br /&gt;#       $p /= $i;&lt;br /&gt;#     }&lt;br /&gt;#     my $d = $p % $i;&lt;br /&gt;#     my $j = @items - $i;&lt;br /&gt;#     return if $j &lt; 0;&lt;br /&gt;#     @items[$j+1..$#items] = reverse @items[$j+1..$#items];&lt;br /&gt;#     @items[$j,$j+$d] = @items[$j+$d,$j];&lt;br /&gt;#     $n++;&lt;br /&gt;#     return @items;&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt; def permute(_items):&lt;br /&gt;    n = 0&lt;br /&gt;    items = _items[:]&lt;br /&gt;&lt;br /&gt;    if n == 0:&lt;br /&gt;        yield items&lt;br /&gt;&lt;br /&gt;    n += 1&lt;br /&gt;    while 1:&lt;br /&gt;        # make a copy so list(permute(my_list)) returns n copies of same item&lt;br /&gt;        # otherwise can remove&lt;br /&gt;        items = items[:] &lt;br /&gt;        i = 1&lt;br /&gt;        p = n&lt;br /&gt;        while i &lt;= len(items)+1 and p % i == 0:&lt;br /&gt;            p /= i&lt;br /&gt;            i += 1&lt;br /&gt;        d = p % i&lt;br /&gt;        j = len(items) - i &lt;br /&gt;&lt;br /&gt;        if j &lt; 0:&lt;br /&gt;            return&lt;br /&gt;&lt;br /&gt;        items[j+1:len(items)] = reversed(items[j+1:len(items)])&lt;br /&gt;        x,y = items[j+d], items[j]&lt;br /&gt;        items[j] = x&lt;br /&gt;        items[j+d] = y &lt;br /&gt;        n += 1&lt;br /&gt;&lt;br /&gt;        yield items&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub make_genes {&lt;br /&gt;#   my $pat = shift;&lt;br /&gt;#   my @tokens = split /[()]/, $pat;&lt;br /&gt;#   for (my $i = 1; $i &lt; @tokens; $i += 2) {&lt;br /&gt;#     $tokens[$i] = [0, split(//, $tokens[$i])];&lt;br /&gt;#   }&lt;br /&gt;#   my $FINISHED = 0;&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     return if $FINISHED;&lt;br /&gt;#     my $finished_incrementing = 0;&lt;br /&gt;#     my $result = "";&lt;br /&gt;#     for my $token (@tokens) {&lt;br /&gt;#       if (ref $token eq "") {    # plain string&lt;br /&gt;#         $result .= $token;&lt;br /&gt;#       } else {                   # wildcard&lt;br /&gt;#         my ($n, @c) = @$token;&lt;br /&gt;#         $result .= $c[$n];&lt;br /&gt;#         unless ($finished_incrementing) {&lt;br /&gt;#           if ($n == $#c) { $token-&gt;[0] = 0 }&lt;br /&gt;#           else { $token-&gt;[0]++; $finished_incrementing = 1 }&lt;br /&gt;#         }&lt;br /&gt;#       }&lt;br /&gt;#     }&lt;br /&gt;#     $FINISHED = 1 unless $finished_incrementing;&lt;br /&gt;#     return $result;&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def make_genes(pat):&lt;br /&gt;    tokens = re.split("[()]",pat)&lt;br /&gt;&lt;br /&gt;    for i in range(len(tokens))[1::2]:&lt;br /&gt;        tokens[i] = [0] + list(tokens[i])&lt;br /&gt;&lt;br /&gt;    FINISHED = False&lt;br /&gt;    while not FINISHED:&lt;br /&gt;        finished_incrementing = False&lt;br /&gt;        result = ""&lt;br /&gt;        for token in tokens:&lt;br /&gt;            if token.__class__ is str:&lt;br /&gt;                result += token&lt;br /&gt;            else:&lt;br /&gt;                n, c = token[0], token[1:]&lt;br /&gt;                result += c[n]&lt;br /&gt;                if not finished_incrementing:&lt;br /&gt;                    if n == len(c) - 1:&lt;br /&gt;                        token[0] = 0&lt;br /&gt;                    else:&lt;br /&gt;                        token[0] += 1&lt;br /&gt;                        finished_incrementing = True&lt;br /&gt;        if not finished_incrementing:&lt;br /&gt;            FINISHED = True&lt;br /&gt;        yield result&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# %n_expand = qw(N ACGT &lt;br /&gt;#                B CGT D AGT H ACT V ACG&lt;br /&gt;#                K GT M AC R AG S CG W AT Y CT);&lt;br /&gt;# sub make_dna_sequences {&lt;br /&gt;#   my $pat = shift;&lt;br /&gt;#   for my $abbrev (keys %n_expand) {&lt;br /&gt;#     $pat =~ s/$abbrev/($n_expand{$abbrev})/g;&lt;br /&gt;#   }&lt;br /&gt;#   return make_genes($pat);&lt;br /&gt;# }&lt;br /&gt;n_expand = {"N" : "ACGT",&lt;br /&gt;            "B" : "CGT", "D" : "AGT", "H" : "ACT", "V" : "ACG",&lt;br /&gt;            "K" : "GT",  "M" : "AC",  "R" : "AG",  "S" : "CG",  "W" : "AT", "Y" : "CT"}&lt;br /&gt;def make_dna_sequences(pat):&lt;br /&gt;    for abbrev in n_expand:&lt;br /&gt;        pat = re.sub(abbrev, n_expand[abbrev], pat)&lt;br /&gt;    &lt;br /&gt;    return make_genes(pat)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub filehandle_iterator {&lt;br /&gt;#   my $fh = shift;&lt;br /&gt;#   return Iterator { &lt;$fh&gt; };&lt;br /&gt;# }&lt;br /&gt;&lt;br /&gt;# my $it = filehandle_iterator(*STDIN);&lt;br /&gt;# while (defined(my $line = NEXTVAL($it))) {&lt;br /&gt;#   # do something with $line&lt;br /&gt;# }&lt;br /&gt;### python already does this by default&lt;br /&gt;for line in file("foo"):&lt;br /&gt;    # do something with line&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# LASTNAME:FIRSTNAME:CITY:STATE:OWES&lt;br /&gt;# Adler:David:New York:NY:157.00&lt;br /&gt;# Ashton:Elaine:Boston:MA:0.00&lt;br /&gt;# Dominus:Mark:Philadelphia:PA:0.00&lt;br /&gt;# Orwant:Jon:Cambridge:MA:26.30&lt;br /&gt;# Schwern:Michael:New York:NY:149658.23&lt;br /&gt;# Wall:Larry:Mountain View:CA:-372.14&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# package FlatDB;&lt;br /&gt;# my $FIELDSEP = qr/:/;&lt;br /&gt;# sub new {&lt;br /&gt;#   my $class = shift;&lt;br /&gt;#   my $file = shift;&lt;br /&gt;#   open my $fh, "&lt;", $file or return;&lt;br /&gt;#   chomp(my $schema = &lt;$fh&gt;);&lt;br /&gt;&lt;br /&gt;#   my @field = split $FIELDSEP, $schema;&lt;br /&gt;#   my %fieldnum = map { uc $field[$_] =&gt; $_ } (0..$#field);&lt;br /&gt;#   bless { FH =&gt; $fh, FIELDS =&gt; \@field, FIELDNUM =&gt; \%fieldnum,&lt;br /&gt;#           FIELDSEP =&gt; $FIELDSEP } =&gt; $class;&lt;br /&gt;# }&lt;br /&gt;class FlatDB(object):&lt;br /&gt;    FIELDSEP = ":"&lt;br /&gt;&lt;br /&gt;    def __init__(self, _file):&lt;br /&gt;        self._file = _file&lt;br /&gt;        self.fh = file(self._file)&lt;br /&gt;        self.schema = self.fh.readline().strip()&lt;br /&gt;        self.field = self.schema.split(FlatDB.FIELDSEP)&lt;br /&gt;        self.fieldnum = dict(zip([x.upper() for x in self.field], range(len(self.field))))&lt;br /&gt;  &lt;br /&gt;# # usage: $dbh-&gt;query(fieldname, value)&lt;br /&gt;# # returns all records for which (fieldname) matches (value)&lt;br /&gt;# use Fcntl ':seek';&lt;br /&gt;# sub query {&lt;br /&gt;#   my $self = shift;&lt;br /&gt;#   my ($field, $value) = @_;&lt;br /&gt;#   my $fieldnum = $self-&gt;{FIELDNUM}{uc $field};&lt;br /&gt;#   return unless defined $fieldnum;&lt;br /&gt;#   my $fh = $self-&gt;{FH};&lt;br /&gt;#   seek $fh, 0, SEEK_SET;&lt;br /&gt;#   &lt;$fh&gt;;                # discard schema line&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     local $_;&lt;br /&gt;#     while (&lt;$fh&gt;) {&lt;br /&gt;#       chomp;&lt;br /&gt;#       my @fields = split $self-&gt;{FIELDSEP}, $_, -1;&lt;br /&gt;#       my $fieldval = $fields[$fieldnum];&lt;br /&gt;#       return $_ if $fieldval eq $value;&lt;br /&gt;#     }&lt;br /&gt;#     return;&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;&lt;br /&gt;    def query(self, field, value):&lt;br /&gt;        fieldnum = self.fieldnum.get(field.upper())&lt;br /&gt;        if fieldnum == None:&lt;br /&gt;            return&lt;br /&gt;        fh = self.fh&lt;br /&gt;        fh.seek(0)&lt;br /&gt;        fh.readline() # discard schema line&lt;br /&gt;        for line in fh:&lt;br /&gt;            fields = line.split(FlatDB.FIELDSEP)&lt;br /&gt;            fieldval = fields[fieldnum]&lt;br /&gt;            if fieldval == value:&lt;br /&gt;                yield line.strip()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# use FlatDB;&lt;br /&gt;# my $dbh = FlatDB-&gt;new('db.txt') or die $!;&lt;br /&gt;# my $q = $dbh-&gt;query('STATE', 'NY');&lt;br /&gt;# while (my $rec = NEXTVAL($q)) {&lt;br /&gt;#   print $rec;&lt;br /&gt;# }&lt;br /&gt;dbh = FlatDB("db.txt")&lt;br /&gt;q = dbh.query("STATE", "NY")&lt;br /&gt;for rec in q:&lt;br /&gt;    print rec&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# my $q = $dbh-&gt;callbackquery(sub { my %F=@_; $F{OWES} &gt; 10 });&lt;br /&gt;# my $q = $dbh-&gt;callbackquery(sub { my %F=@_; $F{FIRSTNAME} =~ /ˆM/ });&lt;br /&gt;&lt;br /&gt;# use Fcntl ':seek';&lt;br /&gt;# sub callbackquery {&lt;br /&gt;#   my $self = shift;&lt;br /&gt;#   my $is_interesting = shift;&lt;br /&gt;#   my $fh = $self-&gt;{FH};&lt;br /&gt;#   seek $fh, 0, SEEK_SET;&lt;br /&gt;#   &lt;$fh&gt;;                # discard header line&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     local $_;&lt;br /&gt;#     while (&lt;$fh&gt;) {&lt;br /&gt;#       chomp;&lt;br /&gt;#        my %F;&lt;br /&gt;#        my @fieldnames = @{$self-&gt;{FIELDS}};&lt;br /&gt;#        my @fields = split $self-&gt;{FIELDSEP};&lt;br /&gt;#        for (0 .. $#fieldnames) {&lt;br /&gt;#          $F{$fieldnames[$_]} = $fields[$_];&lt;br /&gt;#        }&lt;br /&gt;#        return $_ if $is_interesting-&gt;(%F);&lt;br /&gt;#     }&lt;br /&gt;#     return;&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;&lt;br /&gt;q = dbh.callbackquery(lambda F: F["OWES"] &gt; 10)&lt;br /&gt;q = dbh.callbackquery(lambda F: F["FIRSTNAME"].startswith("M") )&lt;br /&gt;&lt;br /&gt;def callbackquery(self, is_interesting):&lt;br /&gt;    fh = self.fh&lt;br /&gt;    fh.seek(0)&lt;br /&gt;    fh.readline() # discard schema line&lt;br /&gt;    for line in fh:&lt;br /&gt;        line = line.strip()&lt;br /&gt;        fieldnames = self.field&lt;br /&gt;        fields = line.split(FlatDB.FIELDSEP)&lt;br /&gt;        F = dict(zip(fieldnames, fields))&lt;br /&gt;        if is_interesting(F):&lt;br /&gt;            yield line&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# use FlatDB;&lt;br /&gt;# my $dbh = FlatDB-&gt;new('db.txt') or die $!;&lt;br /&gt;# my $q1 = $dbh-&gt;query('STATE', 'MA');&lt;br /&gt;# my $q2 = $dbh-&gt;query('STATE', 'NY');&lt;br /&gt;# for (1..2) {&lt;br /&gt;#   print NEXTVAL($q1), NEXTVAL($q2);&lt;br /&gt;# }&lt;br /&gt;dbh = FlatDB("db.txt")&lt;br /&gt;q1 = dbh.query("STATE","MA")&lt;br /&gt;q2 = dbh.query("STATE","NY")&lt;br /&gt;for x in range(1,3):&lt;br /&gt;    print q1.next(), q2.next()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# # usage: $dbh-&gt;query(fieldname, value)&lt;br /&gt;# # returns all records for which (fieldname) matches (value)&lt;br /&gt;# use Fcntl ':seek';&lt;br /&gt;# sub query {&lt;br /&gt;#   my $self = shift;&lt;br /&gt;#   my ($field, $value) = @_;&lt;br /&gt;#   my $fieldnum = $self-&gt;{FIELDNUM}{uc $field};&lt;br /&gt;#   return unless defined $fieldnum;&lt;br /&gt;#   my $fh = $self-&gt;{FH};&lt;br /&gt;#   seek $fh, 0, SEEK_SET;&lt;br /&gt;#   &lt;$fh&gt;;                # discard header line&lt;br /&gt;#   my $position = tell $fh;&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     local $_;&lt;br /&gt;#     seek $fh, $position, SEEK_SET;&lt;br /&gt;#     while (&lt;$fh&gt;) {&lt;br /&gt;#       chomp;&lt;br /&gt;#       $position = tell $fh;&lt;br /&gt;#       my @fields = split $self-&gt;{FIELDSEP};&lt;br /&gt;#       my $fieldval = $fields[$fieldnum];&lt;br /&gt;#       return $_ if $fieldval eq $value;&lt;br /&gt;#     }&lt;br /&gt;#     return;&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;# # callbackquery with bug fix&lt;br /&gt;# use Fcntl ':seek';&lt;br /&gt;# sub callbackquery {&lt;br /&gt;#   my $self = shift;&lt;br /&gt;#   my $is_interesting = shift;&lt;br /&gt;#   my $fh = $self-&gt;{FH};&lt;br /&gt;#   seek $fh, 0, SEEK_SET;&lt;br /&gt;#   &lt;$fh&gt;;                # discard header line&lt;br /&gt;#   my $position = tell $fh;&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     local $_;&lt;br /&gt;#     seek $fh, $position, SEEK_SET;&lt;br /&gt;#     while (&lt;$fh&gt;) {&lt;br /&gt;#       $position = tell $fh;&lt;br /&gt;#       my %F;&lt;br /&gt;#       my @fieldnames = @{$self-&gt;{FIELDS}};&lt;br /&gt;#       my @fields = split $self-&gt;{FIELDSEP};&lt;br /&gt;#       for (0 .. $#fieldnames) {&lt;br /&gt;#         $F{$fieldnames[$_]} = $fields[$_];&lt;br /&gt;#       }&lt;br /&gt;#       return $_ if $is_interesting-&gt;(%F);&lt;br /&gt;&lt;br /&gt;#     }&lt;br /&gt;#     return;&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;# 1;&lt;br /&gt;&lt;br /&gt;class FlatDB(object):&lt;br /&gt;    FIELDSEP = ":"&lt;br /&gt;&lt;br /&gt;    def __init__(self, _file):&lt;br /&gt;        self._file = _file&lt;br /&gt;        self.fh = file(self._file)&lt;br /&gt;        self.schema = self.fh.readline().strip()&lt;br /&gt;        self.field = self.schema.split(FlatDB.FIELDSEP)&lt;br /&gt;        self.fieldnum = dict(zip([x.upper() for x in self.field], range(len(self.field))))&lt;br /&gt;&lt;br /&gt;    def query(self, field, value):&lt;br /&gt;        fieldnum = self.fieldnum.get(field.upper())&lt;br /&gt;        if fieldnum == None:&lt;br /&gt;            return&lt;br /&gt;        fh = self.fh&lt;br /&gt;        fh.seek(0)&lt;br /&gt;        fh.readline() # discard schema line&lt;br /&gt;        while 1:&lt;br /&gt;            line = fh.readline()&lt;br /&gt;            if not line:&lt;br /&gt;                break&lt;br /&gt;            position = fh.tell()&lt;br /&gt;            fields = line.split(FlatDB.FIELDSEP)&lt;br /&gt;            fieldval = fields[fieldnum]&lt;br /&gt;            if fieldval == value:&lt;br /&gt;                yield line.strip()&lt;br /&gt;            fh.seek(position)&lt;br /&gt;&lt;br /&gt;    def callbackquery(self, is_interesting):&lt;br /&gt;        fh = self.fh&lt;br /&gt;        fh.seek(0)&lt;br /&gt;        fh.readline() # discard schema line&lt;br /&gt;        while 1:&lt;br /&gt;            line = fh.readline()&lt;br /&gt;            if not line:&lt;br /&gt;                break&lt;br /&gt;            position = fh.tell()&lt;br /&gt;            line = line.strip()&lt;br /&gt;            fieldnames = self.field&lt;br /&gt;            fields = line.split(FlatDB.FIELDSEP)&lt;br /&gt;            F = dict(zip(fieldnames, fields))&lt;br /&gt;            if is_interesting(F):&lt;br /&gt;                yield line&lt;br /&gt;            fh.seek(position)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# package FlatDB::Iterator;&lt;br /&gt;# my $FIELDSEP = qr/\s+/;&lt;br /&gt;# sub new {&lt;br /&gt;#   my $class = shift;&lt;br /&gt;#   my $it = shift;&lt;br /&gt;#   my @field = @_;&lt;br /&gt;#   my %fieldnum = map { uc $field[$_] =&gt; $_ } (0..$#field);&lt;br /&gt;#   bless { FH =&gt; $it, FIELDS =&gt; \@field, FIELDNUM =&gt; \%fieldnum,&lt;br /&gt;class IterFlatDB(object):&lt;br /&gt;    FIELDSEP = "\s+"&lt;br /&gt;&lt;br /&gt;    def __init__(self, it, *field):&lt;br /&gt;        self.it = it&lt;br /&gt;        self.field = field&lt;br /&gt;        self.fieldnum = dict(zip([x.upper() for x in self.field], range(len(self.field))))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# FlatDB::Iterator-&gt;new(&lt;br /&gt;#   $iterator,&lt;br /&gt;#   qw(address rfc931 username datetime tz method page protocol&lt;br /&gt;#      status bytes referrer agent)&lt;br /&gt;# );&lt;br /&gt;IterFlatDB(iterator, &lt;br /&gt;           "address rfc931 username datetime tz method page protocol status bytes referrer agent".split())&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# # usage: $dbh-&gt;query(fieldname, value)&lt;br /&gt;# # returns all records for which (fieldname) matches (value)&lt;br /&gt;# sub query {&lt;br /&gt;#   my $self = shift;&lt;br /&gt;#   my ($field, $value) = @_;&lt;br /&gt;#   my $fieldnum = $self-&gt;{FIELDNUM}{uc $field};&lt;br /&gt;#   return unless defined $fieldnum;&lt;br /&gt;#   my $it = $self-&gt;{FH};&lt;br /&gt;#   # seek $fh, 0, SEEK_SET;&lt;br /&gt;#   # &lt;$fh&gt;;                # discard header line&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     local $_;&lt;br /&gt;#     while (defined ($_ = NEXTVAL($it))) {&lt;br /&gt;#       my @fields = split $self-&gt;{FIELDSEP};&lt;br /&gt;#       my $fieldval = $fields[$fieldnum];&lt;br /&gt;#       return $_ if $fieldval eq $value;&lt;br /&gt;#     }&lt;br /&gt;#     return;&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;def query(self, field, value):&lt;br /&gt;    fieldnum = self.fieldnum.get(field.upper())&lt;br /&gt;    if fieldnum == None:&lt;br /&gt;        return&lt;br /&gt;&lt;br /&gt;    for record in self.it:&lt;br /&gt;        fields = re.split(IterFlatDB.FIELDSEP, record)&lt;br /&gt;        fieldval = fields[fieldnum]&lt;br /&gt;        if fieldval == value:&lt;br /&gt;            yield record&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# my $qit =&lt;br /&gt;#   FlatDB::Iterator-&gt;new($it, @FIELDNAMES)-&gt;query($field, $value);&lt;br /&gt;qit = IterFlatDB(it, FIELDNAMES).query(field, value)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub readbackwards {&lt;br /&gt;#   my $file = shift;&lt;br /&gt;#   open my($fh), "|-", "tac", $file&lt;br /&gt;#     or return;&lt;br /&gt;#   return Iterator { return scalar(&lt;$fh&gt;) };&lt;br /&gt;# }&lt;br /&gt;def readbackwards(_file):&lt;br /&gt;    return os.popen("tac %s" % _file)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# my @fields = qw(address rfc931 username datetime tz method&lt;br /&gt;#                 page protocol status bytes referrer agent);&lt;br /&gt;# my $logfile = readbackwards("/usr/local/apache/logs/access-log")&lt;br /&gt;# my $db = FlatDB::Iterator-&gt;new($logfile, @fields);&lt;br /&gt;# my $q = $db-&gt;callbackquery(sub {my %F=@_; $F{PAGE}=~ m{/book/$}});&lt;br /&gt;# while (1) {&lt;br /&gt;#   for (1..10) {&lt;br /&gt;#     print NEXTVAL($q);&lt;br /&gt;#   }&lt;br /&gt;#   print "q to quit; CR to continue\n";&lt;br /&gt;#   chomp(my $resp = &lt;STDIN&gt;);&lt;br /&gt;#   last if $resp =~ /q/i;&lt;br /&gt;# }&lt;br /&gt;fields = "address rfc931 username datetime tz method page protocol status bytes referrer agent"&lt;br /&gt;logfile = readbackwards("/var/log/apache2/access.log")&lt;br /&gt;db = IterFlatDB(logfile, fields.split())&lt;br /&gt;q = db.callbackquery(lambda F: re.search("/book/$", f["PAGE" ]))&lt;br /&gt;&lt;br /&gt;while 1:&lt;br /&gt;    for line in itertools.islice(q, 10):&lt;br /&gt;        print line&lt;br /&gt;    print "q to quit; CR to continue"&lt;br /&gt;    if raw_input() == 'q':&lt;br /&gt;        break&lt;br /&gt;        &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# my $seed = 1;&lt;br /&gt;# sub Rand {&lt;br /&gt;#   $seed = (27*$seed+11111) &amp; 0x7fff;&lt;br /&gt;#   return $seed;&lt;br /&gt;# }&lt;br /&gt;seed = 1&lt;br /&gt;def Rand():&lt;br /&gt;    global seed&lt;br /&gt;    seed = (27*seed+11111) &amp; 0x7fff&lt;br /&gt;    return seed&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub SRand {&lt;br /&gt;#   $seed = shift;&lt;br /&gt;# }&lt;br /&gt;def SRand(_seed):&lt;br /&gt;    global seed&lt;br /&gt;    seed = _seed&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# SRand($$);&lt;br /&gt;SRand(os.getpid())&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# use CGI::Push;&lt;br /&gt;# my $seed = shift || $$ ;&lt;br /&gt;# srand($seed);&lt;br /&gt;# open LOG, "&gt; $logfile" or die ... ;&lt;br /&gt;# print LOG "Random seed: $seed\n";&lt;br /&gt;# do_push(...);&lt;br /&gt;if len(sys.argv &gt; 1):&lt;br /&gt;    seed = int(sys.argv[1])&lt;br /&gt;else: &lt;br /&gt;    seed = os.getpid()&lt;br /&gt;srand(seed)&lt;br /&gt;LOG = open(logfile, "w")&lt;br /&gt;LOG.write("Random seed: " + str(seed))&lt;br /&gt;do_push(...)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# use Foo;&lt;br /&gt;# while (&lt;&gt;) {&lt;br /&gt;#   my $random = Rand();&lt;br /&gt;#   # do something with $random&lt;br /&gt;#   foo();&lt;br /&gt;# }&lt;br /&gt;import Foo&lt;br /&gt;for line in sys.stdin:&lt;br /&gt;    random = Rand()&lt;br /&gt;    # do something with random&lt;br /&gt;    Foo.foo()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub make_rand {&lt;br /&gt;#   my $seed = shift || (time &amp; 0x7fff);&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     $seed = (29*$seed+11111) &amp; 0x7fff;&lt;br /&gt;#     return $seed;&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def make_rand(seed=None):&lt;br /&gt;    if seed == None:&lt;br /&gt;        seed = int(time.time()) &amp; 0x7fff&lt;br /&gt;    while 1:&lt;br /&gt;        seed = (29*seed+11111) &amp; 0x7fff&lt;br /&gt;        yield seed&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# use Foo;&lt;br /&gt;# my $rng = make_rand();&lt;br /&gt;# while (&lt;&gt;) {&lt;br /&gt;#   my $random = NEXTVAL($rng);&lt;br /&gt;#   # do something with $random&lt;br /&gt;#   foo();&lt;br /&gt;# }&lt;br /&gt;import Foo&lt;br /&gt;rng = make_rand()&lt;br /&gt;for line in sys.stdin:&lt;br /&gt;    random = rng.next()&lt;br /&gt;    # do something with randome&lt;br /&gt;    Foo.foo()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;### 4.4 Filters and Transforms&lt;br /&gt;&lt;br /&gt;# sub imap {&lt;br /&gt;#   my ($transform, $it) = @_;&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     my $next = NEXTVAL($it);&lt;br /&gt;#     return unless defined $next;&lt;br /&gt;#     return $transform-&gt;($next);&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;&lt;br /&gt;# itertools.imap does this already&lt;br /&gt;def imap(transform, it):&lt;br /&gt;    for next in it:&lt;br /&gt;        yield transform(next)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# my $rng = imap(sub { $_[0] / 37268 }, make_rand());&lt;br /&gt;rng = imap(lambda x: float(x)/37268, make_rand())&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub imap (&amp;$) {&lt;br /&gt;#   my ($transform, $it) = @_;&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     my $next = NEXTVAL($it);&lt;br /&gt;#     return unless defined $next;&lt;br /&gt;#     return $transform-&gt;($next);&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;&lt;br /&gt;# my $rng = imap { $_[0] / 37268 } make_rand();&lt;br /&gt;&lt;br /&gt;# sub imap (&amp;$) {&lt;br /&gt;#   my ($transform, $it) = @_;&lt;br /&gt;#   return Iterator {&lt;br /&gt;#      local $_ = NEXTVAL($it);&lt;br /&gt;#      return unless defined $_;&lt;br /&gt;#      return $transform-&gt;();&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;&lt;br /&gt;# these are irrelevant changes for python&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub igrep (&amp;$) {&lt;br /&gt;#   my ($is_interesting, $it) = @_;&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     local $_;&lt;br /&gt;#     while (defined ($_ = NEXTVAL($it))) {&lt;br /&gt;#       return $_ if $is_interesting-&gt;();&lt;br /&gt;#     }&lt;br /&gt;#     return;&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def igrep(is_interesting, it):&lt;br /&gt;    for x in it:&lt;br /&gt;        if is_interesting(x):&lt;br /&gt;            yield x&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# # instead of         my $next_octopus =&lt;br /&gt;# #    interesting_files(\&amp;contains_octopuses, 'uploads', 'downloads' ;&lt;br /&gt;#                                                                    )&lt;br /&gt;# my $next_octopus = igrep { contains_octopuses($_) }&lt;br /&gt;#                        dir_walk('uploads', 'downloads');&lt;br /&gt;# while ($file = NEXTVAL($next_octopus)) {&lt;br /&gt;#   # do something with the file&lt;br /&gt;# }&lt;br /&gt;for _file in igrep(contains_octopuses, dir_walk("uploads", "downloads")):&lt;br /&gt;    # do something with the file&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub list_iterator {&lt;br /&gt;#   my @items = @_;&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     return shift @items;&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;def list_iterator(*args):&lt;br /&gt;    for x in args:&lt;br /&gt;        yield x&lt;br /&gt;# or just&lt;br /&gt;iter(args)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub append {&lt;br /&gt;#   my @its = @_;&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     while (@its) {&lt;br /&gt;#     my $val = NEXTVAL($its[0]);&lt;br /&gt;#     return $val if defined $val;&lt;br /&gt;#     shift @its;  # Discard exhausted iterator&lt;br /&gt;#   }&lt;br /&gt;#   return;&lt;br /&gt;# };&lt;br /&gt;def append(its):&lt;br /&gt;    for it in its:&lt;br /&gt;        for x in it:&lt;br /&gt;            yield x&lt;br /&gt;# or just&lt;br /&gt;itertools.chain(*its)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;### 4.5 The Semipredicate Problem&lt;br /&gt;&lt;br /&gt;# this whole section is irrelevant due to how &lt;br /&gt;# python uses iterators/generators &lt;br /&gt;# so i skipped it.  it someone sees something&lt;br /&gt;# in here that deserves a python translation&lt;br /&gt;# let me know&lt;br /&gt;&lt;br /&gt;### 4.6 Alternative Interfaces to Iterators&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub equal_arrays (\@\@) {&lt;br /&gt;#   my ($x, $y) = @_;&lt;br /&gt;#   return unless @$x == @$y;     # arrays are the same length?&lt;br /&gt;#   for my $i (0 .. $#$x) {&lt;br /&gt;#     return unless $x-&gt;[$i] eq $y-&gt;[$i];   # mismatched elements&lt;br /&gt;#   }&lt;br /&gt;#   return 1;                     # arrays are equal&lt;br /&gt;# }&lt;br /&gt;def equal_arrays(x,y):&lt;br /&gt;    if len(x) != len(y):&lt;br /&gt;        return False&lt;br /&gt;    for i in range(len(x)):&lt;br /&gt;        if x[i] != y[i]:&lt;br /&gt;            return False&lt;br /&gt;    return True&lt;br /&gt;# but this is unnecessary since we can already do&lt;br /&gt;x == y # in place&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub equal_arrays (\@\@) {&lt;br /&gt;#   my ($x, $y) = @_&lt;br /&gt;#   return unless @$x == @$y;&lt;br /&gt;#   my $xy = each_array(@_ );&lt;br /&gt;#   while (my ($xe, $ye) = NEXTVAL($xy)) {&lt;br /&gt;#     return unless $xe eq $ye;&lt;br /&gt;#   }&lt;br /&gt;#   return 1;&lt;br /&gt;# }&lt;br /&gt;def equal_arrays(x,y):&lt;br /&gt;    if len(x) != len(y):&lt;br /&gt;        return False&lt;br /&gt;    &lt;br /&gt;    xy = each_array(x,y)&lt;br /&gt;    for xe,ye in xy:&lt;br /&gt;        if xe != ye:&lt;br /&gt;            return False&lt;br /&gt;    return True&lt;br /&gt;    &lt;br /&gt;&lt;br /&gt;# sub each_array {&lt;br /&gt;#   my @arrays = @_;&lt;br /&gt;#   my $cur_elt = 0;&lt;br /&gt;#   my $max_size = 0;&lt;br /&gt;#   # Get the length of the longest input array&lt;br /&gt;#   for (@arrays) {&lt;br /&gt;#     $max_size = @$_ if @$_ &gt; $max_size;&lt;br /&gt;#   }&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     $cur_elt = 0, return () if $cur_elt &gt;= $max_size;&lt;br /&gt;#     my $i = $cur_elt++;&lt;br /&gt;#     return map $_-&gt;[$i], @arrays;&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;def each_array(*arrays):&lt;br /&gt;    max_size = max(*[len(ar) for ar in arrays])&lt;br /&gt;    &lt;br /&gt;    def get_item(ar, i):&lt;br /&gt;        if i &lt; len(ar):&lt;br /&gt;            return ar[i]&lt;br /&gt;        return None&lt;br /&gt;&lt;br /&gt;    for i in range(max_size):&lt;br /&gt;        yield [get_item(ar, i) for ar in arrays]&lt;br /&gt;# you could also probably do something clever with itertools.izip() &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# my $buttons = each_array(\@labels, \@values);&lt;br /&gt;# ...&lt;br /&gt;# while (my ($label, $value) = NEXTVAL($buttons)) {&lt;br /&gt;#   print HTML qq{&lt;input type=radio value="$value"&gt; $label&lt;br /&gt;\n};&lt;br /&gt;# }&lt;br /&gt;buttons = each_array(labels, values)&lt;br /&gt;for label, value in buttons:&lt;br /&gt;    HTML.write("&lt;input type=radio value="%(value)s"&gt; %(label)s&lt;br /&gt;\n" % locals())&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub each_array {&lt;br /&gt;#   my @arrays    = @_;&lt;br /&gt;#   my $stop_type = ref $arrays[0] ? 'maximum' : shift @arrays;&lt;br /&gt;#   my $stop_size = @{$arrays[0]};&lt;br /&gt;#   my $cur_elt   = 0;&lt;br /&gt;#   # Get the length of the longest (or shortest) input array&lt;br /&gt;#   if ($stop_type eq 'maximum') {&lt;br /&gt;#     for (@arrays) {&lt;br /&gt;#       $stop_size = @$_ if @$_ &gt; $stop_size;&lt;br /&gt;#     }&lt;br /&gt;#   } elsif ($stop_type eq 'minimum') {&lt;br /&gt;#     for (@arrays) {&lt;br /&gt;#       $stop_size = @$_ if @$_ &lt; $stop_size;&lt;br /&gt;#     }&lt;br /&gt;#   } else {&lt;br /&gt;#     croak "each_array: unknown stopping behavior '$stop_type'";&lt;br /&gt;#   }&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     return ()  if $cur_elt &gt;= $stop_size;&lt;br /&gt;#     my $i = $cur_elt++;&lt;br /&gt;#     return map $_-&gt;[$i], @arrays;&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;def each_array(arrays, stop_type="maximum"):&lt;br /&gt;    assert stop_type in ("minimum", "maximum")&lt;br /&gt;    &lt;br /&gt;    if stop_type == "minimum":&lt;br /&gt;        stop_size = min(*[len(ar) for ar in arrays])&lt;br /&gt;    else:&lt;br /&gt;        stop_size = max(*[len(ar) for ar in arrays])&lt;br /&gt;    &lt;br /&gt;    def get_item(ar, i):&lt;br /&gt;        if i &lt; len(ar):&lt;br /&gt;            return ar[i]&lt;br /&gt;        return None&lt;br /&gt;&lt;br /&gt;    for i in range(stop_size):&lt;br /&gt;        yield [get_item(ar, i) for ar in arrays]&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub eachlike (&amp;$) {&lt;br /&gt;#   my ($transform, $it) = @_;&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     local $_ = NEXTVAL($it);&lt;br /&gt;#     return unless defined $_;&lt;br /&gt;#     my $value = $transform-&gt;();&lt;br /&gt;#     return wantarray ? ($_, $value) : $value;&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;# not sure if wantarray really maps to python&lt;br /&gt;# style&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# package CIA;&lt;br /&gt;# sub TIESCALAR {&lt;br /&gt;#   my $package = shift;&lt;br /&gt;#   my $self = {};&lt;br /&gt;#   bless $self =&gt; $package;&lt;br /&gt;# }&lt;br /&gt;# sub STORE { }&lt;br /&gt;# sub FETCH { "&lt;&lt;Access forbidden&gt;&gt;" }&lt;br /&gt;&lt;br /&gt;# tie $secret, 'CIA';&lt;br /&gt;&lt;br /&gt;# $secret = 'atomic ray';&lt;br /&gt;&lt;br /&gt;# print "The secret weapon is '$secret'.\n"&lt;br /&gt;&lt;br /&gt;# the secret weapon is '&lt;&lt;Access forbidden&gt;&gt;'.&lt;br /&gt;&lt;br /&gt;# I can't think of any reasonable way to do&lt;br /&gt;# this in python.  In part it seems like something&lt;br /&gt;# you could handle with descriptor and in part&lt;br /&gt;# with "with".  I'm just going to ignore TIE-ing&lt;br /&gt;# for now&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;### 4.7 An Extended Example: Web Spiders&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# use HTML::LinkExtor;&lt;br /&gt;# use LWP::Simple;&lt;br /&gt;# sub traverse {&lt;br /&gt;#   my @queue = @_;&lt;br /&gt;#   my %seen;&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     while (@queue) {&lt;br /&gt;#       my $url = shift @queue;&lt;br /&gt;#       $url =~ s/#.*$//;&lt;br /&gt;#       next if $seen{$url}++;&lt;br /&gt;#       my ($content_type) = head($url);&lt;br /&gt;#       if ($content_type =~ m{ˆtext/html\b}) {&lt;br /&gt;#         my $html = get($url);&lt;br /&gt;#         push @queue, get_links($url, $html);&lt;br /&gt;#       }&lt;br /&gt;#       return $url;&lt;br /&gt;#     }&lt;br /&gt;#     return;     # exhausted&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;import urllib2    &lt;br /&gt;def traverse(_queue):&lt;br /&gt;    queue = _queue[:]&lt;br /&gt;    seen = {}&lt;br /&gt;    while queue:&lt;br /&gt;        url = queue.pop(0)&lt;br /&gt;        url = url.split("#")[0]&lt;br /&gt;        seen.setdefault(url,0)&lt;br /&gt;        if seen[url] &gt; 0:&lt;br /&gt;            continue&lt;br /&gt;        seen[url] += 1&lt;br /&gt;        try:&lt;br /&gt;            page = urllib2.urlopen(url)&lt;br /&gt;        except urllib2.HTTPError:&lt;br /&gt;            print "http error for:", url&lt;br /&gt;            continue&lt;br /&gt;        content_type = page.headers.getheader("content-type")&lt;br /&gt;        if re.search(r"^text/html\b", content_type):&lt;br /&gt;            html = page.read()&lt;br /&gt;            queue.extend(get_links(url, html))&lt;br /&gt;        yield url&lt;br /&gt;            &lt;br /&gt;&lt;br /&gt;# sub get_links {&lt;br /&gt;#   my ($base, $html) = @_;&lt;br /&gt;#   my @links;&lt;br /&gt;#   my $more_links = sub {&lt;br /&gt;#     my ($tag, %attrs) = @_;&lt;br /&gt;#     push @links, values %attrs;&lt;br /&gt;#   };&lt;br /&gt;#   HTML::LinkExtor-&gt;new($more_links, $base)-&gt;parse($html);&lt;br /&gt;#   return @links;&lt;br /&gt;# }&lt;br /&gt;# Off the top of my head I don't know a python library&lt;br /&gt;# that provides this exact functionality, so we &lt;br /&gt;# fake it.&lt;br /&gt;def get_links(base, html):&lt;br /&gt;    links = []&lt;br /&gt;&lt;br /&gt;    parsed = urlparse.urlparse(base)&lt;br /&gt;&lt;br /&gt;    for anchor in BeautifulSoup.BeautifulSoup(html)('a'):&lt;br /&gt;        link = anchor.get("href")&lt;br /&gt;        if not link:&lt;br /&gt;            continue&lt;br /&gt;&lt;br /&gt;        if link.startswith("./"):&lt;br /&gt;            link = link[2:]&lt;br /&gt;&lt;br /&gt;        if link.startswith("http"):&lt;br /&gt;            links.append(link)&lt;br /&gt;        elif link.startswith("/"):&lt;br /&gt;            links.append(parsed[0]+"://"+parsed[1]+link)&lt;br /&gt;        else:&lt;br /&gt;            links.append(parsed[0]+"://"+parsed[1]+parsed[2]+link)&lt;br /&gt;&lt;br /&gt;    return links&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# # Version with 'interesting links' callback&lt;br /&gt;# sub traverse {&lt;br /&gt;#   my $interesting_links = sub { @_ };&lt;br /&gt;#   $interesting_links = shift if ref $_[0] eq 'CODE';&lt;br /&gt;#   ...&lt;br /&gt;#         push @queue, $interesting_links-&gt;(get_links($url, $html));&lt;br /&gt;#   ...&lt;br /&gt;# }&lt;br /&gt;def traverse(queue, interesting_links=None):&lt;br /&gt;    ...&lt;br /&gt;    queue.extend(interesting_links(get_links(url, html)))&lt;br /&gt;    ...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# my $top = 'http://perl.plover.com/';&lt;br /&gt;# my $interesting = sub { grep /ˆ\Q$top/o, @_ };&lt;br /&gt;# my $urls = traverse($interesting, $top);&lt;br /&gt;top = "http://perl.plover.com"&lt;br /&gt;interesting = lambda x: top in x&lt;br /&gt;urls = traverse(interesting, top)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# use File::Basename;&lt;br /&gt;# while (my $url = NEXTVAL($urls)) {&lt;br /&gt;#   my $file = $url;&lt;br /&gt;#   $file =~ s/ˆ\Q$top//o;&lt;br /&gt;#   my $dir = dirname($file);&lt;br /&gt;#   system('mkdir', '-p', $dir) == 0 or next;&lt;br /&gt;#   open F, "&gt;", $file or next;&lt;br /&gt;#   print F get($url);&lt;br /&gt;# }&lt;br /&gt;for url in urls:&lt;br /&gt;    _file = url.replace(url, "")&lt;br /&gt;    _dir = os.path.dirname(_file)&lt;br /&gt;    if os.system("mkdir -p %s" % _dir) != 0:&lt;br /&gt;        continue&lt;br /&gt;    try:&lt;br /&gt;        F = open(_file, "w")&lt;br /&gt;    else:&lt;br /&gt;        continue&lt;br /&gt;    F.write(urllib2.urlopen(url).read())&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# while (my $url = NEXTVAL($urls)) {&lt;br /&gt;#   print "Bad link to: $url" unless head($url);&lt;br /&gt;# }&lt;br /&gt;for url in urls:&lt;br /&gt;    try:&lt;br /&gt;        urllib2.urlopen(url)&lt;br /&gt;    except:&lt;br /&gt;        print "Bad link to: %s" % url&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub traverse {&lt;br /&gt;#       ...&lt;br /&gt;#       my (%head, $html);&lt;br /&gt;#       @head{qw(TYPE LENGTH LAST_MODIFIED EXPIRES SERVER)} = head($url);&lt;br /&gt;#       if ($head{TYPE} = ̃ m{ˆtext/html\b}) {&lt;br /&gt;#         $html = get($url);&lt;br /&gt;#         push @queue, $interesting_links-&gt;(get_links($url,$html));&lt;br /&gt;#       }&lt;br /&gt;#       return wantarray ? ($url, \%head, $html) : $url;&lt;br /&gt;#       ...&lt;br /&gt;# }&lt;br /&gt;# I don't think this is a straight forward way to duplicate&lt;br /&gt;# "wantarray" type functionality in python.  In any case&lt;br /&gt;# it would be more uniform to *always* retrn the tuple&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub traverse {&lt;br /&gt;#   my $interesting_links = sub { shift; @_ };&lt;br /&gt;#   $interesting_links = shift if ref $_[0] eq 'CODE';&lt;br /&gt;#   my @queue = map [$_, 'supplied by user'], @_;&lt;br /&gt;#   my %seen;&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     while (@queue) {&lt;br /&gt;#       my ($url, $referrer) = @{shift @queue};&lt;br /&gt;#       $url =~ s/#.*$//;&lt;br /&gt;#       next if $seen{$url}++;&lt;br /&gt;#       my (%head, $html);&lt;br /&gt;#       @head{qw(TYPE LENGTH LAST_MODIFIED EXPIRES SERVER)} = head($url);&lt;br /&gt;#       if ($head{TYPE} =~ m{ˆtext/html\b}) {&lt;br /&gt;#         my $html = get($url);&lt;br /&gt;#         push @queue,&lt;br /&gt;#           map [$_, $url],&lt;br /&gt;#             $interesting_links-&gt;($url, get_links($url, $html));&lt;br /&gt;#       }&lt;br /&gt;#       return wantarray ? ($url, \%head, $referrer, $html) : $url;&lt;br /&gt;#     }&lt;br /&gt;#     return;     #exhausted&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;import urllib2    &lt;br /&gt;def traverse(queue, interesting_links=None):&lt;br /&gt;    queue = [(x, "supplied by user") for x in queue]&lt;br /&gt;&lt;br /&gt;    if interesting_links == None:&lt;br /&gt;        def interesting_links(this_url, other_urls):&lt;br /&gt;            return other_urls&lt;br /&gt;&lt;br /&gt;    seen = {}&lt;br /&gt;&lt;br /&gt;    while queue:&lt;br /&gt;        url, referrer = queue.pop(0)&lt;br /&gt;        url = url.split("#")[0]&lt;br /&gt;        seen.setdefault(url,0)&lt;br /&gt;        if seen[url] &gt; 0:&lt;br /&gt;            continue&lt;br /&gt;        seen[url] += 1&lt;br /&gt;        try:&lt;br /&gt;            page = urllib2.urlopen(url)&lt;br /&gt;        except urllib2.HTTPError:&lt;br /&gt;            print "http error for:", url&lt;br /&gt;            yield url, None, referrer, None&lt;br /&gt;            continue&lt;br /&gt;        content_type = page.headers.getheader("content-type")&lt;br /&gt;        if re.search(r"^text/html\b", content_type):&lt;br /&gt;            html = page.read()&lt;br /&gt;            queue.extend([(x, url) for x in interesting_links(url, get_links(url, html))])&lt;br /&gt;        yield url, page.headers, referrer, html&lt;br /&gt;            &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# my $top = 'http://perl.plover.com/'&lt;br /&gt;# my $interesting = sub { shift; grep /ˆ\Q$top/o, @_ };&lt;br /&gt;# my $urls = traverse($interesting, $top);&lt;br /&gt;# while (my ($url, $head, $referrer) = NEXTVAL($urls)) {&lt;br /&gt;#   next if $head-&gt;{TYPE};&lt;br /&gt;#   print "Page '$referrer' has a bad link to '$url'\n";&lt;br /&gt;# }&lt;br /&gt;top = "http://perl.plover.com"&lt;br /&gt;interesting = (lambda x,y: [_y for _y in y if top in _y])&lt;br /&gt;urls = traverse([top], interesting)&lt;br /&gt;for url, head, referrer, html in urls:&lt;br /&gt;    if not html:&lt;br /&gt;        continue&lt;br /&gt;    print "Page '%s' has a bad link to '%s'" % (referrer, url)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# my $top = 'http://perl.plover.com/';&lt;br /&gt;# my $interesting = sub { shift; grep /ˆ\Q$top/o, @_ };&lt;br /&gt;# my $urls = igrep_l { not $_[1]{TYPE} } traverse($interesting, $top);&lt;br /&gt;# while (my ($url, $head, $referrer) = NEXTVAL($urls)) {&lt;br /&gt;#   print "Page '$referrer' has a bad link to '$url'\n";&lt;br /&gt;# }&lt;br /&gt;top = "http://perl.plover.com"&lt;br /&gt;interesting = (lambda x,y: [_y for _y in y if top in _y])&lt;br /&gt;urls = igrep_l((lambda url, head, referrer, html: not html), traverse([top], interesting))&lt;br /&gt;for url, head, referrer, html in urls:&lt;br /&gt;    if not html:&lt;br /&gt;        continue&lt;br /&gt;    print "Page '%s' has a bad link to '%s'" % (referrer, url)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub igrep_l (&amp;$) {&lt;br /&gt;#   my ($is_interesting, $it) = @_;&lt;br /&gt;#   return Iterator {&lt;br /&gt;#     while (my @vals = NEXTVAL($it)) {&lt;br /&gt;#       return @vals if $is_interesting-&gt;(@vals);&lt;br /&gt;#     }&lt;br /&gt;#     return;&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def igrep_l(is_interesting, it):&lt;br /&gt;    for vals in it:&lt;br /&gt;        if is_interesting(*vals):&lt;br /&gt;            yield vals&lt;br /&gt;        &lt;br /&gt;&lt;br /&gt;# while (my ($url, $head, $referrer) = NEXTVAL($urls)) {&lt;br /&gt;#   print "Page '$referrer' has a bad link to '$url'\n";&lt;br /&gt;#   print "Edit now? ";&lt;br /&gt;#   my $resp = &lt;&gt;;&lt;br /&gt;#   if ($resp =~ /ˆy/i) {&lt;br /&gt;#     system $ENV{EDITOR}, url_to_filename($referrer);&lt;br /&gt;#   } elsif ($resp =~ /∧ q/i) {&lt;br /&gt;#     last;&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;for url, head, referrer, html in urls:&lt;br /&gt;    print "Page '%(referrer)s' has a bad line to '%(url)s'" % locals()&lt;br /&gt;    print "Edit now?"&lt;br /&gt;    resp = raw_input():&lt;br /&gt;    if resp == 'y':&lt;br /&gt;        os.system(os.environ["EDITOR"] + " " + url_to_filename(referrer))&lt;br /&gt;    elif resp == 'q':&lt;br /&gt;        break&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub traverse {&lt;br /&gt;#   my $interesting_link;&lt;br /&gt;#   $interesting_link = shift if ref $_[0] eq 'CODE';&lt;br /&gt;#   my @queue = map [$_, 'supplied by user'], @_;&lt;br /&gt;#   my %seen;&lt;br /&gt;#   my $q_it = igrep { ! $seen{$_-&gt;[0]}++ }&lt;br /&gt;#                imap { $_-&gt;[0] =~ s/#.*$//; $_}&lt;br /&gt;#                  Iterator { return shift(@queue) };&lt;br /&gt;#   if ($interesting_link) {&lt;br /&gt;#     $q_it = igrep {$interesting_link-&gt;(@$_)} $q_it;&lt;br /&gt;#   }&lt;br /&gt;#   return imap {&lt;br /&gt;#       my ($url, $referrer) = @$_;&lt;br /&gt;#       my (%head, $html);&lt;br /&gt;#       @head{qw(TYPE LENGTH LAST_MODIFIED EXPIRES SERVER)} = head($url);&lt;br /&gt;#       if ($head{TYPE} =~ m{ˆtext/html\b}) {&lt;br /&gt;#         $html = get($url);&lt;br /&gt;#         push @queue,&lt;br /&gt;#           map [$_, $url],&lt;br /&gt;#             get_links($url, $html);&lt;br /&gt;#       }&lt;br /&gt;#       return wantarray ? ($url, \%head, $referrer, $html) : $url;&lt;br /&gt;#   } $q_it;&lt;br /&gt;# }&lt;br /&gt;# this is not an exact match but is close enough for our&lt;br /&gt;# purposes.  what ever that purpose could be.&lt;br /&gt;def traverse(queue, interesting_link=None):&lt;br /&gt;    seen = {}&lt;br /&gt;    queue = [(x, "supplied by user") for x in queue]&lt;br /&gt;&lt;br /&gt;    def iterate_queue():&lt;br /&gt;        while queue:&lt;br /&gt;            yield queue.pop(0)&lt;br /&gt;&lt;br /&gt;    def not_seen_yet(url):&lt;br /&gt;        seen.setdefault(url,0)&lt;br /&gt;        seen[url] += 1&lt;br /&gt;        if seen[url] &gt; 1:&lt;br /&gt;            return False&lt;br /&gt;        return True&lt;br /&gt;&lt;br /&gt;    q_it = iterate_queue()&lt;br /&gt;    q_it = ((url[0].split("#")[0], url[1]) for url in q_it)&lt;br /&gt;    q_it = (url for url in q_it if not_seen_yet(url[0]))&lt;br /&gt;&lt;br /&gt;    if interesting_link != None:&lt;br /&gt;        q_it = igrep(interesting_link, q_it)&lt;br /&gt;&lt;br /&gt;    def process_url((url, referrer)): &lt;br /&gt;        print "process_url:", url, referrer&lt;br /&gt;        try:&lt;br /&gt;            page = urllib2.urlopen(url)&lt;br /&gt;        except urllib2.HTTPError:&lt;br /&gt;            print "http error for:", url&lt;br /&gt;            return url, None, referrer, None&lt;br /&gt;&lt;br /&gt;        content_type = page.headers.getheader("content-type")&lt;br /&gt;        if re.search(r"^text/html\b", content_type):&lt;br /&gt;            html = page.read()&lt;br /&gt;            queue.extend([(x, url) for x in get_links(url, html)])&lt;br /&gt;        return url, page.headers, referrer, html&lt;br /&gt;            &lt;br /&gt;    return imap(process_url, q_it)&lt;br /&gt;        &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub make_robot_filter {&lt;br /&gt;#   my $agent = shift;&lt;br /&gt;#   my %seen_site;&lt;br /&gt;#   my $rules = WWW::RobotRules-&gt;new($agent);&lt;br /&gt;#   return sub {&lt;br /&gt;#     my $url = url(shift());&lt;br /&gt;#     return 1 unless $url-&gt;scheme eq 'http';&lt;br /&gt;#     unless ($seen_site{$url-&gt;netloc}++) {&lt;br /&gt;#       my $robots = $url-&gt;clone;&lt;br /&gt;#       $robots-&gt;path('/robots.txt');&lt;br /&gt;#       $robots-&gt;frag(undef);&lt;br /&gt;#       $rules-&gt;parse($robots, get($robots));&lt;br /&gt;#     }&lt;br /&gt;#     $rules-&gt;allowed($url)&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;def make_robot_filter(agent):&lt;br /&gt;    seen_site = {}&lt;br /&gt;&lt;br /&gt;    rules = {} #robotparser.RobotFileParser()&lt;br /&gt;&lt;br /&gt;    def _filter(url):&lt;br /&gt;        u = urlparse.urlparse(url) &lt;br /&gt;        if u.scheme != "http":&lt;br /&gt;            return True&lt;br /&gt;&lt;br /&gt;        if u.netloc not in rules:&lt;br /&gt;            rules[u.netloc] = robotparser.RobotFileParser()&lt;br /&gt;            rules[u.netloc].set_url(u.scheme+"://"+u.netloc+"/robots.txt")&lt;br /&gt;            rules[u.netloc].read()&lt;br /&gt;&lt;br /&gt;        return rules[u.netloc].can_fetch(agent, url)&lt;br /&gt;&lt;br /&gt;    return _filter&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-9002174236756382419?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/9002174236756382419/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=9002174236756382419' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/9002174236756382419'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/9002174236756382419'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2009/02/higher-order-perl-python-style-chapter.html' title='Higher Order Perl (Python Style) : Chapter 4 - Iterators'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-8374790231143746969</id><published>2009-02-02T20:49:00.000-08:00</published><updated>2009-02-02T21:24:07.575-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='squeak'/><title type='text'>Smalltalk for a Year - Status Report 1</title><content type='html'>As I've mentioned, I'm working on smalltalk as my learn a language a year language.  So where am I after a month?&lt;br /&gt;&lt;br /&gt;My initial idea was to ease into things using etoys as a gateway drug.  But after a week or so I decided that, while it's oddly fascinating and kinda fun, it's not really smalltalk per se.  So while I'll probably dabble a little bit, I've decided that I need to work with more mainstream smalltalk learning.&lt;br /&gt;&lt;br /&gt;In that vein, I'm reading Squeak By Example and am about 1/3 of the way through.  It's a very nice no nonsense introduction to smalltalk the language and squeak the environment.  &lt;br /&gt;&lt;br /&gt;After that I'm thinking I'll either look at the Squeak Development Example for Squeak 3.9 tutorial or work on a &lt;a href="http://www.swa.hpi.uni-potsdam.de/seaside/tutorial"&gt;Seaside Tutorial&lt;/a&gt;.  I'm sort of leaning towards the latter, but I'm sure I'll eventually do both, but for some reason I'm more drawn to the web development aspect of things these days.  And how cool are you if you use continuations?  (Pretty cool, I'd wager)&lt;br /&gt;&lt;br /&gt;I have to confess that already in the first month I've had to fight off the the urge just to ditch this project.  I'm a little embarrassed to admit that I'm not immediately overwhelmed with a love for smalltalk.  And logically that's not really so unexpected.  Learning a language is *hard*.  And in the initial phases you basically see everything with your blub colored glasses and in the new language you see blub features but they are distorted and some blub features are completely missing or almost too awkward to be usable.  You may dimly see some features that are interesting but they are obscured by an alien syntax and semantics.  &lt;br /&gt;&lt;br /&gt;And that's where I am now with smalltalk.  There are somethings that seem interesting (elegant metaclass programming features, turtles all the way down, highly integrated development environment, etc) but I've never used these features "in anger".  So at best they just seem kinda interesting.  On the other hand the lack of modules feels archaic, the default user interface look-and-feel seems oddly clunky, the lack of list access syntax (e.g. foo[3:5]) makes me sad, and I miss emacs.&lt;br /&gt;&lt;br /&gt;So I'm in the no man's land right now.  I see things faintly off in the distance that seem interesting but everything in my reach is (seemingly) inferior and awkward.&lt;br /&gt;&lt;br /&gt;I guess it helps me to lay it out like this.  I'm surprised (even though I should know myself better by now) at how easily I could just abandon things and jump over to haskell for a while.  Oooh, and then lisp is kinda cool, and then, oh yeah, I heard javascript is the NBL, I better look at that for a few minutes today.&lt;br /&gt;&lt;br /&gt;I wonder if it's harder to fall in love with another language when your day job is python.  But I'm a little afraid that python has become my blub and I must fight the tendencies of a blub programmer to be blind to non-blubby goodness.&lt;br /&gt;&lt;br /&gt;Ok, enough dithering.  I'm putting my blinders on again and focusing on smalltalk.  I actually hope (and somewhat expect) that I will get over the oddness hurdle and really love smalltalk.  &lt;br /&gt;&lt;br /&gt;But it's not love at first sight.  It's more like cautious optimism at first sight.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-8374790231143746969?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/8374790231143746969/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=8374790231143746969' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/8374790231143746969'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/8374790231143746969'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2009/02/smalltalk-for-year-status-report-1.html' title='Smalltalk for a Year - Status Report 1'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-4969800666607986577</id><published>2009-01-23T21:29:00.000-08:00</published><updated>2009-01-23T21:40:17.659-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='functional programming'/><category scheme='http://www.blogger.com/atom/ns#' term='higher order perl'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='hop'/><title type='text'>Higher Order Perl (Python Style) : Chapter 3 - Caching and Memoization</title><content type='html'>&lt;pre&gt;&lt;br /&gt;### 3.1 Caching Fixes Recursion&lt;br /&gt;&lt;br /&gt;# sub RGB_to_CMYK {&lt;br /&gt;#   my ($r, $g, $b) = @_;&lt;br /&gt;#   my ($c, $m, $y) = (255-$r, 255-$g, 255-$b);&lt;br /&gt;#   my $k = $c &lt; $m ? ($c &lt; $y ? $c : $y)&lt;br /&gt;#                   : ($m &lt; $y ? $m : $y);  # Minimum&lt;br /&gt;#   for ($c, $m, $y) { $_ -= $k }&lt;br /&gt;#   [$c, $m, $y, $k];&lt;br /&gt;# }&lt;br /&gt;def rgb_to_cmyk(r,g,b):&lt;br /&gt;   c, m, y = 255-r, 255-g, 255-b&lt;br /&gt;   k = min([c,m,y])&lt;br /&gt;   c, m, y = c - k, m - k, y - k&lt;br /&gt;   return c, m, y, k&lt;br /&gt;&lt;br /&gt;# my %cache;&lt;br /&gt;# sub RGB_to_CMYK {&lt;br /&gt;#   my ($r, $g, $b) = @_;&lt;br /&gt;#   my $key = join ',', $r, $g, $b;&lt;br /&gt;#   return $cache{$key} if exists $cache{$key};&lt;br /&gt;#   my ($c, $m, $y) = (255-$r, 255-$g, 255-$b);&lt;br /&gt;#   my $k = $c &lt; $m ? ($c &lt; $y ? $c : $y)&lt;br /&gt;#                   : ($m &lt; $y ? $m : $y);  # Minimum&lt;br /&gt;#   for ($c, $m, $y) { $_ -= $k }&lt;br /&gt;#   return $cache{$key} = [$c, $m, $y, $k];&lt;br /&gt;# }&lt;br /&gt;CACHE = {}&lt;br /&gt;def rgb_to_cmyk(r,g,b):&lt;br /&gt;   if (r,g,b) not in CACHE:&lt;br /&gt;       c, m, y = 255-r, 255-g, 255-b&lt;br /&gt;       k = min([c,m,y])&lt;br /&gt;       c, m, y = c - k, m - k, y - k&lt;br /&gt;       CACHE[(r,g,b)] = (c,m,y,k)&lt;br /&gt;   return CACHE[(r,g,b)]&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# Compute the number of pairs of rabbits alive in month n&lt;br /&gt;# sub fib {&lt;br /&gt;#    my ($month) = @_;&lt;br /&gt;#    if ($month &lt; 2) { 1 }&lt;br /&gt;#    else {&lt;br /&gt;#        fib($month-1) + fib($month-2);&lt;br /&gt;#    }&lt;br /&gt;# }&lt;br /&gt;def fib(month):&lt;br /&gt;   if month &lt; 2:&lt;br /&gt;       return 1&lt;br /&gt;   else:&lt;br /&gt;       return fib(month - 1) + fib(month - 2)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;### 3.2 Inline Caching&lt;br /&gt;&lt;br /&gt;# # Compute the number of pairs of rabbits alive in month n&lt;br /&gt;# { my %cache;&lt;br /&gt;#   sub fib {&lt;br /&gt;#     my ($month) = @_;&lt;br /&gt;#     unless (exists $cache{$month}) {&lt;br /&gt;#       if ($month &lt; 2) { $cache{$month} = 1 }&lt;br /&gt;#       else {&lt;br /&gt;#         $cache{$month} = fib($month-1) + fib($month-2);&lt;br /&gt;#       }&lt;br /&gt;#     }&lt;br /&gt;#     return $cache{$month};&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;# NOTE: we include the function itself as part of&lt;br /&gt;#       the hash key since python can't do the&lt;br /&gt;#       scoping technique above w/o hackery (AFAIK).&lt;br /&gt;#       Of course the more natural python way&lt;br /&gt;#       would probably be just to use a decorator&lt;br /&gt;CACHE = {}&lt;br /&gt;def fib(month):&lt;br /&gt;   if (fib, month) not in CACHE:&lt;br /&gt;       if month &lt; 2:&lt;br /&gt;           CACHE[(fib, month)] = 1&lt;br /&gt;       else:&lt;br /&gt;           CACHE[(fib, month)] = fib(month - 1) + fib(month - 2)&lt;br /&gt;   return CACHE[(fib, month)]&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub fib {&lt;br /&gt;#   my %cache;&lt;br /&gt;#   ...&lt;br /&gt;# }&lt;br /&gt;def fib(month):&lt;br /&gt;   CACHE = {}&lt;br /&gt;   ...&lt;br /&gt;&lt;br /&gt;### 3.3 Good Ideas&lt;br /&gt;&lt;br /&gt;# sub some_function {&lt;br /&gt;#   $result = some computation involving @_;&lt;br /&gt;#   return $result;&lt;br /&gt;# }&lt;br /&gt;def some_function(*x):&lt;br /&gt;   result = some computation involving x&lt;br /&gt;   return result&lt;br /&gt;&lt;br /&gt;# { my %cache;&lt;br /&gt;#   sub some_function_with_caching {&lt;br /&gt;#     my $key = join ',', @_;&lt;br /&gt;#     return $cache{$key} if exists $cache{$key};&lt;br /&gt;#     $result = the same computation involving @_;&lt;br /&gt;#     return $cache{$key} = $result;&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;CACHE = {}&lt;br /&gt;def some_function_with_caching(*x):&lt;br /&gt;   key = (some_function_with_caching, x)&lt;br /&gt;   if key not in CACHE:&lt;br /&gt;       CACHE[key] = the same computation involving *x&lt;br /&gt;   return CACHE[key]&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;### 3.4 Memoization&lt;br /&gt;&lt;br /&gt;# use Memoize;&lt;br /&gt;# memoize 'fib';&lt;br /&gt;# # Compute the number of pairs of rabbits alive in month n&lt;br /&gt;# sub fib {&lt;br /&gt;#   my ($month) = @_;&lt;br /&gt;#   if ($month &lt; 2) { 1 }&lt;br /&gt;#   else {&lt;br /&gt;#       fib($month-1) + fib($month-2);&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;# Just for sake of argument let's pretend we have a "memoize"&lt;br /&gt;# module that provides a memoize decorator&lt;br /&gt;@memoize&lt;br /&gt;def fib(month):&lt;br /&gt;   if month &lt; 2:&lt;br /&gt;       return 1&lt;br /&gt;   else:&lt;br /&gt;       return fib(month - 1) + fib(month - 2)&lt;br /&gt;&lt;br /&gt;### 3.5 The Memoize Module&lt;br /&gt;&lt;br /&gt;# sub memoize {&lt;br /&gt;#   my ($func) = @_;&lt;br /&gt;#   my %cache;&lt;br /&gt;#   my $stub = sub {&lt;br /&gt;#     my $key = join ',', @_;&lt;br /&gt;#     $cache{$key} = $func-&gt;(@_) unless exists $cache{$key};&lt;br /&gt;#     return $cache{$key};&lt;br /&gt;#   };&lt;br /&gt;#   return $stub;&lt;br /&gt;# }&lt;br /&gt;&lt;br /&gt;# One reasonable way to do this in python is as a decorator.  To keep&lt;br /&gt;# things simple we'll leave out **kwargs since we can't (simply) use&lt;br /&gt;# dicts as parts of keys.  We also assume for now that args contains&lt;br /&gt;# only things that can be in a dict key&lt;br /&gt;def memoize(func):&lt;br /&gt;   CACHE = {}&lt;br /&gt;   def memoized_func(*args):&lt;br /&gt;       if args not in CACHE:&lt;br /&gt;           CACHE[args] = func(*args)&lt;br /&gt;       return CACHE[args]&lt;br /&gt;   return memoized_func&lt;br /&gt;&lt;br /&gt;# NOTE: running the above with fib actually gave&lt;br /&gt;# "RuntimeError: maximum recursion depth exceeded"&lt;br /&gt;# for month = 1000.  But if you do some smaller&lt;br /&gt;# values first to "seed" it it will handle the&lt;br /&gt;# larger values.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#$fastfib = memoize(\&amp;fib);&lt;br /&gt;@memoize&lt;br /&gt;def fib(month):&lt;br /&gt;   ....&lt;br /&gt;# or&lt;br /&gt;# fastfib = memoize(fib)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub make_counter {&lt;br /&gt;#   my $n = shift;&lt;br /&gt;#   return sub { print "n is ", $n++ };&lt;br /&gt;# }&lt;br /&gt;# my $x = make_counter(7);&lt;br /&gt;# my $y = make_counter(20);&lt;br /&gt;&lt;br /&gt;# We cheat a little here since python's scoping&lt;br /&gt;# won't let's us work with the n directly&lt;br /&gt;def make_counter(n):&lt;br /&gt;   x = [n]&lt;br /&gt;   def counter():&lt;br /&gt;       print "n is", x[0]&lt;br /&gt;       x[0] += 1&lt;br /&gt;   return counter&lt;br /&gt;x = make_counter(7)&lt;br /&gt;y = make_counter(20)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;### 3.6 Caveats&lt;br /&gt;&lt;br /&gt;# use Memoize;&lt;br /&gt;# sub iota {&lt;br /&gt;#   my $n = shift;&lt;br /&gt;#   return [1 .. $n];&lt;br /&gt;# }&lt;br /&gt;# memoize 'iota';&lt;br /&gt;# $i10 = iota(10);&lt;br /&gt;# $j10 = iota(10);&lt;br /&gt;# pop @$i10;&lt;br /&gt;# print @$j10;&lt;br /&gt;@memoize&lt;br /&gt;def iota(n):&lt;br /&gt;   return range(n)&lt;br /&gt;i10 = iota(10)&lt;br /&gt;j10 = iota(10)&lt;br /&gt;i10.pop()&lt;br /&gt;print j10&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# package Octopus;&lt;br /&gt;# sub new {&lt;br /&gt;#   my ($class, %args) = @_;&lt;br /&gt;#   $args{tentacles} = 8;&lt;br /&gt;#   bless \%args =&gt; $class;&lt;br /&gt;# }&lt;br /&gt;&lt;br /&gt;# sub name {&lt;br /&gt;#   my $self = shift;&lt;br /&gt;#   if (@_) { $self-&gt;{name} = shift }&lt;br /&gt;#   $self-&gt;{name};&lt;br /&gt;# }&lt;br /&gt;&lt;br /&gt;# my $junko = Octopus-&gt;new(favorite_food =&gt; "crab cakes");&lt;br /&gt;# $junko-&gt;name("Junko");&lt;br /&gt;# my $fenchurch = Octopus-&gt;new(favorite_food =&gt; "crab cakes");&lt;br /&gt;# $fenchurch-&gt;name("Fenchurch");&lt;br /&gt;&lt;br /&gt;# # This prints "Fenchurch" -- oops!&lt;br /&gt;# print "The name of the FIRST octopus is ", $junko-&gt;name, "\n";&lt;br /&gt;class Octopus(object):&lt;br /&gt;   def __init__(self, **kwargs):&lt;br /&gt;       self.tentacles = 8&lt;br /&gt;       self.__dict__.update(kwargs)&lt;br /&gt;&lt;br /&gt;   def name(self, new_name=None):&lt;br /&gt;       if new_name != None:&lt;br /&gt;           self.name = new_name&lt;br /&gt;       return self.name&lt;br /&gt;&lt;br /&gt;# OK, this one has me stumped.  The text says&lt;br /&gt;# that "new" was memoized, but I'm not seeing it&lt;br /&gt;# Either a typo on the books part or a thinko on mine&lt;br /&gt;# But the lesson about caching mutable items is well taken&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#@result = sort { -M $a &lt;=&gt; -M $b } @files;&lt;br /&gt;result = sorted(files, key=(lambda x: os.path.getmtime(x)))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;### 3.7 Key Generation&lt;br /&gt;&lt;br /&gt;#my $key = join ',', @_;&lt;br /&gt;key = ",".join([str(x) for x in key_list])&lt;br /&gt;# or just use a tuple and avoid problems in the first place&lt;br /&gt;key = tuple(key_list)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# my @args = @_;&lt;br /&gt;# s/([\\,])/\\$1/g for @args;&lt;br /&gt;# my $key = join ",", @args;&lt;br /&gt;key = ",".join([str(x).replace(",","\,").replace("\\","\\\\") for x in key_list])&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub memoize {&lt;br /&gt;#   my ($func, $keygen) = @_;&lt;br /&gt;#   my %cache;&lt;br /&gt;#   my $stub = sub {&lt;br /&gt;#     my $key = $keygen ? $keygen-&gt;(@_) : join ',', @_;&lt;br /&gt;#     $cache{$key} = $func-&gt;(@_) unless exists $cache{$key};&lt;br /&gt;#     return $cache{$key};&lt;br /&gt;#   };&lt;br /&gt;#   return $stub;&lt;br /&gt;# }&lt;br /&gt;# There are other ways to do this, but we want&lt;br /&gt;# to stay close to the perl version.  Now&lt;br /&gt;# memoize will need to be invoked as&lt;br /&gt;#@memoize() # need the "()" here&lt;br /&gt;#@memoize(keygen_func)&lt;br /&gt;def memoize(keygen=None):&lt;br /&gt;   CACHE = {}&lt;br /&gt;   def outer_func(func):&lt;br /&gt;       def inner_func(*args):&lt;br /&gt;           key = args&lt;br /&gt;           if keygen:&lt;br /&gt;               key = keygen(args)&lt;br /&gt;           if key not in CACHE:&lt;br /&gt;               CACHE[key] = func(*args)&lt;br /&gt;           return CACHE[key]&lt;br /&gt;&lt;br /&gt;       return inner_func&lt;br /&gt;   return outer_func&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub memoize {&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#   my ($func, $keygen) = @_;&lt;br /&gt;#   my %cache;&lt;br /&gt;#   my $stub = $keygen ?&lt;br /&gt;#     sub { my $key = $keygen-&gt;(@_);&lt;br /&gt;#           $cache{$key} = $func-&gt;(@_) unless exists $cache{$key};&lt;br /&gt;#           return $cache{$key};&lt;br /&gt;#         }&lt;br /&gt;#   :&lt;br /&gt;#     sub { my $key = join ',', @_;&lt;br /&gt;#           $cache{$key} = $func-&gt;(@_) unless exists $cache{$key};&lt;br /&gt;#           return $cache{$key};&lt;br /&gt;#         }&lt;br /&gt;#   ;&lt;br /&gt;#   return $stub;&lt;br /&gt;# }&lt;br /&gt;def memoize(keygen=None):&lt;br /&gt;   CACHE = {}&lt;br /&gt;   def outer_func(func):&lt;br /&gt;       def inner_func_no_keygen(*args):&lt;br /&gt;           key = args&lt;br /&gt;           if key not in CACHE:&lt;br /&gt;               CACHE[key] = func(*args)&lt;br /&gt;           return CACHE[key]&lt;br /&gt;&lt;br /&gt;       def inner_func_with_keygen(*args):&lt;br /&gt;           key = keygen(args)&lt;br /&gt;           if key not in CACHE:&lt;br /&gt;               CACHE[key] = func(*args)&lt;br /&gt;           return CACHE[key]&lt;br /&gt;&lt;br /&gt;       return keygen and inner_func_with_keygen or inner_func_no_keygen&lt;br /&gt;   return outer_func&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# $memoized = memoize(\&amp;fib, q{my @args = @_;&lt;br /&gt;#                              s/([\\,])/\\$1/g for @args;&lt;br /&gt;#                              join ',', @args;&lt;br /&gt;#                             });&lt;br /&gt;&lt;br /&gt;# sub memoize {&lt;br /&gt;#   my ($func, $keygen) = @_;&lt;br /&gt;#   $keygen ||= q{join ',', @_};&lt;br /&gt;#   my %cache;&lt;br /&gt;#   my $newcode = q{&lt;br /&gt;#     sub { my $key = do { KEYGEN };&lt;br /&gt;#           $cache{$key} = $func-&gt;(@_) unless exists $cache{$key};&lt;br /&gt;#           return $cache{$key};&lt;br /&gt;#         }&lt;br /&gt;#   };&lt;br /&gt;#   $newcode =~ s/KEYGEN/$keygen/g;&lt;br /&gt;#   return eval $newcode;&lt;br /&gt;# }&lt;br /&gt;&lt;br /&gt;# $keygen ||= 'join \',\', @_';&lt;br /&gt;&lt;br /&gt;# my $newcode = "&lt;br /&gt;#   sub { my \$key = do { $keygen };&lt;br /&gt;#         \$cache{\$key} = \$func-&gt;(\@_) unless exists \$cache{\$key};&lt;br /&gt;#         return \$cache{\$key};&lt;br /&gt;#       }&lt;br /&gt;# ";&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub memoize {&lt;br /&gt;#   my ($func, $keygen) = @_;&lt;br /&gt;#   my $keyfunc;&lt;br /&gt;#   if ($keygen eq '') {&lt;br /&gt;#     $keygen = q{join ',', @_}&lt;br /&gt;#   } elsif (UNIVERSAL::isa($keygen, 'CODE')) {&lt;br /&gt;#     $keyfunc = $keygen;&lt;br /&gt;#     $keygen = q{$keyfunc-&gt;(@_)};&lt;br /&gt;#   }&lt;br /&gt;#   my %cache;&lt;br /&gt;#   my $newcode = q{&lt;br /&gt;#     sub { my $key = do { KEYGEN };&lt;br /&gt;#           $cache{$key} = $func-&gt;(@_) unless exists $cache{$key};&lt;br /&gt;#           return $cache{$key};&lt;br /&gt;#         }&lt;br /&gt;#   };&lt;br /&gt;#   $newcode = ̃ s/KEYGEN/$keygen/g;&lt;br /&gt;#   return eval $newcode;&lt;br /&gt;# }&lt;br /&gt;&lt;br /&gt;# if (ref($keygen) eq 'CODE') { ... }&lt;br /&gt;&lt;br /&gt;"""&lt;br /&gt;UNCLE!  This seems too far off the path for a sane python translation.&lt;br /&gt;Actually I can think of a way to do it, you just wouldn't, so unless&lt;br /&gt;there is a great public outcry, I will just skip trying to force this&lt;br /&gt;down the python's throat.&lt;br /&gt;"""&lt;br /&gt;&lt;br /&gt;# sub example {&lt;br /&gt;#   my %args = @_;&lt;br /&gt;#   $args{A} = 32 unless defined $args{A};&lt;br /&gt;#   $args{B} = 17 unless defined $args{B};&lt;br /&gt;#   # ...&lt;br /&gt;# }&lt;br /&gt;def example(**args):&lt;br /&gt;   args.setdefault("A", 32)&lt;br /&gt;   args.setdefault("B", 17)&lt;br /&gt;   #&lt;br /&gt;&lt;br /&gt;# example(C =&gt; 99);&lt;br /&gt;# example(C =&gt; 99, A =&gt; 32);&lt;br /&gt;# example(A =&gt; 32, C =&gt; 99);&lt;br /&gt;# example(B =&gt; 17, C =&gt; 99);&lt;br /&gt;# example(C =&gt; 99, B =&gt; 17);&lt;br /&gt;# example(A =&gt; 32, C =&gt; 99, B =&gt; 17);&lt;br /&gt;# example(B =&gt; 17, A =&gt; 32, C =&gt; 99);&lt;br /&gt;# (etc.)&lt;br /&gt;&lt;br /&gt;example(C=99)&lt;br /&gt;example(C=99, A=32)&lt;br /&gt;example(A=32, C=99)&lt;br /&gt;example(B=17, C=99)&lt;br /&gt;example(C=99, B=17)&lt;br /&gt;example(A=32, C=99, B=17)&lt;br /&gt;example(B=17, A=32, C=99)&lt;br /&gt;(etc.)&lt;br /&gt;&lt;br /&gt;# NOTE: the problem in python is that&lt;br /&gt;#       we just can't use dicts as a hash&lt;br /&gt;#       otherwise this would be a no brainer&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub {&lt;br /&gt;#   my %h = @_;&lt;br /&gt;#   $h{A} = 32 unless defined $h{A};&lt;br /&gt;#   $h{B} = 17 unless defined $h{B};&lt;br /&gt;#   join ",", @h{'A','B','C'};&lt;br /&gt;# }&lt;br /&gt;# we give this function a name so that we don't&lt;br /&gt;# have to work around python's lambda limitations&lt;br /&gt;def _keygen(a, h):&lt;br /&gt;   h.setdefault("A", 32)&lt;br /&gt;   h.setdefault("B", 17)&lt;br /&gt;   # a tuple key is probably more natural&lt;br /&gt;   return tuple([x[1] for x in sorted(h.items())])&lt;br /&gt;   # but we can make a string just as simply&lt;br /&gt;   # return ','.join([str(x[1]) for x in sorted(args.items())])&lt;br /&gt;&lt;br /&gt;# keygen only has to work over the set of expected values so we can go ahead and&lt;br /&gt;# do something like: &lt;br /&gt;# have the key be a tuple of (args, kwargs)&lt;br /&gt;# where kwargs has been adapted to a tuple of key,value pair tuples&lt;br /&gt;# BUT now our keygen needs to handle *Both* types of inputs&lt;br /&gt;def memoize(keygen=None):&lt;br /&gt;   CACHE = {}&lt;br /&gt;   def outer_func(func):&lt;br /&gt;       def inner_func(*args, **kwargs):&lt;br /&gt;           kwargs_ = tuple(sorted(kwargs.items()))&lt;br /&gt;           key = (args, kwargs_)&lt;br /&gt;           if keygen:&lt;br /&gt;               key = keygen(args, kwargs)&lt;br /&gt;           if key not in CACHE:&lt;br /&gt;               print "updating cache:", key&lt;br /&gt;               CACHE[key] = func(*args, **kwargs)&lt;br /&gt;           return CACHE[key]&lt;br /&gt;&lt;br /&gt;       return inner_func&lt;br /&gt;   return outer_func&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub example {&lt;br /&gt;#   my ($a, $b, $c) = @_;&lt;br /&gt;#   $a = 32 unless defined $a;&lt;br /&gt;#   $b = 17 unless defined $b;&lt;br /&gt;#   # more calculation here ...&lt;br /&gt;# }&lt;br /&gt;def example(a=32, b=17,c=None):&lt;br /&gt;   # more calculation here ...&lt;br /&gt;&lt;br /&gt;# my ($a, $b, $c) = @_;&lt;br /&gt;# $a = 32 unless defined $a;&lt;br /&gt;# $b = 17 unless defined $b;&lt;br /&gt;# join ',', $a, $b, $c;&lt;br /&gt;&lt;br /&gt;# first three lines handled by machinery of argument passing in python&lt;br /&gt;return ",".join([str(x) for x in (a,b,c)])&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub { my $key = do { $_[0] = 32 unless defined $_[0];&lt;br /&gt;#                      $_[1] = 17 unless defined $_[1];&lt;br /&gt;#                      join ',', @_;&lt;br /&gt;#                    };&lt;br /&gt;#       $cache{$key} = $func-&gt;(@_) unless exists $cache{$key};&lt;br /&gt;#       return $cache{$key};&lt;br /&gt;#     }&lt;br /&gt;&lt;br /&gt;# Hmm... very likely I'm missing a sublety here but&lt;br /&gt;# is seems that python's default args already handles&lt;br /&gt;# this case.  Plus the fact that (if I'm understanding&lt;br /&gt;# correctly), python doesn't map well to the "do { }"&lt;br /&gt;# syntax.  I'll punt for now.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub set_to_57 {&lt;br /&gt;#   $_[0] = 57;&lt;br /&gt;# }&lt;br /&gt;# my $x = 119;&lt;br /&gt;# set_to_57($x);&lt;br /&gt;&lt;br /&gt;# you can't take advantage of this (or get bit by, depending&lt;br /&gt;# on your perspective) in python, but cause the args are pass&lt;br /&gt;# by value.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub safe_function {&lt;br /&gt;#    my ($n) = @_;&lt;br /&gt;#    $n = 57; # does *not* set $x to 57&lt;br /&gt;# }&lt;br /&gt;# my $x = 119;&lt;br /&gt;# safe_function($x);&lt;br /&gt;def safe_function(n):&lt;br /&gt;   n = 57&lt;br /&gt;x = 119&lt;br /&gt;safe_function(x)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# memoize(\&amp;example, q{&lt;br /&gt;#   my ($a, $b, $c) = @_;&lt;br /&gt;#   $a = 32 unless defined $a;&lt;br /&gt;#   $b = 17 unless defined $b;&lt;br /&gt;#   @_ = ($a, $b, $c);         # line 5&lt;br /&gt;#   join ',', @_;&lt;br /&gt;# });&lt;br /&gt;# We avoid sending the keygen function as a string&lt;br /&gt;# since it is too unpythonic.&lt;br /&gt;# Here we are assuming that the arguments are positional&lt;br /&gt;# and "None" if not available.  Usually we would use&lt;br /&gt;# key word args in this situation&lt;br /&gt;def _keygen(args, kwargs):&lt;br /&gt;   print "args=",args,"kwargs=",kwargs&lt;br /&gt;   a = args[0] == None and 32 or args[0]&lt;br /&gt;   b = args[1] == None and 17 or args[1]&lt;br /&gt;   c = args[2]&lt;br /&gt;   return (a,b,c)&lt;br /&gt;   # or return ",".join([str(x) for x in (a,b,c)])&lt;br /&gt;&lt;br /&gt;@memoize(_keygen)&lt;br /&gt;def exercise(*args):&lt;br /&gt;   a,b,c = (args + [None]*3)[:3]&lt;br /&gt;   ...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub is_in {&lt;br /&gt;#   my ($needle, $haystack) = @_;&lt;br /&gt;#   for my $item (@$haystack) {&lt;br /&gt;#     return 1 if $item == $needle;&lt;br /&gt;#   }&lt;br /&gt;#   return;&lt;br /&gt;# }&lt;br /&gt;def is_in(needle, haystack):&lt;br /&gt;   for item in haystack:&lt;br /&gt;       if item == needle:&lt;br /&gt;           return True&lt;br /&gt;   return False&lt;br /&gt;&lt;br /&gt;# if (is_in($my_id, \@employee_ids)) { ... }&lt;br /&gt;if is_in(my_id, employee_ids):&lt;br /&gt;   ...&lt;br /&gt;&lt;br /&gt;#sub { join ",", $_[0], @{$_[1]} }&lt;br /&gt;# In python the most obvious solution here&lt;br /&gt;# is again to just use tuple()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub find_share {&lt;br /&gt;#   my ($target, $treasures) = @_;&lt;br /&gt;#   return [] if $target == 0;&lt;br /&gt;#   return    if $target &lt; 0 || @$treasures == 0;&lt;br /&gt;#   my ($first, @rest) = @$treasures;&lt;br /&gt;#   my $solution = find_share($target-$first, \@rest);&lt;br /&gt;#   return [$first, @$solution] if $solution;&lt;br /&gt;#   return         find_share($target       , \@rest);&lt;br /&gt;# }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def find_share(target, treasures):&lt;br /&gt;   if target == 0:&lt;br /&gt;       return []&lt;br /&gt;   if target &lt; 0 or len(treasures) == 0:&lt;br /&gt;       return&lt;br /&gt;   first, rest = treasures[0], treasures[1:]&lt;br /&gt;   solution = find_share(target-first, rest)&lt;br /&gt;   if solution != None:&lt;br /&gt;       return [first] + solution&lt;br /&gt;   return find_share(target, rest)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub delivery_charge {&lt;br /&gt;#   my ($quantity_ordered) = @_;&lt;br /&gt;#   my ($hour, $day_of_week) = (localtime)[2,6];&lt;br /&gt;#   # perform complex computation involving $weight, $gross_cost,&lt;br /&gt;#   #     $hour, $day_of_week, and $quantity_ordered&lt;br /&gt;#   # ...&lt;br /&gt;#   return $delivery_charge;&lt;br /&gt;# }&lt;br /&gt;import time&lt;br /&gt;def delivery_charge(quantity_ordered):&lt;br /&gt;   localtime = time.localtime()&lt;br /&gt;   hour, day_of_week = localtime[2], localtime[6]&lt;br /&gt;   # perform complex computation involving $weight, $gross_cost,&lt;br /&gt;   #     $hour, $day_of_week, and $quantity_ordered&lt;br /&gt;   # ...&lt;br /&gt;   return delivery_charge&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub delivery_charge_key {&lt;br /&gt;#   join ',', @_, (localtime)[2,6];&lt;br /&gt;# }&lt;br /&gt;def delivery_charge_key(quantity_ordered):&lt;br /&gt;   localtime = time.localtime()&lt;br /&gt;   return ",".join([str(x) for x in (quantity_ordered,localtime[2], localtime[6])])&lt;br /&gt;   ## or just return (quantity_ordered,localtime[2], localtime[6])&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub delivery_charge_key {&lt;br /&gt;#   my ($hour, $day_of_week) = (localtime)[2,6];&lt;br /&gt;#   my $weekend = $day_of_week == 0 || $day_of_week == 6;&lt;br /&gt;#   join ',', @_, $hour, $weekend;&lt;br /&gt;# }&lt;br /&gt;def delivery_charge_key(quantity_ordered):&lt;br /&gt;   localtime = time.localtime()&lt;br /&gt;   hour, day_of_week = localtime[2], localtime[6]&lt;br /&gt;   weekend = day_of_week in (0,6)&lt;br /&gt;   return ",".join([str(x) for x in (quantity_ordered, hour, weekend)])&lt;br /&gt;   ## or just return (quantity_ordered, hour, weekend)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;### 3.8 Caching in Object Methods&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# package Investor;&lt;br /&gt;# # Compute total amount currently invested&lt;br /&gt;# sub total {&lt;br /&gt;#   my $self = shift;&lt;br /&gt;#   # ... complex computation performed here ...&lt;br /&gt;#   return $total;&lt;br /&gt;# }&lt;br /&gt;class Investor(object):&lt;br /&gt;   def total(self):&lt;br /&gt;       # ... complex computation performed here ...&lt;br /&gt;       return total&lt;br /&gt;&lt;br /&gt;# # Compute total amount currently invested&lt;br /&gt;# { my %cache;&lt;br /&gt;#   sub total {&lt;br /&gt;#     my $self = shift;&lt;br /&gt;#     return $cache{$self} if exists $cache{$self};&lt;br /&gt;#     # ... complex computation performed here ...&lt;br /&gt;#     return $cache{$self} = $total;&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;# One way to simulate this in python would be to&lt;br /&gt;# have a cache "class" variable&lt;br /&gt;class Investor(object):&lt;br /&gt;   _cache = {}&lt;br /&gt;   def total(self):&lt;br /&gt;       # ... complex computation performed here ...\&lt;br /&gt;       key = (self, Investor.total)&lt;br /&gt;       if key not in Investor._cache:&lt;br /&gt;           # ... complex computation performed here ...&lt;br /&gt;           Investor._cache[key] = total&lt;br /&gt;       return Investor._cache[key]&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# # here 90,000 is returned from the cache&lt;br /&gt;# $old_total = $old_object-&gt;total();&lt;br /&gt;# undef $old_object;&lt;br /&gt;# $new_object = Investor-&gt;new();&lt;br /&gt;# $new_total = $new_object-&gt;total();&lt;br /&gt;old_total = old_object.total()&lt;br /&gt;del old_object&lt;br /&gt;new_object = Investor()&lt;br /&gt;new_total = new_object.total()&lt;br /&gt;&lt;br /&gt;# NOTE: in addition to manually invoked "destroy"&lt;br /&gt;# method recommended in the text, we could&lt;br /&gt;# also have __del__ method, though that might not be&lt;br /&gt;# reliable.  Also could just stick in a unix timestamp&lt;br /&gt;# as part of the key so new objects with same memory location&lt;br /&gt;# won't be confusing&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# # Compute total amount currently invested&lt;br /&gt;# sub total {&lt;br /&gt;#   my $self = shift;&lt;br /&gt;#   return $self-&gt;{cached_total} if exists $self-&gt;{cached_total};&lt;br /&gt;#   # ... complex computation performed here ...&lt;br /&gt;#   return $self-&gt;{cached_total} = $total;&lt;br /&gt;# }&lt;br /&gt;def total(self):&lt;br /&gt;   if self.cached_total == None:&lt;br /&gt;       # ... complex computation performed here ...&lt;br /&gt;       self.cached_total = total&lt;br /&gt;   return self.cached_total&lt;br /&gt;&lt;br /&gt;# # Compute total amount currently invested&lt;br /&gt;# { my %cache;&lt;br /&gt;#   sub total {&lt;br /&gt;#     my $self = shift;&lt;br /&gt;#     return $cache{$self} if exists $cache{$self};&lt;br /&gt;#     # ... complex computation performed here ...&lt;br /&gt;#     return $cache{$self} = $total;&lt;br /&gt;#   }&lt;br /&gt;#   sub expire_total {&lt;br /&gt;#     my $self = shift;&lt;br /&gt;#     delete $cache{$self};&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;# sub invest {&lt;br /&gt;#   my ($self, $amount, ...) = @_;&lt;br /&gt;#   $self-&gt;expire_total;&lt;br /&gt;#   ...&lt;br /&gt;# }&lt;br /&gt;class Investor(object):&lt;br /&gt;   _cache = {}&lt;br /&gt;   def total(self):&lt;br /&gt;       # ... complex computation performed here ...\&lt;br /&gt;       key = (self, Investor.total)&lt;br /&gt;       if key not in Investor._cache:&lt;br /&gt;           # ... complex computation performed here ...&lt;br /&gt;           Investor._cache[key] = total&lt;br /&gt;       return Investor._cache[key]&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;   def expire_total(self):&lt;br /&gt;       key = (self, Investor.total)&lt;br /&gt;       del Investor._cache[key]&lt;br /&gt;&lt;br /&gt;   def invest(self, ammount, ...):&lt;br /&gt;       self.expire_total()&lt;br /&gt;       ...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# # Compute total amount currently invested&lt;br /&gt;# sub total {&lt;br /&gt;#   my $self = shift;&lt;br /&gt;#   return $self-&gt;{cached_total} if exists $self-&gt;{cached_total};&lt;br /&gt;#   # ... complex computation performed here ...&lt;br /&gt;#   return $self-&gt;{cached_total} = $total;&lt;br /&gt;# }&lt;br /&gt;# sub invest {&lt;br /&gt;#   my ($self, $amount, ...) = @_;&lt;br /&gt;#   delete $self-&gt;{cached_total};&lt;br /&gt;#   ...&lt;br /&gt;# }&lt;br /&gt;&lt;br /&gt;def total(self):&lt;br /&gt;   if self.cached_total == None:&lt;br /&gt;       # ... complex computation performed here ...&lt;br /&gt;       self.cached_total = total&lt;br /&gt;   return self.cached_total&lt;br /&gt;&lt;br /&gt;def invest(self, amount, ...):&lt;br /&gt;   self.cached_total = None&lt;br /&gt;   ...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub memoize_method {&lt;br /&gt;#   my ($method, $key) = @_;&lt;br /&gt;#   return sub {&lt;br /&gt;#     my $self = shift;&lt;br /&gt;#     return $self-&gt;{$key} if exists $self-&gt;{$key};&lt;br /&gt;#     return $self-&gt;{$key} = $method-&gt;($self, @_);&lt;br /&gt;#   };&lt;br /&gt;# }&lt;br /&gt;# As a decorator&lt;br /&gt;def memoize_method(key):&lt;br /&gt;   def outter(method):&lt;br /&gt;       def inner(self):&lt;br /&gt;           if self.__dict__.get(key) == None:&lt;br /&gt;               print "cache miss:", method, key, self&lt;br /&gt;               self.__dict__[key] = method(self)&lt;br /&gt;           return self.__dict__[key]&lt;br /&gt;       return inner&lt;br /&gt;   return outter&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#*Investor::total = memoize_method(\&amp;Investor::total, 'cached_total');&lt;br /&gt;#$investor_bob-&gt;total;&lt;br /&gt;&lt;br /&gt;@memoize_method("cached_total")&lt;br /&gt;def total(self):&lt;br /&gt;   ...&lt;br /&gt;&lt;br /&gt;investor_bob.total()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# $memoized_total = memoize_method(\&amp;Investor::total, 'cached_total');&lt;br /&gt;# $investor_bob-&gt;$memoized_total;&lt;br /&gt;&lt;br /&gt;# I suppose you could also use the pre-decorator syntax:&lt;br /&gt;&lt;br /&gt;def total(self):&lt;br /&gt;   ...&lt;br /&gt;total = memoize_method("cached_total")(total)&lt;br /&gt;&lt;br /&gt;investor_bob.total()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;### 3.9 Persistent Caches&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# use DB_File;&lt;br /&gt;# sub memoize {&lt;br /&gt;#   my ($func, $keygen, $file) = @_;&lt;br /&gt;#   my %cache;&lt;br /&gt;#   if (defined $file) {&lt;br /&gt;#     tie %cache =&gt; 'DB_File', $file, O_RDWR|O_CREAT, 0666&lt;br /&gt;#       or die "Couldn’t access cache file $file: $!; aborting";&lt;br /&gt;#   }&lt;br /&gt;#   my $stub = sub {&lt;br /&gt;#     my $key = $keygen ? $keygen-&gt;(@_) : join ',', @_;&lt;br /&gt;#     $cache{$key} = $func-&gt;(@_) unless exists $cache{$key};&lt;br /&gt;#     return $cache{$key};&lt;br /&gt;#   };&lt;br /&gt;#   return $stub;&lt;br /&gt;# }&lt;br /&gt;import anydbm&lt;br /&gt;&lt;br /&gt;def memoize(keygen=None, cache_file=None):&lt;br /&gt;   if cache_file:&lt;br /&gt;       cache = anydbm.open(cache_file, 'c')&lt;br /&gt;   else:&lt;br /&gt;       cache = {}&lt;br /&gt;   def outer_func(func):&lt;br /&gt;       def inner_func(*args):&lt;br /&gt;           key = str(args)&lt;br /&gt;           if keygen:&lt;br /&gt;               key = keygen(args)&lt;br /&gt;           if key not in cache:&lt;br /&gt;               cache[key] = func(*args)&lt;br /&gt;           return cache[key]&lt;br /&gt;&lt;br /&gt;       return inner_func&lt;br /&gt;   return outer_func&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub memoize {&lt;br /&gt;#     my ($func, $keygen, $cache) = @_;&lt;br /&gt;#     $cache = {} unless defined $cache;&lt;br /&gt;#     my $stub = sub {&lt;br /&gt;#        my $key = $keygen ? $keygen-&gt;(@_) : join ',', @_;&lt;br /&gt;#        $cache-&gt;{$key} = $func-&gt;(@_) unless exists $cache-&gt;{$key};&lt;br /&gt;#        return $cache-&gt;{$key};&lt;br /&gt;#     };&lt;br /&gt;#     return $stub;&lt;br /&gt;# }&lt;br /&gt;def memoize(keygen=None, cache=None):&lt;br /&gt;   if cache == None:&lt;br /&gt;       cache = {}&lt;br /&gt;   def outer_func(func):&lt;br /&gt;       def inner_func(*args):&lt;br /&gt;           key = str(args)&lt;br /&gt;           if keygen:&lt;br /&gt;               key = keygen(args)&lt;br /&gt;           if key not in cache:&lt;br /&gt;               cache[key] = func(*args)&lt;br /&gt;           return cache[key]&lt;br /&gt;&lt;br /&gt;       return inner_func&lt;br /&gt;   return outer_func&lt;br /&gt;&lt;br /&gt;### 3.10 Alternatives To Memoization&lt;br /&gt;&lt;br /&gt;# @sorted_numbers = sort { $a &lt;=&gt; $b } @numbers;&lt;br /&gt;# This is default behavior for sort&lt;br /&gt;sorted_numbers = sorted(numbers)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# @sorted_numbers = sort numerically @numbers;&lt;br /&gt;# sub numerically { $a &lt;=&gt; $b }&lt;br /&gt;sorted_numbers = sorted(numbers, cmp=numerically)&lt;br /&gt;def numerically(a,b):&lt;br /&gt;   return cmp(a,b)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# @sorted_dates = sort chronologically @dates;&lt;br /&gt;&lt;br /&gt;# %m2n =&lt;br /&gt;#    ( jan =&gt;  1, feb =&gt;  2, mar =&gt;  3,&lt;br /&gt;#      apr =&gt;  4, may =&gt;  5, jun =&gt;  6,&lt;br /&gt;#      jul =&gt;  7, aug =&gt;  8, sep =&gt;  9,&lt;br /&gt;#      oct =&gt; 10, nov =&gt; 11, dec =&gt; 12, );&lt;br /&gt;&lt;br /&gt;# sub chronologically {&lt;br /&gt;#   my ($am, $ad, $ay) =&lt;br /&gt;#     ($a =~ /(\w{3}) (\d+), (\d+)/);&lt;br /&gt;#   my ($bm, $bd, $by) =&lt;br /&gt;#     ($b =~ /(\w{3}) (\d+), (\d+)/);&lt;br /&gt;#              $ay  &lt;=&gt;         $by&lt;br /&gt;#   || $m2n{lc $am} &lt;=&gt; $m2n{lc $bm}&lt;br /&gt;#   ||         $ad  &lt;=&gt;         $bd;&lt;br /&gt;# }&lt;br /&gt;&lt;br /&gt;sorted_dates = sorted(dates, chronologically)&lt;br /&gt;&lt;br /&gt;m2n = {&lt;br /&gt;   "jan" : 1,  "feb" : 2,  "mar" : 3,&lt;br /&gt;   "apr" : 4,  "may" : 5,  "jun" : 6,&lt;br /&gt;   "jul" : 7,  "aug" : 8,  "sep" : 9,&lt;br /&gt;   "oct" : 10, "nov" : 11, "dec" : 12&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;def chronologically(a,b):&lt;br /&gt;   am, ad, ay = re.search("(\w{3}) (\d+), (\d+)", a).groups()&lt;br /&gt;   bm, bd, by = re.search("(\w{3}) (\d+), (\d+)", b).groups()&lt;br /&gt;&lt;br /&gt;   return (cmp(int(ay), int(by)) or&lt;br /&gt;           cmp(m2n[am.lower()],  m2n[bm.lower()]) or&lt;br /&gt;           cmp(int(ad), int(bd)))&lt;br /&gt;&lt;br /&gt;#     ## or more simply&lt;br /&gt;#     return cmp((int(ay), m2n[am.lower()], int(ad)),&lt;br /&gt;#                (int(by), m2n[bm.lower()], int(bd)))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# @sorted_dates = sort chronologically @dates;&lt;br /&gt;# %m2n =&lt;br /&gt;#    ( jan =&gt;  1, feb =&gt;  2, mar =&gt;  3,&lt;br /&gt;#      apr =&gt;  4, may =&gt;  5, jun =&gt;  6,&lt;br /&gt;#      jul =&gt;  7, aug =&gt;  8, sep =&gt;  9,&lt;br /&gt;#      oct =&gt; 10, nov =&gt; 11, dec =&gt; 12, );&lt;br /&gt;# sub chronologically {&lt;br /&gt;#   my ($am, $ad, $ay) = split_date($a);&lt;br /&gt;#   my ($bm, $bd, $by) = split_date($b);&lt;br /&gt;#              $ay  &lt;=&gt;         $by&lt;br /&gt;#   || $m2n{lc $am} &lt;=&gt; $m2n{lc $bm}&lt;br /&gt;#   ||         $ad  &lt;=&gt;         $bd;&lt;br /&gt;# }&lt;br /&gt;# sub split_date {&lt;br /&gt;#   $_[0] =~ /(\w{3}) (\d+), (\d+)/;&lt;br /&gt;# }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;sorted_dates = sorted(dates, chronologically)&lt;br /&gt;&lt;br /&gt;m2n = {&lt;br /&gt;   "jan" : 1,  "feb" : 2,  "mar" : 3,&lt;br /&gt;   "apr" : 4,  "may" : 5,  "jun" : 6,&lt;br /&gt;   "jul" : 7,  "aug" : 8,  "sep" : 9,&lt;br /&gt;   "oct" : 10, "nov" : 11, "dec" : 12&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;def chronologically(a,b):&lt;br /&gt;   am, ad, ay = split_date(a)&lt;br /&gt;   bm, bd, by = split_date(b)&lt;br /&gt;&lt;br /&gt;   return (cmp(int(ay), int(by)) or&lt;br /&gt;           cmp(m2n[am.lower()],  m2n[bm.lower()]) or&lt;br /&gt;           cmp(int(ad), int(bd)))&lt;br /&gt;&lt;br /&gt;def split_date(dt):&lt;br /&gt;   return re.search("(\w{3}) (\d+), (\d+)", dt).groups()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;@sorted_dates = sort chronologically @dates;  COD E L IB RARY&lt;br /&gt;                                            chrono-3&lt;br /&gt;# %m2n =&lt;br /&gt;#    ( jan =&gt;  1, feb =&gt;  2, mar =&gt;  3,&lt;br /&gt;#      apr =&gt;  4, may =&gt;  5, jun =&gt;  6,&lt;br /&gt;#      jul =&gt;  7, aug =&gt;  8, sep =&gt;  9,&lt;br /&gt;#      oct =&gt; 10, nov =&gt; 11, dec =&gt; 12, );&lt;br /&gt;&lt;br /&gt;# sub chronologically {&lt;br /&gt;#   date_to_string($a) cmp date_to_string($b)&lt;br /&gt;# }&lt;br /&gt;&lt;br /&gt;# sub date_to_string {&lt;br /&gt;#   my ($m, $d, $y) = ($_[0] =~ /(\w{3}) (\d+), (\d+)/);&lt;br /&gt;#   sprintf "%04d%02d%02d", $y, $m2n{lc $m}, $d;&lt;br /&gt;# }&lt;br /&gt;&lt;br /&gt;m2n = {&lt;br /&gt;   "jan" : 1,  "feb" : 2,  "mar" : 3,&lt;br /&gt;   "apr" : 4,  "may" : 5,  "jun" : 6,&lt;br /&gt;   "jul" : 7,  "aug" : 8,  "sep" : 9,&lt;br /&gt;   "oct" : 10, "nov" : 11, "dec" : 12&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;def chronologically(a,b):&lt;br /&gt;   return cmp(date_to_string(a), date_to_string(b))&lt;br /&gt;&lt;br /&gt;def date_to_string(dt):&lt;br /&gt;   m, d, y = re.search("(\w{3}) (\d+), (\d+)", dt).groups()&lt;br /&gt;   return "%04d%02d%02d" % (int(y), m2n[m.lower()], int(d))&lt;br /&gt;&lt;br /&gt;# { my %cache;&lt;br /&gt;#   sub chronologically {&lt;br /&gt;#     ($cache{$a} ||= date_to_string($a))&lt;br /&gt;#        cmp&lt;br /&gt;#     ($cache{$b} ||= date_to_string($b))&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;# In python we can't do the scope trick with cache&lt;br /&gt;# but we ignore that for now&lt;br /&gt;CACHE = {}&lt;br /&gt;def chronologically(a, b):&lt;br /&gt;   if a not in CACHE:&lt;br /&gt;       CACHE[a] = date_to_string(a)&lt;br /&gt;   if b not in CACHE:&lt;br /&gt;       CACHE[b] = date_to_string(b)&lt;br /&gt;&lt;br /&gt;   return cmp(CACHE[a], CACHE[b])&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# { my %cache;&lt;br /&gt;#   sub by_total_invested {&lt;br /&gt;#     ($cache{$a} ||= total_invested($a))&lt;br /&gt;#        &lt;=&gt;&lt;br /&gt;#     ($cache{$b} ||= total_invested($b))&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;CACHE = {}&lt;br /&gt;def by_total_invested(a,b):&lt;br /&gt;   if not CACHE.get(a):&lt;br /&gt;       CACHE[a] = total_invested(a)&lt;br /&gt;   if not CACHE.get(b):&lt;br /&gt;       CACHE[b] = total_invested(b)&lt;br /&gt;&lt;br /&gt;   return cmp(CACHE[a], CACHE[b])&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# { my %cache;&lt;br /&gt;#   sub by_total_invested {&lt;br /&gt;#    (exists $cache{$a} ? $cache{$a} : ($cache{$a} = total_invested($a)))&lt;br /&gt;#        &lt;=&gt;&lt;br /&gt;#    (exists $cache{$b} ? $cache{$b} : ($cache{$b} = total_invested($b)))&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;CACHE = {}&lt;br /&gt;def by_total_invested(a,b):&lt;br /&gt;   if a not in CACHE:&lt;br /&gt;       CACHE[a] = total_invested(a)&lt;br /&gt;   if b not in CACHE:&lt;br /&gt;       CACHE[b] = total_invested(b)&lt;br /&gt;&lt;br /&gt;   return cmp(CACHE[a], CACHE[b])&lt;br /&gt;&lt;br /&gt;# { my %cache;&lt;br /&gt;#   sub by_total_invested {&lt;br /&gt;#     ($cache{$a} ||= total_invested($a) || "0e0")&lt;br /&gt;#        &lt;=&gt;&lt;br /&gt;#     ($cache{$b} ||= total_invested($b) || "0e0")&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;# I can't think of a reason to try to emulate this trick&lt;br /&gt;# (or how, frankly)&lt;br /&gt;&lt;br /&gt;### 3.11 Evangelism&lt;br /&gt;&lt;br /&gt;### 3.12 The Benefits of Speed&lt;br /&gt;&lt;br /&gt;# sub function {&lt;br /&gt;#   if (++$CALLS == 100) { memoize 'function'}&lt;br /&gt;#   ...&lt;br /&gt;# }&lt;br /&gt;def function():&lt;br /&gt;   CALL += 1&lt;br /&gt;   if CALLS == 100:&lt;br /&gt;       function = memoize(function)&lt;br /&gt;   ...&lt;br /&gt;&lt;br /&gt;# use Time::HiRes 'time';&lt;br /&gt;# my (%time, %calls);&lt;br /&gt;# sub profile {&lt;br /&gt;#   my ($func, $name) = @_;&lt;br /&gt;#   my $stub = sub {&lt;br /&gt;#     my $start = time;&lt;br /&gt;#     my $return = $func-&gt;(@_);&lt;br /&gt;#     my $end = time;&lt;br /&gt;#     my $elapsed = $end - $start;&lt;br /&gt;#     $calls{$name} += 1;&lt;br /&gt;#     $time{$name} += $elapsed;&lt;br /&gt;#     return $return;&lt;br /&gt;#   };&lt;br /&gt;#   return $stub;&lt;br /&gt;# }&lt;br /&gt;# This is another good case for a decorator&lt;br /&gt;import time&lt;br /&gt;time_ = {}&lt;br /&gt;calls = {}&lt;br /&gt;def profile(func):&lt;br /&gt;   calls.setdefault(func.__name__, 0)&lt;br /&gt;   time_.setdefault(func.__name__, 0)&lt;br /&gt;   def stub(*args):&lt;br /&gt;       start = time.time()&lt;br /&gt;       return_ = func(*args)&lt;br /&gt;       end = time.time()&lt;br /&gt;       elapsed = end - start&lt;br /&gt;       calls[func.__name__] += 1&lt;br /&gt;       time_[func.__name__] += elapsed&lt;br /&gt;       return return_&lt;br /&gt;   return stub&lt;br /&gt;&lt;br /&gt;@profile&lt;br /&gt;def foo(x):&lt;br /&gt;   return x&lt;br /&gt;&lt;br /&gt;# END {&lt;br /&gt;#   printf STDERR "%-12s %9s %6s\n", "Function", "# calls", "Elapsed";&lt;br /&gt;#   for my $name (sort {$time{$b} &lt;=&gt; $time{$a}} (keys %time)) {&lt;br /&gt;#     printf STDERR "%-12s %9d %6.2f\n", $name, $calls{$name}, $time{$name};&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;print &gt;&gt;sys.stderr, "%-12s %9s %6s\n" % ("Function", "# calls", "Elapsed")&lt;br /&gt;for name, _time in sorted(time_.items(), key=lambda x: x[1]):&lt;br /&gt;   print &gt;&gt;sys.stderr, "%-12s %9d %6.2f\n" % (name, calls[name], time_[name])&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-4969800666607986577?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/4969800666607986577/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=4969800666607986577' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/4969800666607986577'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/4969800666607986577'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2009/01/higher-order-perl-python-style-chapter_23.html' title='Higher Order Perl (Python Style) : Chapter 3 - Caching and Memoization'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-5180127173349759313</id><published>2009-01-18T13:45:00.000-08:00</published><updated>2009-01-18T13:46:58.085-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='99 problems'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>99 problems - python - 57</title><content type='html'>Binary search trees (dictionaries)&lt;br /&gt;&lt;br /&gt;Use the predicate add/3, developed in chapter 4 of the course, to write a predicate to construct a binary search tree from a list of integer numbers.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;# Example:&lt;br /&gt;&lt;br /&gt;# * construct([3,2,5,7,1],T).&lt;br /&gt;# T = t(3, t(2, t(1, nil, nil), nil), t(5, nil, t(7, nil, nil)))&lt;br /&gt;&lt;br /&gt;# Then use this predicate to test the solution of the problem P56.&lt;br /&gt;&lt;br /&gt;# Example:&lt;br /&gt;&lt;br /&gt;# * test-symmetric([5,3,18,1,4,12,21]).&lt;br /&gt;# Yes&lt;br /&gt;# * test-symmetric([3,2,5,7,1]).&lt;br /&gt;# No&lt;br /&gt;def construct(nums):&lt;br /&gt;    if not nums:&lt;br /&gt;        return None&lt;br /&gt;    ordered_nums = sorted(nums)&lt;br /&gt;    center_index = (len(nums) / 2) &lt;br /&gt;    return (ordered_nums[center_index], construct(ordered_nums[:center_index]), construct(ordered_nums[center_index+1:]))&lt;br /&gt;&lt;br /&gt;def test_symmetric(nums):&lt;br /&gt;    return symmetric_binary_tree(construct(nums))&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-5180127173349759313?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/5180127173349759313/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=5180127173349759313' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/5180127173349759313'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/5180127173349759313'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2009/01/99-problems-python-57.html' title='99 problems - python - 57'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-7597937517580617092</id><published>2009-01-14T20:46:00.000-08:00</published><updated>2009-01-14T20:54:18.507-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='functional programming'/><category scheme='http://www.blogger.com/atom/ns#' term='higher order perl'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='hop'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><title type='text'>Higher Order Perl (Python Style) : Chapter 2</title><content type='html'>&lt;pre&gt;&lt;br /&gt;#&lt;br /&gt;# Dispatch Tables&lt;br /&gt;#&lt;br /&gt;&lt;br /&gt;# sub read_config {&lt;br /&gt;#   my ($filename) = @_;&lt;br /&gt;#   open my($CF), $filename or return; # Failure&lt;br /&gt;#   while (&lt;$CF&gt;) {&lt;br /&gt;#     chomp;&lt;br /&gt;#     my ($directive, $rest) = split /\s+/, $_, 2;&lt;br /&gt;#     if ($directive eq 'CHDIR') {&lt;br /&gt;#       chdir($rest) or die "Couldn't chdir to '$rest': $!; aborting";&lt;br /&gt;#     } elsif ($directive eq 'LOGFILE') {&lt;br /&gt;#       open STDERR, "&gt;&gt;", $rest&lt;br /&gt;#         or die "Couldn't open log file '$rest': $!; aborting";&lt;br /&gt;#     } elsif ($directive eq 'VERBOSITY') {&lt;br /&gt;#       $VERBOSITY = $rest;&lt;br /&gt;#     } elsif ($directive eq ...) {&lt;br /&gt;#       ...&lt;br /&gt;#     } ...&lt;br /&gt;#     } else {&lt;br /&gt;#       die "Unrecognized directive $directive on line $. of $filename; aborting";&lt;br /&gt;#     }&lt;br /&gt;#   }&lt;br /&gt;#   return 1; # Success&lt;br /&gt;# }&lt;br /&gt;def read_config(filename):&lt;br /&gt;    global VERBOSITY&lt;br /&gt;    for i, line in enumerate(file(filename)):&lt;br /&gt;        line = line.strip()&lt;br /&gt;        directive, rest = line.split(None, 1)&lt;br /&gt;        try:&lt;br /&gt;            if directive == "CHDIR":&lt;br /&gt;                os.chdir(rest)&lt;br /&gt;            elif directive == "LOGFILE":&lt;br /&gt;                sys.stderr = file(rest, "w")&lt;br /&gt;            elif directive == "VERBOSITY":&lt;br /&gt;                VERBOSITY = rest&lt;br /&gt;            elif directive == ...:&lt;br /&gt;                ...&lt;br /&gt;            else:&lt;br /&gt;                sys.exit("Unrecognized directive %s in line %s of %s; aborting" % (directive, i, filename))&lt;br /&gt;        except StandardError, why:&lt;br /&gt;            sys.exit(why)&lt;br /&gt;    return 1 # success&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub read_config {                               &lt;br /&gt;#   my ($filename, $actions) = @_;&lt;br /&gt;#   open my($CF), $filename or return; # Failure&lt;br /&gt;#   while (&lt;$CF&gt;) {&lt;br /&gt;#     chomp;&lt;br /&gt;#     my ($directive, $rest) = split /\s+/, $_, 2;&lt;br /&gt;#     if (exists $actions-&gt;{$directive}) {&lt;br /&gt;#       $actions-&gt;{$directive}-&gt;($rest);&lt;br /&gt;#     } else {&lt;br /&gt;#       die "Unrecognized directive $directive on line $. of $filename; aborting";&lt;br /&gt;#     }&lt;br /&gt;#   }&lt;br /&gt;#   return 1; # Success&lt;br /&gt;# }&lt;br /&gt;def read_config(filename, actions):&lt;br /&gt;    for i, line in enumerate(file(filename)):&lt;br /&gt;        line = line.strip()&lt;br /&gt;        directive, rest = line.split(None, 1)&lt;br /&gt;        if directive in actions:&lt;br /&gt;            actions[directive](rest)&lt;br /&gt;        else:&lt;br /&gt;            sys.exit("Unrecognized directive %s in line %s of %s; aborting" % (directive, i, filename))&lt;br /&gt;    return 1 # success&lt;br /&gt;&lt;br /&gt;# $dispatch_table =&lt;br /&gt;# { CHDIR      =&gt; \&amp;change_dir,&lt;br /&gt;#   LOGFILE    =&gt; \&amp;open_log_file,&lt;br /&gt;#   VERBOSITY  =&gt; \&amp;set_verbosity,&lt;br /&gt;#   ...        =&gt;  ...,&lt;br /&gt;# };&lt;br /&gt;dispatch_table = {&lt;br /&gt;    "CHDIR"     : change_dir,&lt;br /&gt;    "LOGFILE"   : open_log_file,&lt;br /&gt;    "VERBOSITY" : set_verbosity,&lt;br /&gt;    ...         : ...,&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;# sub change_dir {&lt;br /&gt;#   my ($dir) = @_;&lt;br /&gt;#   chdir($dir)&lt;br /&gt;#     or die "Couldn't chdir to '$dir': $!; aborting";&lt;br /&gt;# }&lt;br /&gt;# sub open_log_file {&lt;br /&gt;#   open STDERR, "&gt;&gt;", $_[0]&lt;br /&gt;#     or die "Couldn't open log file '$_[0]': $!; aborting";&lt;br /&gt;# }&lt;br /&gt;# sub set_verbosity {&lt;br /&gt;#   $VERBOSITY = shift&lt;br /&gt;# }&lt;br /&gt;def change_dir(dir):&lt;br /&gt;    try:&lt;br /&gt;        os.chdir(dir)&lt;br /&gt;    except OSError, why:&lt;br /&gt;        sys.exit(why)        &lt;br /&gt;def open_log_file(log_file):&lt;br /&gt;    try:&lt;br /&gt;        sys.stderr = file(log_file, "w")&lt;br /&gt;    except OSError, why:&lt;br /&gt;        sys.exit(why)&lt;br /&gt;def set_verbosity(verbosity):&lt;br /&gt;    global VERBOSITY&lt;br /&gt;    VERBOSITY = verbosity&lt;br /&gt;&lt;br /&gt;# $dispatch_table =&lt;br /&gt;#   { CHDIR      =&gt; sub { my ($dir) = @_;&lt;br /&gt;#                        chdir($dir) or&lt;br /&gt;#                          die "Couldn't chdir to '$dir' $!; aborting";&lt;br /&gt;#                                                       :&lt;br /&gt;#                       },&lt;br /&gt;#     LOGFILE    =&gt; sub { open STDERR, "&gt;&gt;", $_[0] or&lt;br /&gt;#                           die "Couldn't open log file '$_[0]': $!; aborting";&lt;br /&gt;#                       },&lt;br /&gt;#     VERBOSITY  =&gt; sub { $VERBOSITY = shift },&lt;br /&gt;#     ...        =&gt; ...,&lt;br /&gt;# };&lt;br /&gt;&lt;br /&gt;# This doesn't map well since lambda doesn't allow&lt;br /&gt;# statements. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# 'DEFINE' =&gt; \&amp;define_config_directive,&lt;br /&gt;"DEFINE" : define_config_directive,&lt;br /&gt;&lt;br /&gt;# sub define_config_directive {&lt;br /&gt;#   my $rest = shift;&lt;br /&gt;#   $rest =~ s/∧ \s+//;&lt;br /&gt;#   my ($new_directive, $def_txt) = split /\s+/, $rest, 2;&lt;br /&gt;#   if (exists $CONFIG_DIRECTIVE_TABLE{$new_directive}) {&lt;br /&gt;#     warn "$new_directive already defined; skipping.\n";&lt;br /&gt;#     return;&lt;br /&gt;#   }&lt;br /&gt;#   my $def = eval "sub { $def_txt }";&lt;br /&gt;#   if (not defined $def) {&lt;br /&gt;#     warn "Could not compile definition for '$new_directive': $@; skipping.\n";&lt;br /&gt;#     return;&lt;br /&gt;#   }&lt;br /&gt;#   $CONFIG_DIRECTIVE_TABLE{$new_directive} = $def;&lt;br /&gt;# }&lt;br /&gt;&lt;br /&gt;def define_config_directive(rest):&lt;br /&gt;    rest = rest.strip()&lt;br /&gt;    new_directive, def_txt = rest.split(None, 1)&lt;br /&gt;&lt;br /&gt;    if new_directive in CONFIG_DIRECTIVE_TABLE:&lt;br /&gt;        print "%s already defined; skipping." % new_directive&lt;br /&gt;        return&lt;br /&gt;&lt;br /&gt;    try:&lt;br /&gt;        _def = eval("""lambda x: %s""" % def_txt)&lt;br /&gt;    except StandardError, why:&lt;br /&gt;        print "Could not compile definition for '%s': %s" % (new_directive, why)&lt;br /&gt;        return&lt;br /&gt;&lt;br /&gt;    CONFIG_DIRECTIVE_TABLE[new_directive] = _def&lt;br /&gt;&lt;br /&gt;    &lt;br /&gt;# This doesn't map well to the python way of doing things since&lt;br /&gt;# lambdas are sort of limited.  Instead we would&lt;br /&gt;# probably just define the functions with a name directly.&lt;br /&gt;# On the other hand if you really must doing everything "lambda style"&lt;br /&gt;# you can probably get farther than you'd first guess with clever uses&lt;br /&gt;# of and/or connectors in your lambda expression&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub read_config {&lt;br /&gt;#   my ($filename, $actions) = @_;&lt;br /&gt;#   open my($CF), $filename or return; # Failure&lt;br /&gt;#   while (&lt;$CF&gt;) {&lt;br /&gt;#     chomp;&lt;br /&gt;#     my ($directive, $rest) = split /\s+/, $_, 2;&lt;br /&gt;#     if (exists $actions-&gt;{$directive}) {&lt;br /&gt;#       $actions-&gt;{$directive}-&gt;($rest, $actions);&lt;br /&gt;#     } else {&lt;br /&gt;#       die "Unrecognized directive $directive on line $. of $filename; aborting";&lt;br /&gt;#     }&lt;br /&gt;#   }&lt;br /&gt;#   return 1; # Success&lt;br /&gt;# }&lt;br /&gt;def read_config(filename, actions):&lt;br /&gt;    for i, line in enumerate(file(filename)):&lt;br /&gt;        line = line.strip()&lt;br /&gt;        directive, rest = line.split(None, 1)&lt;br /&gt;        if directive in actions:&lt;br /&gt;            actions[directive](rest, actions)&lt;br /&gt;        else:&lt;br /&gt;            sys.exit("Unrecognized directive %s in line %s of %s; aborting" % (directive, i, filename))&lt;br /&gt;    return 1 # success&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub define_config_directive {&lt;br /&gt;#   my ($rest, $dispatch_table) = @_;&lt;br /&gt;#   $rest =~ s/∧ \s+//;&lt;br /&gt;#   my ($new_directive, $def_txt) = split /\s+/, $rest, 2;&lt;br /&gt;#   if (exists $dispatch_table-&gt;{$new_directive}) {&lt;br /&gt;#     warn "$new_directive already defined; skipping.\n";&lt;br /&gt;#     return;&lt;br /&gt;#   }&lt;br /&gt;#   my $def = eval "sub { $def_txt }";&lt;br /&gt;#   if (not defined $def) {&lt;br /&gt;#     warn "Could not compile definition for '$new_directive': $@; skipping.\n";&lt;br /&gt;#     return;&lt;br /&gt;#   }&lt;br /&gt;#   $dispatch_table-&gt;{$new_directive} = $def;&lt;br /&gt;# }&lt;br /&gt;def define_config_directive(rest, dispatch_table):&lt;br /&gt;    rest = rest.strip()&lt;br /&gt;    new_directive, def_txt = rest.split(None, 1)&lt;br /&gt;&lt;br /&gt;    if new_directive in dispatch_table:&lt;br /&gt;        print "%s already defined; skipping." % new_directive&lt;br /&gt;        return&lt;br /&gt;&lt;br /&gt;    try:&lt;br /&gt;        _def = eval("""lambda x: %s""" % def_txt)&lt;br /&gt;    except StandardError, why:&lt;br /&gt;        print "Could not compile definition for '%s': %s" % (new_directive, why)&lt;br /&gt;        return&lt;br /&gt;&lt;br /&gt;    dispatch_table[new_directive] = _def&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub read_config {&lt;br /&gt;#   my ($filename, $actions, $user_param) = @_;&lt;br /&gt;#   open my($CF), $filename or return; # Failure&lt;br /&gt;#   while (&lt;$CF&gt;) {&lt;br /&gt;#     my ($directive, $rest) = split /\s+/, $_, 2;&lt;br /&gt;#     if (exists $actions-&gt;{$directive}) {&lt;br /&gt;#       $actions-&gt;{$directive}-&gt;($rest, $user_param, $actions);&lt;br /&gt;#     } else {&lt;br /&gt;#       die "Unrecognized directive $directive on line $. of $filename; aborting";&lt;br /&gt;#     }&lt;br /&gt;#   }&lt;br /&gt;#   return 1; # Success&lt;br /&gt;# }&lt;br /&gt;def read_config(filename, actions, user_param):&lt;br /&gt;    for i, line in enumerate(file(filename)):&lt;br /&gt;        line = line.strip()&lt;br /&gt;        directive, rest = line.split(None, 1)&lt;br /&gt;        if directive in actions:&lt;br /&gt;            actions[directive](rest, user_param, actions)&lt;br /&gt;        else:&lt;br /&gt;            sys.exit("Unrecognized directive %s in line %s of %s; aborting" % (directive, i, filename))&lt;br /&gt;    return 1 # success&lt;br /&gt;&lt;br /&gt;#read_config($filename, $dispatch_table, \@dirs);&lt;br /&gt;read_config(filename, dispatch_table, dirs)&lt;br /&gt;&lt;br /&gt;#read_config($filename, $dispatch_table, []);&lt;br /&gt;read_config(filename, dispatch_table, [])&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub read_config {&lt;br /&gt;#   my ($filename, $actions, $user_param) = @_;&lt;br /&gt;#   open my($CF), $filename or return; # Failure&lt;br /&gt;#   while (&lt;$CF&gt;) {&lt;br /&gt;#     my ($directive, $rest) = split /\s+/, $_, 2;&lt;br /&gt;#     if (exists $actions-&gt;{$directive}) {&lt;br /&gt;#       $actions-&gt;{$directive}-&gt;($directive, $rest, $actions, $user_param);&lt;br /&gt;#     } else {&lt;br /&gt;#       die "Unrecognized directive $directive on line $. of $filename; aborting";&lt;br /&gt;#     }&lt;br /&gt;#   }&lt;br /&gt;#   return 1; # Success&lt;br /&gt;# }&lt;br /&gt;def read_config(filename, actions, user_param):&lt;br /&gt;    for i, line in enumerate(file(filename)):&lt;br /&gt;        line = line.strip()&lt;br /&gt;        directive, rest = line.split(None, 1)&lt;br /&gt;        if directive in actions:&lt;br /&gt;            actions[directive](directive, rest, user_param, actions)&lt;br /&gt;        else:&lt;br /&gt;            sys.exit("Unrecognized directive %s in line %s of %s; aborting" % (directive, i, filename))&lt;br /&gt;    return 1 # success&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub set_var {&lt;br /&gt;#   my ($var, $val) = @_;&lt;br /&gt;#   $$var = $val;&lt;br /&gt;# }&lt;br /&gt;# OK, this is pretty unnatural at this point&lt;br /&gt;# we would almost certianly work instead with a&lt;br /&gt;# global or passed in dictionary&lt;br /&gt;# (not tested)&lt;br /&gt;def set_var(var, val):&lt;br /&gt;    exec "global %s" % var&lt;br /&gt;    exec "%s = %r" % (var, val)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub set_var {&lt;br /&gt;#   my ($var, $val, undef, $config_hash) = @_;&lt;br /&gt;#   $config_hash-&gt;{$var} = $val;&lt;br /&gt;# }&lt;br /&gt;def set_var(var, val, _, config_hash):&lt;br /&gt;    config_hash[var] = val&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub open_input_file {&lt;br /&gt;#   my ($handle, $filename) = @_;&lt;br /&gt;#   unless (open $handle, $filename) {&lt;br /&gt;#     warn "Couldn't open $handle file '$filename': $!; ignoring.\n";&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;# yet more unnatural hackery (not tested)&lt;br /&gt;# again, we'd probably work with a global or passed in dictionary&lt;br /&gt;def open_input_file(handle, filename):&lt;br /&gt;    exec "global %s" % handle&lt;br /&gt;    exec """&lt;br /&gt;try:&lt;br /&gt;    %s = file('%s')&lt;br /&gt;except StandardError, why:&lt;br /&gt;    print "Counldn't open %s file '%s': %%s; ignoring." %% why&lt;br /&gt;return """ % (handle, filename, handle, filename)&lt;br /&gt;    &lt;br /&gt;&lt;br /&gt;# sub read_config {&lt;br /&gt;#   my ($filename, $actions, $userparam) = @_;&lt;br /&gt;#   open my($CF), $filename or return; # Failure&lt;br /&gt;#   while (&lt;$CF&gt;) {&lt;br /&gt;#     chomp;&lt;br /&gt;#     my ($directive, $rest) = split /\s+/, $_, 2;&lt;br /&gt;#     my $action = $actions-&gt;{$directive} || $actions-&gt;{_DEFAULT_};&lt;br /&gt;#     if ($action) {&lt;br /&gt;#       $action-&gt;($directive, $rest, $actions, $userparam);&lt;br /&gt;#     } else {&lt;br /&gt;#       die "Unrecognized directive $directive on line $. of $filename; aborting";&lt;br /&gt;#     }&lt;br /&gt;#   }&lt;br /&gt;#   return 1; # Success&lt;br /&gt;# }&lt;br /&gt;def read_config(filename, actions, user_param):&lt;br /&gt;    for i, line in enumerate(file(filename)):&lt;br /&gt;        line = line.strip()&lt;br /&gt;        directive, rest = line.split(None, 1)&lt;br /&gt;        action = actions[directive] or actions["__DEFAULT__"]&lt;br /&gt;        if action:&lt;br /&gt;            action[directive](directive, rest, user_param, actions)&lt;br /&gt;        else:&lt;br /&gt;            sys.exit("Unrecognized directive %s in line %s of %s; aborting" % (directive, i, filename))&lt;br /&gt;    return 1 # success&lt;br /&gt;&lt;br /&gt;# sub no_such_directive {&lt;br /&gt;#   my ($directive) = @_;&lt;br /&gt;#   warn "Unrecognized directive $directive at line $.; ignoring.\n";&lt;br /&gt;# }&lt;br /&gt;# python doesn't have a trivial way to get the line number&lt;br /&gt;# so we leave that out, but may get close with inspect&lt;br /&gt;def no_such_directive(directive):&lt;br /&gt;    print "Unrecognized directive %s; ignoring." % directive&lt;br /&gt;&lt;br /&gt;# sub no_such_directive {&lt;br /&gt;#   my ($bad, $rest, $table) = @_;&lt;br /&gt;#   my ($best_match, $best_score);&lt;br /&gt;#   for my $good (keys %$table) {&lt;br /&gt;#     my $score = score_match($bad, $good);&lt;br /&gt;#     if ($score &gt; $best_score) {&lt;br /&gt;#       $best_score = $score;&lt;br /&gt;#       $best_match = $good;&lt;br /&gt;#     }&lt;br /&gt;#   }&lt;br /&gt;#   warn "Unrecognized directive $bad at line $.;\n";&lt;br /&gt;#   warn "\t(perhaps you meant $best_match?)\n";&lt;br /&gt;# }&lt;br /&gt;def no_such_directive(bad, rest, table):&lt;br /&gt;    best_match = None&lt;br /&gt;    best_score = 0&lt;br /&gt;    &lt;br /&gt;    for good in table:&lt;br /&gt;        score = score_match(bad, good)&lt;br /&gt;        if score &gt; best_score:&lt;br /&gt;            best_score = score&lt;br /&gt;            best_match = good&lt;br /&gt;&lt;br /&gt;    print "Unrecognized directive %s." % bad&lt;br /&gt;    print "\t(perhaps you meant %s?)" % best_match&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# $address_actions =&lt;br /&gt;#   { _DEFAULT_ =&gt; sub { my ($id, $addr, $act, $aref) = @_;&lt;br /&gt;#                        push @$aref, [$id, $addr];&lt;br /&gt;#                      },&lt;br /&gt;#   };&lt;br /&gt;# read_config($ADDRESS_FILE, $address_actions, \@address_array);&lt;br /&gt;address_actions = {&lt;br /&gt;    "__DEFAULT__" : lambda id, addr, act, aref: aref.append([id, addr])&lt;br /&gt;    }&lt;br /&gt;read_config(ADDRESS_FILE, address_actions, address_array)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# my $result = evaluate($ARGV[0]);&lt;br /&gt;# print "Result: $result\n";&lt;br /&gt;# sub evaluate {&lt;br /&gt;#   my @stack;&lt;br /&gt;#   my ($expr) = @_;&lt;br /&gt;#   my @tokens = split /\s+/, $expr;&lt;br /&gt;#   for my $token (@tokens) {&lt;br /&gt;#     if ($token =~ /∧ \d+$/) { # It's a number&lt;br /&gt;#       push @stack, $token;&lt;br /&gt;#     } elsif ($token eq '+') {&lt;br /&gt;#        push @stack, pop(@stack) + pop(@stack);&lt;br /&gt;#     } elsif ($token eq '-') {&lt;br /&gt;#        my $s = pop(@stack);&lt;br /&gt;#        push @stack, pop(@stack) - $s&lt;br /&gt;#     } elsif ($token eq '*') {&lt;br /&gt;#        push @stack, pop(@stack) * pop(@stack);&lt;br /&gt;#     } elsif ($token eq '/') {&lt;br /&gt;#        my $s = pop(@stack);&lt;br /&gt;#        push @stack, pop(@stack) / $s&lt;br /&gt;#     } else {&lt;br /&gt;#       die "Unrecognized token '$token'; aborting";&lt;br /&gt;#     }&lt;br /&gt;#    }&lt;br /&gt;#   return pop(@stack);&lt;br /&gt;# }&lt;br /&gt;result = evaluate(sys.argv[1])&lt;br /&gt;print "Result: %s" % result&lt;br /&gt;def evaluate(expr):&lt;br /&gt;    stack = []&lt;br /&gt;    tokens = expr.split()&lt;br /&gt;    for token in tokens:&lt;br /&gt;        if token.isdigit():&lt;br /&gt;            stack.insert(0, int(token))&lt;br /&gt;        elif token == "+":&lt;br /&gt;            stack.insert(0, (stack.pop(0) + stack.pop(0)))&lt;br /&gt;        elif token == "-":&lt;br /&gt;            s = stack.pop(1)&lt;br /&gt;            stack.insert(0, (stack.pop(0) - s))&lt;br /&gt;        elif token == "*":&lt;br /&gt;            stack.insert(0, (stack.pop(0) * stack.pop(0)))&lt;br /&gt;        elif token == "/":&lt;br /&gt;            s = stack.pop(1)&lt;br /&gt;            stack.insert(0, (stack.pop(0) / s))&lt;br /&gt;        else:&lt;br /&gt;            sys.exit("Unrecognized toke '%s'; aborting" % token)&lt;br /&gt;    return stack.pop(0)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# my @stack;&lt;br /&gt;# my $actions = {&lt;br /&gt;#   '+' =&gt; sub { push @stack, pop(@stack) + pop(@stack) },&lt;br /&gt;#   '*' =&gt; sub { push @stack, pop(@stack) * pop(@stack) },&lt;br /&gt;#   '-' =&gt; sub { my $s = pop(@stack); push @stack, pop(@stack) - $s },&lt;br /&gt;#   '/' =&gt; sub { my $s = pop(@stack); push @stack, pop(@stack) / $s },&lt;br /&gt;#   'NUMBER' =&gt; sub { push @stack, $_[0] },&lt;br /&gt;#   '_DEFAULT_' =&gt; sub { die "Unrecognized token '$_[0]'; aborting" }&lt;br /&gt;# };&lt;br /&gt;# my $result = evaluate($ARGV[0], $actions);&lt;br /&gt;# print "Result: $result\n";&lt;br /&gt;# sub evaluate {&lt;br /&gt;#   my ($expr, $actions) = @_;&lt;br /&gt;#   my @tokens = split /\s+/, $expr;&lt;br /&gt;#&lt;br /&gt;#   for my $token (@tokens) {&lt;br /&gt;#     my $type;&lt;br /&gt;#     if ($token =~ /∧ \d+$/) { # It's a number&lt;br /&gt;#       $type = 'NUMBER';&lt;br /&gt;#     }&lt;br /&gt;#     my $action = $actions-&gt;{$type}&lt;br /&gt;#               || $actions-&gt;{$token}&lt;br /&gt;#               || $actions-&gt;{_DEFAULT_};&lt;br /&gt;#     $action-&gt;($token, $type, $actions);&lt;br /&gt;#   }&lt;br /&gt;#   return pop(@stack);&lt;br /&gt;# }&lt;br /&gt;stack = []&lt;br /&gt;actions = {&lt;br /&gt;    "+" : (lambda *x: stack.insert(0,  stack.pop(0)    + stack.pop(0))),&lt;br /&gt;    "-" : (lambda *x: stack.insert(0, -stack.pop(0)    + stack.pop(0))),&lt;br /&gt;    "*" : (lambda *x: stack.insert(0,  stack.pop(0)    * stack.pop(0))),&lt;br /&gt;    "/" : (lambda *x: stack.insert(0, (1/stack.pop(0)) * stack.pop(0))),&lt;br /&gt;    "NUMBER" : (lambda *x: stack.insert(0, int(x[0]))),&lt;br /&gt;    "__DEFAULT__" : (lambda *x: sys.exit("Unrecognized token '%s'; aborting" % x[0]))&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;result = evaluate(sys.argv[1], actions)&lt;br /&gt;print "Result: %s" % result&lt;br /&gt;def evaluate(expr, actions):&lt;br /&gt;    tokens = expr.split()&lt;br /&gt;&lt;br /&gt;    for token in tokens:&lt;br /&gt;        type = None&lt;br /&gt;        if token.isdigit():&lt;br /&gt;            type = "NUMBER"&lt;br /&gt;        action = actions.get(type or token) or actions["__DEFAULT__"]&lt;br /&gt;        action(token, type, actions)&lt;br /&gt;    return stack.pop(0)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#sqrt' =&gt; sub { push @stack, sqrt(pop(@stack)) },&lt;br /&gt;"sqrt" : (lambda *x: stack.insert(0, math.sqrt(stack.pop(0))))&lt;br /&gt;&lt;br /&gt;# my $actions = {&lt;br /&gt;#   'NUMBER'    =&gt; sub { push @stack,   $_[0] },&lt;br /&gt;#   '_DEFAULT_' =&gt; sub { my $s = pop(@stack);&lt;br /&gt;#                        push @stack,&lt;br /&gt;#                           [ $_[0], pop(@stack), $s ]&lt;br /&gt;#                        },&lt;br /&gt;# };&lt;br /&gt;actions = {&lt;br /&gt;    "NUMBER" : (lambda *x: stack.insert(0, int(x[0]))),&lt;br /&gt;    "__DEFAULT__" : (lambda *x: stack.insert(0, [x[0], stack.pop(1), stack.pop(0)]))&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub AST_to_string {&lt;br /&gt;#   my ($tree) = @_;&lt;br /&gt;#   if (ref $tree) {&lt;br /&gt;#     my ($op, $a1, $a2) = @$tree;&lt;br /&gt;#     my ($s1, $s2) = (AST_to_string($a1),&lt;br /&gt;#                      AST_to_string($a2));&lt;br /&gt;#     "($s1 $op $s2)";&lt;br /&gt;#   } else {&lt;br /&gt;#     $tree;&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def AST_to_string(tree):&lt;br /&gt;    if tree:&lt;br /&gt;        op, a1, a2 =  tree&lt;br /&gt;        s1, s2 = AST_to_string(a1), AST_to_string(a2)&lt;br /&gt;        return "%s %s %s" % (s1, op, s2)&lt;br /&gt;    else:&lt;br /&gt;        return tree&lt;br /&gt;&lt;br /&gt;# sub elementfunc {&lt;br /&gt;#   my $table = { h1        =&gt; sub { shift; my $text = join '', @_;&lt;br /&gt;#                                    print $text; return $text ;&lt;br /&gt;#                                  }&lt;br /&gt;#                 _DEFAULT_ =&gt; sub { shift; my $text = join '', @_;&lt;br /&gt;#                                               return $text ;&lt;br /&gt;#               };&lt;br /&gt;#   my ($element) = @_;&lt;br /&gt;#   my $tag = $element-&gt;{_tag};&lt;br /&gt;#   my $action = $table-&gt;{$tag} || $table{_DEFAULT_};&lt;br /&gt;#   return $action-&gt;(@_);&lt;br /&gt;# }&lt;br /&gt;def elementfunc(element):&lt;br /&gt;    table = {"h1"          : (lambda html, results: "".join(results)),&lt;br /&gt;             "__DEFAULT__" : (lambda html, results: "".join(results))}&lt;br /&gt;&lt;br /&gt;    tag = element["_tag"]&lt;br /&gt;    action = table.get(tag) or table["__DEFAULT__"]&lt;br /&gt;    return action(element)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub walk_html {&lt;br /&gt;#   my ($html, $textfunc, $elementfunc_table) = @_;&lt;br /&gt;#   return $textfunc-&gt;($html) unless ref $html; # It's a plain string&lt;br /&gt;#   my ($item, @results);&lt;br /&gt;#   for $item (@{$html-&gt;{_content}}) {&lt;br /&gt;#     push @results, walk_html($item, $textfunc, $elementfunc_table);&lt;br /&gt;#   }&lt;br /&gt;#   my $tag = $html-&gt;{_tag};&lt;br /&gt;#   my $elementfunc = $elementfunc_table-&gt;{$tag}&lt;br /&gt;#                  || $elementfunc_table-&gt;{_DEFAULT_}&lt;br /&gt;#                  || die "No function defined for tag '$tag'";&lt;br /&gt;#   return $elementfunc-&gt;($html, @results);&lt;br /&gt;# }&lt;br /&gt;def walk_html(html, textfunc, elementfunc_table):&lt;br /&gt;    if type(html) == str:&lt;br /&gt;        return textfunc(html)&lt;br /&gt;&lt;br /&gt;    results = []&lt;br /&gt;    for item in html["_content"]:&lt;br /&gt;        results.append(walk_html(item, textfunc, elementfunc_table))&lt;br /&gt;    &lt;br /&gt;    tag = html["_tag"]&lt;br /&gt;    elementfunc = elementfunc_table.get(tag) or elementfunc_table.get("__DEFAULT__")&lt;br /&gt;    if not elementfunc:&lt;br /&gt;        sys.exit("No function defined for tag '%s'" % tag)&lt;br /&gt;    return elementfunc(html, results)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub walk_html {&lt;br /&gt;#   my ($html, $textfunc, $elementfunc, $userparam) = @_;&lt;br /&gt;#   return $textfunc-&gt;($html, $userparam) unless ref $html;&lt;br /&gt;#   my ($item, @results);&lt;br /&gt;#   for $item (@{$html-&gt;{_content}}) {&lt;br /&gt;#     push @results, walk_html($item, $textfunc, $elementfunc, $userparam);&lt;br /&gt;#   }&lt;br /&gt;#   return $elementfunc-&gt;($html, $userparam, @results);&lt;br /&gt;# }&lt;br /&gt;def walk_html(html, textfunc, elementfunc, userparam):&lt;br /&gt;    if type(html) == str:&lt;br /&gt;        return textfunc(html)&lt;br /&gt;&lt;br /&gt;    results = []&lt;br /&gt;    for item in html["_content"]:&lt;br /&gt;        results.append(walk_html(item, textfunc, elementfunc, userparam))&lt;br /&gt;&lt;br /&gt;    return elementfunc(html, results, userparam)&lt;br /&gt;        &lt;br /&gt;# walk_html($html_text,&lt;br /&gt;#  # $textfunc&lt;br /&gt;#  sub { my ($text, $aref) = @_;&lt;br /&gt;#        push @$aref, $text },&lt;br /&gt;#  # $elementfunc does nothing&lt;br /&gt;#  sub { },&lt;br /&gt;#  # user parameter&lt;br /&gt;#  \@text_array&lt;br /&gt;# );&lt;br /&gt;def walk_html(html_text,&lt;br /&gt;              lambda text, aref: aref.append(text),&lt;br /&gt;              lambda x,y: [],&lt;br /&gt;              text_array)&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-7597937517580617092?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/7597937517580617092/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=7597937517580617092' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/7597937517580617092'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/7597937517580617092'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2009/01/higher-order-perl-python-style-chapter_14.html' title='Higher Order Perl (Python Style) : Chapter 2'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-6771304440578786245</id><published>2009-01-14T20:44:00.000-08:00</published><updated>2009-01-15T22:36:07.235-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='functional programming'/><category scheme='http://www.blogger.com/atom/ns#' term='higher order perl'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='hop'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><title type='text'>Higher Order Perl (Python Style) : Chapter 1</title><content type='html'>&lt;pre&gt;&lt;br /&gt;#&lt;br /&gt;# Recursion and Callbacks&lt;br /&gt;#&lt;br /&gt;&lt;br /&gt;# sub binary {&lt;br /&gt;#   my ($n) = @_;&lt;br /&gt;#   return $n if $n == 0 || $n == 1;&lt;br /&gt;&lt;br /&gt;#   my $k = int($n/2);&lt;br /&gt;#   my $b = $n % 2;&lt;br /&gt;&lt;br /&gt;#   my $E = binary($k);&lt;br /&gt;&lt;br /&gt;#   return $E . $b;&lt;br /&gt;# }&lt;br /&gt;def binary(n):&lt;br /&gt;    if n in (0,1):&lt;br /&gt;        return str(n)&lt;br /&gt;&lt;br /&gt;    k = n / 2&lt;br /&gt;    b = n % 2&lt;br /&gt;&lt;br /&gt;    return binary(k) + str(b)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub factorial {&lt;br /&gt;#   my ($n) = @_;&lt;br /&gt;#   return factorial($n-1) * $n;&lt;br /&gt;# }&lt;br /&gt;def factorial(n):&lt;br /&gt;    return factorial(n-1) * n&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub factorial {&lt;br /&gt;#   my ($n) = @_;&lt;br /&gt;#   return 1 if $n == 0;&lt;br /&gt;#   return factorial($n-1) * $n;&lt;br /&gt;# }&lt;br /&gt;def factorial(n):&lt;br /&gt;    if n == 0:&lt;br /&gt;        return 1&lt;br /&gt;    return factorial(n-1) * n&lt;br /&gt;&lt;br /&gt;# sub hanoi {&lt;br /&gt;#   my ($n, $start, $end, $extra) = @_;&lt;br /&gt;#   if ($n == 1) {&lt;br /&gt;#     print "Move disk #1 from $start to $end.\n";  # Step 1&lt;br /&gt;#   } else {&lt;br /&gt;#     hanoi($n-1, $start, $extra, $end);            # Step 2&lt;br /&gt;#     print "Move disk #$n from $start to $end.\n"; # Step 3&lt;br /&gt;#     hanoi($n-1, $extra, $end, $start);            # Step 4&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def hanoi(n, start, end, extra):&lt;br /&gt;    if n == 1:&lt;br /&gt;        print "Move disk #1 from %(start)s to %(end)s." % locals()&lt;br /&gt;    else:&lt;br /&gt;        hanoi(n-1, start, extra, end)&lt;br /&gt;        print "Move disk #%(n)s from %(start)s to %(end)s." % locals()&lt;br /&gt;        hanoi(n-1, extra, end, start)&lt;br /&gt;        &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub hanoi {&lt;br /&gt;#   my ($n, $start, $end, $extra, $move_disk) = @_;&lt;br /&gt;#   if ($n == 1) {&lt;br /&gt;#     $move_disk-&gt;(1, $start, $end);&lt;br /&gt;#   } else {&lt;br /&gt;#     hanoi($n-1, $start, $extra, $end, $move_disk);&lt;br /&gt;#     $move_disk-&gt;($n, $start, $end);&lt;br /&gt;#     hanoi($n-1, $extra, $end, $start, $move_disk);&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def hanoi(n, start, end, extra, move_disk):&lt;br /&gt;    if n == 1:&lt;br /&gt;        move_disk(1, start, end)&lt;br /&gt;    else:&lt;br /&gt;        hanoi(n-1, start, extra, end, move_disk)&lt;br /&gt;        move_disk(n, start, end)&lt;br /&gt;        hanoi(n-1, extra, end, start, move_disk)&lt;br /&gt;    &lt;br /&gt;&lt;br /&gt;# sub print_instruction {&lt;br /&gt;#   my ($disk, $start, $end) = @_;&lt;br /&gt;#   print "Move disk #$disk from $start to $end.\n";&lt;br /&gt;# }&lt;br /&gt;# hanoi(3, 'A', 'C', 'B', \&amp;print_instruction);&lt;br /&gt;def print_instruction(disk, start, end):&lt;br /&gt;    print "Move disk %(disk)s from %(start)s to %(end)s" % locals()&lt;br /&gt;&lt;br /&gt;hanoi(3, "A", "C", "B", print_instruction)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# @position = ('', ('A') x 3); # Disks are all initially on peg A&lt;br /&gt;&lt;br /&gt;# sub check_move {&lt;br /&gt;#     my $i;&lt;br /&gt;#     my ($disk, $start, $end) = @_;&lt;br /&gt;&lt;br /&gt;#     if ($disk &lt; 1 || $disk &gt; $#position) {&lt;br /&gt;#        die "Bad disk number $disk. Should be 1..$#position.\n";&lt;br /&gt;#     }&lt;br /&gt;&lt;br /&gt;#     unless ($position[$disk] eq $start) {&lt;br /&gt;#        die "Tried to move disk $disk from $start, but it is on peg&lt;br /&gt;#                                                       $position[$disk].\n";&lt;br /&gt;#     }&lt;br /&gt;&lt;br /&gt;#     for $i (1 .. $disk-1) {&lt;br /&gt;#        if ($position[$i] eq $start) {&lt;br /&gt;#            die "Can't move disk $disk from $start because $i is on top of it.\n";&lt;br /&gt;#        } elsif ($position[$i] eq $end) {&lt;br /&gt;#            die "Can't move disk $disk to $end because $i is already there.\n";&lt;br /&gt;#        }&lt;br /&gt;#     }&lt;br /&gt;&lt;br /&gt;#     $position[$disk] = $end;&lt;br /&gt;# }&lt;br /&gt;&lt;br /&gt;# hanoi(3, 'A', 'C', 'B', \&amp;check_move);&lt;br /&gt;position = [""] + ["A"] * 3&lt;br /&gt;&lt;br /&gt;def check_move(disk, start, end):&lt;br /&gt;    if disk &lt; 1 or disk &gt; len(position) - 1:&lt;br /&gt;        sys.exit("Bad disk number %s. Should be 1..%s" % (disk, len(position)-1))&lt;br /&gt;&lt;br /&gt;    if position[disk] != start:&lt;br /&gt;        sys.exit("Tried to move disk %s from %s, but it is on peg %s" % (disk, start, position[disk]))&lt;br /&gt;&lt;br /&gt;    for i in range(1, disk):&lt;br /&gt;        if position[i] == start:&lt;br /&gt;            sys.exit("Can't move disk %s from %s because %s is on top of it." % (disk, start, i))&lt;br /&gt;        elif position[i] == end:&lt;br /&gt;            sys.exit("Can't move disk %s to %s because %s is already there." % (disk, end, i))&lt;br /&gt;&lt;br /&gt;    position[disk] = end&lt;br /&gt;    print position&lt;br /&gt;&lt;br /&gt;hanoi(3, "A", "C", "B", check_move)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub total_size {&lt;br /&gt;#    my ($top) = @_;&lt;br /&gt;#    my $total = -s $top;&lt;br /&gt;&lt;br /&gt;#    return $total if -f $top;&lt;br /&gt;#    unless (opendir DIR, $top) {&lt;br /&gt;#       warn "Couldn’t open directory $top: $!; skipping.\n";&lt;br /&gt;#       return $total;&lt;br /&gt;#    }&lt;br /&gt;&lt;br /&gt;#    my $file;&lt;br /&gt;#    while ($file = readdir DIR) {&lt;br /&gt;#      next if $file eq '.' || $file eq '..';&lt;br /&gt;#      $total += total_size("$top/$file");&lt;br /&gt;#    }&lt;br /&gt;&lt;br /&gt;#    closedir DIR;&lt;br /&gt;#    return $total;&lt;br /&gt;# }&lt;br /&gt;&lt;br /&gt;# NOTE: in python you'd probably prefer&lt;br /&gt;#       os.walk&lt;br /&gt;# ALSO: it would be unnatural to duplicate the &lt;br /&gt;#       buggy first version of this function so we'll&lt;br /&gt;#       just be satisfied with it working. :) &lt;br /&gt;def total_size(top):&lt;br /&gt;    total = os.path.getsize(top)&lt;br /&gt;&lt;br /&gt;    if os.path.isfile(top):&lt;br /&gt;        return total&lt;br /&gt;&lt;br /&gt;    try:&lt;br /&gt;        for filename in os.listdir(top):&lt;br /&gt;            total += total_size(os.path.join(top, filename))&lt;br /&gt;    except OSError:&lt;br /&gt;        print "Couldn't open directory: %s; skipping." % top&lt;br /&gt;        return total&lt;br /&gt;    return total&lt;br /&gt;&lt;br /&gt;# sub dir_walk {&lt;br /&gt;#   my ($top, $code) = @_;&lt;br /&gt;#   my $DIR;&lt;br /&gt;#   $code-&gt;($top);&lt;br /&gt;#   if (-d $top) {&lt;br /&gt;#     my $file;&lt;br /&gt;#     unless (opendir $DIR, $top) {&lt;br /&gt;#       warn "Couldn’t open directory $top: $!; skipping.\n";&lt;br /&gt;#       return;&lt;br /&gt;#     }&lt;br /&gt;#     while ($file = readdir $DIR) {&lt;br /&gt;#       next if $file eq '.' || $file eq '..';&lt;br /&gt;#       dir_walk("$top/$file", $code);&lt;br /&gt;#     }&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;&lt;br /&gt;# the traditional python way would be to use os.walk()&lt;br /&gt;def dir_walk(top, code):&lt;br /&gt;    code(top)&lt;br /&gt;    &lt;br /&gt;    if os.path.isdir(top):&lt;br /&gt;        try:&lt;br /&gt;            for filename in os.listdir(top):&lt;br /&gt;                dir_walk(os.path.join(top, filename), code)&lt;br /&gt;        except OSError:&lt;br /&gt;            print "Couldn't open directory: %s; skipping." % top&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;        &lt;br /&gt;# sub print_dir {&lt;br /&gt;#   print $_[0], "\n";&lt;br /&gt;# }&lt;br /&gt;def print_dir(x):&lt;br /&gt;    print x&lt;br /&gt;&lt;br /&gt;#dir_walk('.', sub { printf "%6d %s\n", -s $_[0], $_[0] } );&lt;br /&gt;dir_walk('.', lambda x: sys.stdout.write("%6d %s\n" % (os.path.getsize(x), x)))&lt;br /&gt;&lt;br /&gt;#dir_walk('.', sub { print $_[0], "\n" if -l $_[0] &amp;&amp; ! -e $_[0] });&lt;br /&gt;dir_walk('.', lambda x: (os.path.islink(x) and os.path.exists(os.readlink(x)) and sys.stdout.write("%s\n" % x)))&lt;br /&gt;&lt;br /&gt;# my $TOTAL = 0;&lt;br /&gt;# dir_walk('.', sub { $TOTAL += -s $_[0] });&lt;br /&gt;# print "Total size is $TOTAL.\n";&lt;br /&gt;&lt;br /&gt;# can't run += or global w/in lambda&lt;br /&gt;total = 0&lt;br /&gt;def accumulate(x):&lt;br /&gt;    global total&lt;br /&gt;    total += os.path.getsize(x)&lt;br /&gt;dir_walk('.', accumulate)&lt;br /&gt;print "Total size is %s." % total&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub dir_walk { &lt;br /&gt;#   my ($top, $filefunc, $dirfunc) = @_;&lt;br /&gt;#   my $DIR;&lt;br /&gt;#   if (-d $top) {&lt;br /&gt;#     my $file;&lt;br /&gt;#     unless (opendir $DIR, $top) {&lt;br /&gt;#       warn "Couldn’t open directory $code: $!; skipping.\n";&lt;br /&gt;#       return;&lt;br /&gt;#     }&lt;br /&gt;#     my @results;&lt;br /&gt;#     while ($file = readdir $DIR) {&lt;br /&gt;#       next if $file eq '.' || $file eq '..';&lt;br /&gt;#       push @results, dir_walk("$top/$file", $filefunc, $dirfunc);&lt;br /&gt;#     }&lt;br /&gt;#     return $dirfunc-&gt;($top, @results);&lt;br /&gt;#   } else {&lt;br /&gt;#     return $filefunc-&gt;($top);&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;&lt;br /&gt;# sub file_size { -s $_[0] }&lt;br /&gt;&lt;br /&gt;# sub dir_size {&lt;br /&gt;#   my $dir = shift;&lt;br /&gt;#   my $total = -s $dir;&lt;br /&gt;#   my $n;&lt;br /&gt;#   for $n (@_) { $total += $n }&lt;br /&gt;#   return $total;&lt;br /&gt;# }&lt;br /&gt;&lt;br /&gt;# $total_size = dir_walk('.', \&amp;file_size, \&amp;dir_size);&lt;br /&gt;def dir_walk(top, filefunc, dirfunc):&lt;br /&gt;    if os.path.isdir(top):&lt;br /&gt;        results = []&lt;br /&gt;        try:&lt;br /&gt;            for filename in os.listdir(top):&lt;br /&gt;                results.append(dir_walk(os.path.join(top, filename), filefunc, dirfunc))&lt;br /&gt;        except OSError:&lt;br /&gt;            print "Couldn't open directory: %s; skipping." % top&lt;br /&gt;            return&lt;br /&gt;        return dirfunc(top, results)&lt;br /&gt;    else:&lt;br /&gt;        return filefunc(top)&lt;br /&gt;&lt;br /&gt;def file_size(x):&lt;br /&gt;    return os.path.getsize(x)&lt;br /&gt;&lt;br /&gt;def dir_size(d, accum):&lt;br /&gt;    _dirsize = os.path.getsize(d)&lt;br /&gt;    total = _dirsize + sum(accum)&lt;br /&gt;    return total&lt;br /&gt;&lt;br /&gt;# sub dir_size {&lt;br /&gt;#   my $dir = shift;&lt;br /&gt;#   my $total = -s $dir;&lt;br /&gt;#   my $n;&lt;br /&gt;#   for $n (@_) { $total += $n }&lt;br /&gt;#   printf "%6d %s\n", $total, $dir;&lt;br /&gt;#   return $total;&lt;br /&gt;# }&lt;br /&gt;def dir_size(d, accum):&lt;br /&gt;    _dirsize = os.path.getsize(d)&lt;br /&gt;    total = _dirsize + sum(accum)&lt;br /&gt;    print "%6d %ss" % (total, d)&lt;br /&gt;    return total&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub file {&lt;br /&gt;#   my $file = shift;&lt;br /&gt;#   [short($file), -s $file];&lt;br /&gt;# }&lt;br /&gt;# sub short {&lt;br /&gt;#   my $path = shift;&lt;br /&gt;#   $path = ̃ s{.*/}{};&lt;br /&gt;#   $path;&lt;br /&gt;# }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def _file(x):&lt;br /&gt;    return os.path.basename(x), os.path.getsize(x)&lt;br /&gt;&lt;br /&gt;# sub dir {&lt;br /&gt;#   my ($dir, @subdirs) = @_;&lt;br /&gt;#   my %new_hash;&lt;br /&gt;#   for (@subdirs) {&lt;br /&gt;#     my ($subdir_name, $subdir_structure) = @$_;&lt;br /&gt;#     $new_hash{$subdir_name} = $subdir_structure;&lt;br /&gt;#   }&lt;br /&gt;#   return [short($dir), \%new_hash];&lt;br /&gt;# }&lt;br /&gt;def _dir(dir, subdirs):&lt;br /&gt;    new_hash = {}&lt;br /&gt;    for subdir_name, subdir_structure in subdirs:&lt;br /&gt;        new_hash[subdir_name] = subdir_structure&lt;br /&gt;    return os.path.basename(dir), new_hash&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub print_filename { print $_[0], "\n" }&lt;br /&gt;# dir_walk('.', \&amp;print_filename, \&amp;print_filename);&lt;br /&gt;&lt;br /&gt;# we add a result parameter so that the function&lt;br /&gt;# can work as dirfunc&lt;br /&gt;def print_filename(x, result=None):&lt;br /&gt;    print x&lt;br /&gt;dir_walk(".", print_filename, print_filename)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub dangles {&lt;br /&gt;#   my $file = shift;&lt;br /&gt;#   print "$file\n" if -l $file &amp;&amp; ! -e $file;&lt;br /&gt;# }&lt;br /&gt;# dir_walk('.', \&amp;dangles, sub {});&lt;br /&gt;def dangles(x):&lt;br /&gt;    if os.path.islink(x) and not os.path.exists(os.readlink(x)):&lt;br /&gt;        print x&lt;br /&gt;dir_walk('.', dangles, lambda x,y: ())&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub dir_walk {&lt;br /&gt;#   my ($top, $filefunc, $dirfunc) = @_;&lt;br /&gt;#   my $DIR;&lt;br /&gt;#   if (-d $top) {&lt;br /&gt;#     my $file;&lt;br /&gt;#     unless (opendir $DIR, $top) {&lt;br /&gt;#       warn "Couldn’t open directory $top: $!; skipping.\n";&lt;br /&gt;#       return;&lt;br /&gt;#     }&lt;br /&gt;#     my @results;&lt;br /&gt;#     while ($file = readdir $DIR) {&lt;br /&gt;#       next if $file eq '.' || $file eq '..';&lt;br /&gt;#       push @results, dir_walk("$top/$file" $filefunc, $dirfunc);&lt;br /&gt;#                                           ,&lt;br /&gt;#     }&lt;br /&gt;#     return $dirfunc ? $dirfunc-&gt;($top, @results) : () ;&lt;br /&gt;#   } else {&lt;br /&gt;#     return $filefunc ? $filefunc-&gt;($top): () ;&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def dir_walk(top, filefunc=None, dirfunc=None):&lt;br /&gt;    if os.path.isdir(top):&lt;br /&gt;        results = []&lt;br /&gt;        try:&lt;br /&gt;            for filename in os.listdir(top):&lt;br /&gt;                results.append(dir_walk(os.path.join(top, filename), filefunc, dirfunc))&lt;br /&gt;        except OSError:&lt;br /&gt;            print "Couldn't open directory: %s; skipping." % top&lt;br /&gt;            return&lt;br /&gt;        return dirfunc and dirfunc(top, results)&lt;br /&gt;    else:&lt;br /&gt;        return filefunc and filefunc(top)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#@all_plain_files = dir_walk('.', sub { $_[0] }, sub { shift; return @_ });&lt;br /&gt;all_plain_files = dir_walk(".", lambda x: x, lambda x, y: y)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# { _tag =&gt; "p",&lt;br /&gt;#   _content =&gt; [ "But I don't ",&lt;br /&gt;#                 { _tag =&gt; "font",&lt;br /&gt;#                   _content =&gt; [ "want" ],&lt;br /&gt;#                   color =&gt; "red",&lt;br /&gt;#                   size =&gt; 3,&lt;br /&gt;#                 },&lt;br /&gt;#                 " to go to bed now!",&lt;br /&gt;#                 ],&lt;br /&gt;# }&lt;br /&gt;&lt;br /&gt;# so I'm going to punt on this.  I believe I could&lt;br /&gt;# probably get something usefully similar from&lt;br /&gt;# BeautifulSoup or lxml but I don't really&lt;br /&gt;# want to vary from the original perl code *too* much&lt;br /&gt;# so I'll just use a hardcoded dict representation&lt;br /&gt;# and see how far I get with that.&lt;br /&gt;tree =  { "_tag"      : "(document)",&lt;br /&gt;          "_content" : [{"_tag" : "h1",&lt;br /&gt;                         "_content" : [ "What Junior Said Next",&lt;br /&gt;                                     ],                    &lt;br /&gt;                         },&lt;br /&gt;                        "\n\n",&lt;br /&gt;                        {"_tag" : "p",&lt;br /&gt;                         "_content" : [ "But I don't ",&lt;br /&gt;                                        { "_tag" : "font",&lt;br /&gt;                                          "_content" : [ "want" ],&lt;br /&gt;                                          "color" : "red",&lt;br /&gt;                                          "size" : 3,&lt;br /&gt;                                          },&lt;br /&gt;                                        " to go to bed now!",&lt;br /&gt;                                        ],&lt;br /&gt;                         }&lt;br /&gt;                        ]&lt;br /&gt;          }   &lt;br /&gt;# sub untag_html {&lt;br /&gt;#   my ($html) = @_;&lt;br /&gt;#   return $html unless ref $html;   # It’s a plain string&lt;br /&gt;#   my $text = '';&lt;br /&gt;#   for my $item (@{$html-&gt;{_content}}) {&lt;br /&gt;#     $text .= untag_html($item);&lt;br /&gt;#   }&lt;br /&gt;#   return $text;&lt;br /&gt;# }&lt;br /&gt;def untag_html(html):&lt;br /&gt;    if type(html) != dict:&lt;br /&gt;        return html&lt;br /&gt;&lt;br /&gt;    text = ""&lt;br /&gt;    for item in html["_content"]:&lt;br /&gt;        text += untag_html(item)&lt;br /&gt;    return text&lt;br /&gt;&lt;br /&gt;# sub walk_html {&lt;br /&gt;#   my ($html, $textfunc, $elementfunc) = @_;&lt;br /&gt;#   return $textfunc-&gt;($html) unless ref $html; # It’s a plain string&lt;br /&gt;#   my @results;&lt;br /&gt;#   for my $item (@{$html-&gt;{_content}}) {&lt;br /&gt;#     push @results, walk_html($item, $textfunc, $elementfunc);&lt;br /&gt;#   }&lt;br /&gt;#   return $elementfunc-&gt;($html, @results);&lt;br /&gt;# }&lt;br /&gt;# we are going to cheat hear a little and also&lt;br /&gt;# pass around a concatenator function&lt;br /&gt;# since perl's "push" function has some &lt;br /&gt;# "magic" properties that don't map well to python&lt;br /&gt;def appender(accum, item):&lt;br /&gt;    accum.append(item)&lt;br /&gt;def extender(accum, item):&lt;br /&gt;    accum.extend(item)&lt;br /&gt;def walk_html(html, textfunc, elementfunc, &lt;br /&gt;              concatenator=appender):&lt;br /&gt;    if type(html) == str:&lt;br /&gt;        return textfunc(html)&lt;br /&gt;&lt;br /&gt;    results = []&lt;br /&gt;    for item in html["_content"]:&lt;br /&gt;        concatenator(results, walk_html(item, textfunc, elementfunc, concatenator))&lt;br /&gt;&lt;br /&gt;    return elementfunc(html, results)&lt;br /&gt;&lt;br /&gt;# walk_html($tree, sub { $_[0] },&lt;br /&gt;#                  sub { shift; join '', @_ });&lt;br /&gt;walk_html(tree, lambda x: x, lambda x,y: "".join(y))&lt;br /&gt;&lt;br /&gt;# sub print_if_h1tag {&lt;br /&gt;#   my $element = shift;&lt;br /&gt;#   my $text = join '', @_;&lt;br /&gt;#   print $text if $element-&gt;{_tag} eq 'h1';&lt;br /&gt;#   return $text;&lt;br /&gt;# }&lt;br /&gt;def print_if_h1tag(element, results):&lt;br /&gt;    text = "".join(results)&lt;br /&gt;    if element["_tag"] == "h1":&lt;br /&gt;        print text&lt;br /&gt;    return text&lt;br /&gt;&lt;br /&gt;#walk_html($tree, sub { $_[0] }, \&amp;print_if_h1tag);&lt;br /&gt;walk_html(tree, lambda x: x, print_if_h1tag)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# @tagged_texts = walk_html($tree, sub { ['MAYBE', $_[0]] },&lt;br /&gt;#                                  \&amp;promote_if_h1tag);&lt;br /&gt;# sub promote_if_h1tag {&lt;br /&gt;#   my $element = shift;&lt;br /&gt;#   if ($element-&gt;{_tag} eq 'h1') {&lt;br /&gt;#     return ['KEEPER', join '', map {$_-&gt;[1]} @_];&lt;br /&gt;#   } else {&lt;br /&gt;#     return @_;&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;tagged_texts = walk_html(tree, lambda x: [["MAYBE", x]],&lt;br /&gt;                               promote_if_h1tag,&lt;br /&gt;                               extender)&lt;br /&gt;def promote_if_h1tag(element, results):&lt;br /&gt;    if element["_tag"] == "h1":&lt;br /&gt;        return [["KEEPER", "".join([ _x[1] for _x in results])]]&lt;br /&gt;    else:&lt;br /&gt;        return results&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub extract_headers {&lt;br /&gt;#   my $tree = shift;&lt;br /&gt;#   my @tagged_texts = walk_html($tree, sub { ['MAYBE', $_[0]] },&lt;br /&gt;#                                       \&amp;promote_if_h1tag);&lt;br /&gt;#   my @keepers = grep { $_-&gt;[0] eq 'KEEPER'} @tagged_texts;&lt;br /&gt;#   my @keeper_text = map { $_-&gt;[1] } @keepers;&lt;br /&gt;#   my $header_text = join '', @keeper_text;&lt;br /&gt;#   return $header_text;&lt;br /&gt;# }&lt;br /&gt;def extract_headers(tree):&lt;br /&gt;    tagged_texts = walk_html(tree, lambda x: [["MAYBE", x]],&lt;br /&gt;                                   promote_if_h1tag,&lt;br /&gt;                                   extender)&lt;br /&gt;    keepers = [x for x in tagged_texts if x[0] == "KEEPER"]&lt;br /&gt;    keeper_text = [x[1] for x in keepers]&lt;br /&gt;    header_text = "".join(keeper_text)&lt;br /&gt;    return header_text&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub extract_headers {&lt;br /&gt;#   my $tree = shift;&lt;br /&gt;#   my @tagged_texts = walk_html($tree, sub { ['MAYBE', $_[0]] },&lt;br /&gt;#                                       \&amp;promote_if_h1tag);&lt;br /&gt;#   join '', map { $_-&gt;[1] } grep { $_-&gt;[0] eq 'KEEPER'} @tagged_texts;&lt;br /&gt;# }&lt;br /&gt;def extract_headers(tree):&lt;br /&gt;    tagged_texts = walk_html(tree, lambda x: [["MAYBE", x]],&lt;br /&gt;                                   promote_if_h1tag,&lt;br /&gt;                                   extender)&lt;br /&gt;    return "".join([x[1] for x in tagged_texts if x[0] == "KEEPER"])&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub promote_if_h1tag {&lt;br /&gt;#   my $element = shift;&lt;br /&gt;#   if ($element-&gt;{_tag} =~ /^h\d+$/) {&lt;br /&gt;#     return ['KEEPER', join '', map {$_-&gt;[1]} @_];&lt;br /&gt;#   } else {&lt;br /&gt;#     return @_;&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def promote_if_h1tag(element, results):&lt;br /&gt;    if re.search("^h\d+", element["_tag"]):&lt;br /&gt;        return [["KEEPER", "".join([ _x[1] for _x in results])]]&lt;br /&gt;    else:&lt;br /&gt;        return results&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub promote_if {&lt;br /&gt;#   my $is_interesting = shift;&lt;br /&gt;#   my $element = shift;&lt;br /&gt;#   if ($is_interesting-&gt;($element-&gt;{_tag}) {&lt;br /&gt;#     return ['KEEPER', join '', map {$_-&gt;[1]} @_];&lt;br /&gt;#   } else {&lt;br /&gt;#     return @_;&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def promote_if(is_interesting, element, results):&lt;br /&gt;    if is_interesting(element["_tag"]):&lt;br /&gt;        return [["KEEPER", "".join([ _x[1] for _x in results])]]&lt;br /&gt;    else:&lt;br /&gt;        return results&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# my @tagged_texts = walk_html($tree,&lt;br /&gt;#                               sub { ['maybe', $_[0]] },&lt;br /&gt;#                               sub { promote_if(&lt;br /&gt;#                                        sub { $_[0] eq 'h1'},&lt;br /&gt;#                                        $_[0])&lt;br /&gt;#                               });&lt;br /&gt;tagged_texts = walk_html(tree, lambda x: [["MAYBE", x]],&lt;br /&gt;                               lambda y,z: (promote_if((lambda _y: _y == "h1"), y,z)),&lt;br /&gt;                               extender)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub fib {&lt;br /&gt;#   my ($month) = @_;&lt;br /&gt;#   if ($month &lt; 2) { 1 }&lt;br /&gt;#   else {&lt;br /&gt;#     fib($month-1) + fib($month-2);&lt;br /&gt;#   }&lt;br /&gt;# }&lt;br /&gt;def fib(month):&lt;br /&gt;    if month &lt; 2:&lt;br /&gt;        return 1&lt;br /&gt;    else:&lt;br /&gt;        return fib(month-1) + fib(month-2)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub find_share {&lt;br /&gt;#   my ($target, $treasures) = @_;&lt;br /&gt;#   return [] if $target == 0;&lt;br /&gt;#   return    if $target &lt; 0 || @$treasures == 0;&lt;br /&gt;#   my ($first, @rest) = @$treasures;&lt;br /&gt;#   my $solution = find_share($target-$first, \@rest);&lt;br /&gt;#   return [$first, @$solution] if $solution;&lt;br /&gt;#   return         find_share($target       , \@rest);&lt;br /&gt;# }&lt;br /&gt;def find_share(target, treasures):&lt;br /&gt;    if target == 0:&lt;br /&gt;        return []&lt;br /&gt;    if target &lt; 0 or len(treasures) == 0:&lt;br /&gt;        return&lt;br /&gt;    first, rest = treasures[0], treasures[1:]&lt;br /&gt;    solution = find_share(target-first, rest)&lt;br /&gt;    if solution != None:&lt;br /&gt;        return [first] + solution&lt;br /&gt;    return find_share(target, rest)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# sub partition {&lt;br /&gt;#   my $total = 0;&lt;br /&gt;#   my $share_2;&lt;br /&gt;#   for my $treasure (@_) {&lt;br /&gt;#     $total += $treasure;&lt;br /&gt;#   }&lt;br /&gt;#   my $share_1 = find_share($total/2, [@_]);&lt;br /&gt;#   return unless defined $share_1;&lt;br /&gt;#   my %in_share_1;&lt;br /&gt;#   for my $treasure (@$share_1) {&lt;br /&gt;#     ++$in_share_1{$treasure};&lt;br /&gt;#   }&lt;br /&gt;#   for my $treasure (@_) {&lt;br /&gt;#     if ($in_share_1{$treasure}) {&lt;br /&gt;#       --$in_share_1{$treasure};&lt;br /&gt;#     } else {&lt;br /&gt;#       push @$share_2, $treasure;&lt;br /&gt;#     }&lt;br /&gt;#   }&lt;br /&gt;#   return ($share_1, $share_2);&lt;br /&gt;# }&lt;br /&gt;# NOTE: would be more pythonic to use sum, sets()&lt;br /&gt;def partition(treasures):&lt;br /&gt;    share_2 = []&lt;br /&gt;    total = 0&lt;br /&gt;&lt;br /&gt;    for treasure in treasures:&lt;br /&gt;        total += treasure&lt;br /&gt;&lt;br /&gt;    share_1 = find_share(total/2, treasures)&lt;br /&gt;    if not share_1:&lt;br /&gt;        return &lt;br /&gt;&lt;br /&gt;    in_share_1 = {}&lt;br /&gt;    for treasure in share_1:&lt;br /&gt;        in_share_1.setdefault(treasure, 0)&lt;br /&gt;        in_share_1[treasure] += 1&lt;br /&gt;&lt;br /&gt;    for treasure in treasures:&lt;br /&gt;        if treasure in in_share_1:&lt;br /&gt;            in_share_1[treasure] -= 1&lt;br /&gt;        else:&lt;br /&gt;            share_2.append(treasure)&lt;br /&gt;    &lt;br /&gt;    return share_1, share_2&lt;br /&gt;&lt;br /&gt;# a more pythonic way:&lt;br /&gt;def partition_(treasures):&lt;br /&gt;    share_1 = find_share(sum(treasures)/2, treasures)&lt;br /&gt;    if not share_1:&lt;br /&gt;        return &lt;br /&gt;&lt;br /&gt;    return share_1, list(set(treasures) - set(share_1))&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-6771304440578786245?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/6771304440578786245/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=6771304440578786245' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/6771304440578786245'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/6771304440578786245'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2009/01/higher-order-perl-python-style-chapter.html' title='Higher Order Perl (Python Style) : Chapter 1'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-1144729700573429564</id><published>2009-01-14T20:33:00.000-08:00</published><updated>2009-04-08T20:31:27.466-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='functional programming'/><category scheme='http://www.blogger.com/atom/ns#' term='higher order perl'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='hop'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><title type='text'>Higher Order Perl (Python Style) TOC</title><content type='html'>Higher Order Perl has many things of interest for a python programmer.  As I've been going through this book (a free copy of which can be viewed &lt;a href="http://hop.perl.plover.com/"&gt;here&lt;/a&gt;) I've been translating (as literally as possible) the perl code to python.   I will continue to update this page as I finish each chapter.  In general my plan is to stay as close as possible to the original perl unless the python transliteration is impossible (perl lambda are more expressive) or too unpythonic (e.g. some times a list comprehension or a set datatype does wonders for brevity).  But still in many cases there will be a more pythonic way to write many of these examples.  This is left as an exercise for the reader.  :)&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;   &lt;li&gt;  &lt;a href="http://dustbunnylair.blogspot.com/2009/01/higher-order-perl-python-style-chapter.html"&gt;Recursion and Callbacks&lt;/a&gt;&lt;br /&gt;   &lt;li&gt; &lt;a href="http://dustbunnylair.blogspot.com/2009/01/higher-order-perl-python-style-chapter_14.html"&gt;Dispatch Tables&lt;/a&gt;&lt;br /&gt;   &lt;li&gt; &lt;a href="http://dustbunnylair.blogspot.com/2009/01/higher-order-perl-python-style-chapter_23.html"&gt;Caching and Memoization&lt;/a&gt;&lt;br /&gt;   &lt;li&gt; &lt;a href="http://dustbunnylair.blogspot.com/2009/02/higher-order-perl-python-style-chapter.html"&gt;Iterators&lt;/a&gt;&lt;br /&gt;   &lt;li&gt; &lt;a href="http://dustbunnylair.blogspot.com/2009/03/higher-order-perl-python-style-chapter.html"&gt;From Recursion to Iterators&lt;/a&gt;&lt;br /&gt;   &lt;li&gt; &lt;a href="http://dustbunnylair.blogspot.com/2009/03/higher-order-perl-python-style-chapter_25.html"&gt;Infinite Streams&lt;/a&gt;&lt;br /&gt;   &lt;li&gt; &lt;a href="http://dustbunnylair.blogspot.com/2009/04/higher-order-perl-python-style-chapter.html"&gt;Higher-Order Functions&lt;/a&gt;&lt;br /&gt;   &lt;li&gt; Parsing&lt;br /&gt;   &lt;li&gt; Declarative Programming &lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;Please let me know if you see any serious translation errors or have other suggestions.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-1144729700573429564?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/1144729700573429564/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=1144729700573429564' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/1144729700573429564'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/1144729700573429564'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2009/01/higher-order-perl-python-style-toc.html' title='Higher Order Perl (Python Style) TOC'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-387249817989219216</id><published>2009-01-09T21:51:00.000-08:00</published><updated>2009-01-09T21:32:42.890-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='emacslisp'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='emacs'/><title type='text'>emacs python mode from scratch: stage 18 - miscellany</title><content type='html'>stage 19 - miscellany&lt;br /&gt;&lt;br /&gt;A grab bag of functions to finish us up:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;python-jython-packages&lt;br /&gt;python-maybe-jython&lt;br /&gt;python-fill-paragraph&lt;br /&gt;python-shift-left&lt;br /&gt;python-shift-right&lt;br /&gt;python-outline-level&lt;br /&gt;python-current-defun&lt;br /&gt;python-mark-block&lt;br /&gt;&lt;/pre&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;python-jython-packages&lt;br /&gt;&lt;li&gt;python-maybe-jython&lt;br /&gt;   &lt;p&gt;I don't really care too much about jython support so this won't get much more that a brief   inspection.  python-jython-packages seems to be a list of   module names that if found in a package will cause jython mode to   be used&lt;br /&gt;   &lt;p&gt;python-maybe-jython is the function that uses the above list among   other things to magically determine whether or not to use python or   jython mode.&lt;br /&gt;&lt;li&gt;python-fill-paragraph&lt;br /&gt;   &lt;p&gt;Provides a python aware fill-paragraph-function.  The usual process   would be to define paragraph-start and paragraph-separate and let   fill do it's magic, but apparently this doesn't work quite right   for the last paragraph in a multiline string.  So instead   This function narrows the string itself and does the fill action   on that.&lt;br /&gt;&lt;li&gt;python-shift-left&lt;br /&gt;&lt;li&gt;python-shift-right&lt;br /&gt;   &lt;p&gt;Move some code left or right by the requested number of columns   (defaulting to the prima facie indentation level).  If there   is no region selected then just work with current line.   This works by iterating over every line and running indent-rigidly.   There is less code for the python-shift-right variation since you   don't have to look out for the case where you run out of room   for shifting.&lt;br /&gt;&lt;li&gt;python-outline-level&lt;br /&gt;   &lt;p&gt;get outline-level.  This is just the multiple of python-indent   of the current indentation level.&lt;br /&gt;&lt;li&gt;python-current-defun&lt;br /&gt;   &lt;p&gt;Work up an indentation level at a time (ie def or class)   and keep track of the names of the def and class entities   along the way.&lt;br /&gt;   &lt;p&gt;This is used by add-log (add-log.el).  apparently this is some sore of   functionality for working with change logs.  this function   gives a way to refer to the name of the function where point   currently resides.  But honestly I'm not really sure how   this fits into the big picture.  This may well be some feature   i depend on, but I'm having trouble figuring out what features   use this and why.&lt;br /&gt;   &lt;p&gt;If you run M-x add-change-log-entry it opens up something   called a change log.  I guess I'll just keep my eyes open   for this in the future.&lt;br /&gt;&lt;li&gt;python-mark-block&lt;br /&gt;   &lt;p&gt;Mark the block.  Just as you'd expect.&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;So that's pretty much the end except for some little details here and there.  This was an interesting exercise though if I had thought about how long this was going to take I'm not sure I would have started down this path.  I definitely learned a lot, but the main thing I learned was how little I really know about emacs.   Now that I'm finished I find a nice little tutorial on &lt;a href="http://renormalist.net/cgi-bin/twiki/view/Renormalist/EmacsLanguageModeCreationTutorial"&gt;how language modes work&lt;/a&gt;.  Well at least I know what they are talking about now.  My main lesson I guess is that I really need to learn emacslisp if I want to become a master.  But even then I will still have a lot to learn about the actual environment.  But I'm getting there.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-387249817989219216?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/387249817989219216/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=387249817989219216' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/387249817989219216'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/387249817989219216'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2009/01/emacs-python-mode-from-scratch-stage-18.html' title='emacs python mode from scratch: stage 18 - miscellany'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-4552938935670102209</id><published>2009-01-08T22:00:00.001-08:00</published><updated>2009-01-08T22:08:01.914-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pushups'/><title type='text'>100 Pushups: week 6 milestone - 60</title><content type='html'>OK, so after almost 7 month of the hilariously named 6 week program for achieving &lt;a href="http://hundredpushups.com/"&gt;100 pushups&lt;/a&gt; I finally hit 60.  And actually it's even better than that because I did a total of 188 reps (in sets of 18 to 30) immediately preceding this.  &lt;br /&gt;&lt;br /&gt;So I'm making progress.  There is probably a more efficient training regimen, but I'm getting there.&lt;br /&gt;&lt;br /&gt;I don't know if this really is what got me there tonight but several times earlier in the day I had rehearsed in my head the last 5 pushups.  56. 57. 58. 59. 60!  I visualized getting stronger on each one.  And I felt sort of lame and new agey doing this, but I really wanted to hit this milestone tonight so I was willing to allow a little "woo" in my life if it was going to give me the edge. &lt;br /&gt;&lt;br /&gt;OK, just 40 more.  *sigh*&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-4552938935670102209?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/4552938935670102209/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=4552938935670102209' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/4552938935670102209'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/4552938935670102209'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2009/01/100-pushups-week-6-milestone-60.html' title='100 Pushups: week 6 milestone - 60'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-6428423497942291890</id><published>2009-01-02T23:59:00.000-08:00</published><updated>2009-01-02T23:37:00.792-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='focus'/><category scheme='http://www.blogger.com/atom/ns#' term='mindfulness'/><category scheme='http://www.blogger.com/atom/ns#' term='meditation'/><title type='text'>Mindfulness++</title><content type='html'>I'm one of those guys who always talks about how they hate new year's resolutions and then makes a bunch anyway.  Hi, nice to meet you.&lt;br /&gt;&lt;br /&gt;One thing that has been weighing on me for a while and especially since I've had kids is that my concentration seems to be a faint shadow of it's former glory.  It's probably partly age and partly the induced ADD of having to watch over young children.  And don't forget the internets.  Damn those tubes!  &lt;br /&gt;&lt;br /&gt;In any case I find myself so easily distracted, even when I'm doing something that I really enjoy.  There seems to be a constantly growing wave of research and advice on this topic and much of it points to meditation/mindfulness as being a practice that will increase your ability to focus.  Most recently I read this when going through Pragmatic Thinking and Learning: Refactor your Wetware.&lt;br /&gt;&lt;br /&gt;I've actually had as a goal for at least a year to do 8 minutes a night of "watching my breath".  Sometimes I'll go weeks of doing this every night.  Sometimes I go weeks without even thinking about it.&lt;br /&gt;&lt;br /&gt;So there are two parts to this resolution.  I want to make this a do not skip part of my nightly routine.  And I also want to increase the time to 20 minutes.  There is probably nothing magic about the number 8 or 20, but I more often see recommended numbers closer to (at least) 20, so that will be my target.  So the other part is to increase my time limit by 1 minute a month all of this year.  This will bring me ever so slowly and gently to 20 minutes.&lt;br /&gt;&lt;br /&gt;So with any luck, by the end of the year laser beams will be coming to me for advice on how to be so focused.&lt;br /&gt;&lt;br /&gt;Any long term mindfulness practitioners out there with advice/encouragement?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-6428423497942291890?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/6428423497942291890/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=6428423497942291890' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/6428423497942291890'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/6428423497942291890'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/12/mindfulness.html' title='Mindfulness++'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-8393181268696756378</id><published>2009-01-01T22:30:00.000-08:00</published><updated>2009-01-01T22:40:30.988-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='smalltalk'/><category scheme='http://www.blogger.com/atom/ns#' term='squeak'/><title type='text'>One New Language a Year:  Smalltalk</title><content type='html'>The Pragmatic Programmers recommend learning a new programming language every year.  I thought for a long time that this was a good idea, but didn't really act on it.  Instead I'd dabble in a dozen languages a year.  While this has built my intuition as to what various languages are like it has not made me a competent programmer in a new language.  So for this year I'm going to actually pick a language, make a learning schedule and keep on it for the entire year.  Now I almost certainly won't be able to stop myself from messing around with other languages to some extent (e.g. I'm slowly working through the Real World Haskell book), but my goal is to direct more of my focus and energy on a smaller target and hopefully get to the point where I really have an additional tool to use for solving real problems.&lt;br /&gt;&lt;br /&gt;So then comes the question of which language to learn.  In one sense I should probably pick a language that I don't know at all.  The problem is that I've played with a lot of languages over the years.  I don't think there are a lot of programming concepts/paradigms that I'm not at least familiar with.  But there are a lot that I have not mastered.  So I will just pick from the list of languages of which I'm not a master.  A very healthy list.&lt;br /&gt;&lt;br /&gt;Here is a short list of languages that I at least briefly considered:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;smalltalk&lt;br /&gt;&lt;li&gt;haskell&lt;br /&gt;&lt;li&gt;something lispy (common lisp, scheme, emacslisp)&lt;br /&gt;&lt;li&gt;clojure (also lispy, but new and different)&lt;br /&gt;&lt;li&gt;erlang&lt;br /&gt;&lt;li&gt;mozart/oz&lt;br /&gt;&lt;li&gt;ruby&lt;br /&gt;&lt;li&gt;perl 6&lt;br /&gt;&lt;li&gt;javascript&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;All the languages above have some intriguing features but in the end I chose smalltalk (specifically squeak, but perhaps soon pharo).  The deciding factors were:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;My kids are getting close to an age where I might introduce them to programming so I'd like to have some mastery of a kid friendly programming language.  While I think python fits that requirement pretty well, etoys seems like it might fit it even better (scratch also looks interesting).&lt;br /&gt;&lt;li&gt;I want to bump up my familiarity with deeper OO programming practices.  In particular, I want to bump up my mastery of design patterns.  Smalltalk is one of the languages where design patterns were first developed&lt;br /&gt;&lt;li&gt;I want to have a language where I can return to one of my first loves: graphical simulations.  I could clearly do that in python, but well, learning a new language is always a good excuse to play around with graphics libraries.&lt;br /&gt;&lt;li&gt;I want to work on something that is really different in a fundamental way.  Being forced to use the squeak development environment (instead of emacs)  and an "image" instead of text files fits the bill&lt;br /&gt;&lt;li&gt;seaside just seems inherently cool and I'd like to get my mind around continuations.&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;I imagine my plan will change as I learn more and follow my interests, but as a first pass:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Learn etoys&lt;br /&gt;  &lt;p&gt;This will give me a chance to start off sort of easy and genuinely playful.  I've been collecting some links and project ideas but initially I am planning on looking at: &lt;a href="http://www.amazon.com/Powerful-Ideas-Classroom-B-J-Allen/dp/0974313106"&gt;Powerful Ideas in the Classroom&lt;/a&gt;, &lt;a href="http://www.iam.unibe.ch/~scg/Archive/Papers/Gael06aC5.pdf"&gt;Idioms for Composing Games with Etoys&lt;/a&gt;, Etoys chapter in &lt;a href="http://smallwiki.unibe.ch/botsinc"&gt;Learn Programming With Robots&lt;/a&gt;, and poke around in the squeakland site.&lt;br /&gt;&lt;li&gt;Intro to smalltalk/squeak reading/tutorials.&lt;br /&gt;&lt;p&gt;Some items that stand out as worth perusing include: &lt;a href="http://squeak.preeminent.org/tut2007/html/"&gt;Laser Game Tutorial&lt;/a&gt;, &lt;a href="http://www.squeakbyexample.org/"&gt;Squeak By Example&lt;/a&gt;, Design Patterns Smalltalk Companion (I've had this on the shelf for a while and it's really time I read it), Mark Guzdial's "Blue" book, and browse around on &lt;a href="http://stephane.ducasse.free.fr/FreeBooks.html"&gt;this list of smalltalk books&lt;/a&gt;.&lt;br /&gt;&lt;li&gt;Get familiar with seaside&lt;br /&gt;&lt;p&gt;In particular I will work on porting over a Django app (which used to be a quixote app) to seaside.  I'm not planning on migrating it to seaside for real, but it's nice to have a real world project with lots of ugly edge cases in mind to see how a framework really works.&lt;br /&gt;&lt;li&gt;Depend on smalltalk&lt;br /&gt;&lt;p&gt;By the end of the year I hope to have some project that I maintain (even if just for myself) that keeps me involved with writing and maintaining smalltalk code.&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;Note:  Haskell came in a very close second.  It was actually a very hard decision.  It seems like Haskell is poised to take off and it would be nice to be part of that wave.  On the other hand.  I already know that that language is pretty challenging to make progress on and will still be waiting for me next year.  &lt;br /&gt;&lt;br /&gt;FWIW: I recently came across &lt;a href="http://www.h3rald.com/articles/10-programming-languages"&gt;this&lt;/a&gt; list of languages worth learning.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-8393181268696756378?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/8393181268696756378/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=8393181268696756378' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/8393181268696756378'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/8393181268696756378'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2009/01/one-new-language-year-smalltalk.html' title='One New Language a Year:  Smalltalk'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-1005357807008264469</id><published>2008-12-21T12:56:00.001-08:00</published><updated>2009-01-18T13:45:19.100-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='99 problems'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>99 problems - python - 56</title><content type='html'>Symmetric binary trees&lt;br /&gt;&lt;br /&gt;Let us call a binary tree symmetric if you can draw a vertical line through the root node and then the right subtree is the mirror image of the left subtree. Write a predicate symmetric/1 to check whether a given binary tree is symmetric. Hint: Write a predicate mirror/2 first to check whether one tree is the mirror image of another. We are only interested in the structure, not in the contents of the nodes.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;# Example in Haskell:&lt;br /&gt;&lt;br /&gt;# *Main&gt; symmetric (Branch 'x' (Branch 'x' Empty Empty) Empty)&lt;br /&gt;# False&lt;br /&gt;# *Main&gt; symmetric (Branch 'x' (Branch 'x' Empty Empty) (Branch 'x' Empty Empty))&lt;br /&gt;# True&lt;br /&gt;&lt;br /&gt;def mirror(tree):&lt;br /&gt;    if tree == None:&lt;br /&gt;        return None&lt;br /&gt;    else:&lt;br /&gt;        val, left, right = tree&lt;br /&gt;        return (None, mirror(right), mirror(left))&lt;br /&gt;&lt;br /&gt;def symmetric_binary_tree(tree):&lt;br /&gt;    return mirror(mirror(tree)) == mirror(tree)&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-1005357807008264469?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/1005357807008264469/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=1005357807008264469' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/1005357807008264469'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/1005357807008264469'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/12/99-problems-python-56.html' title='99 problems - python - 56'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-406753535915123219</id><published>2008-12-21T09:24:00.000-08:00</published><updated>2008-12-21T09:29:23.883-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='99 problems'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>99 problems - python - 55</title><content type='html'>Construct completely balanced binary trees&lt;br /&gt;&lt;br /&gt;In a completely balanced binary tree, the following property holds for every node: The number of nodes in its left subtree and the number of nodes in its right subtree are almost equal, which means their difference is not greater than one.&lt;br /&gt;&lt;br /&gt;Write a function cbal-tree to construct completely balanced binary trees for a given number of nodes. The predicate should generate all solutions via backtracking. Put the letter 'x' as information into all nodes of the tree.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;# Example:&lt;br /&gt;&lt;br /&gt;# * cbal-tree(4,T). &lt;br /&gt;# T = t(x, t(x, nil, nil), t(x, nil, t(x, nil, nil))) ;&lt;br /&gt;# T = t(x, t(x, nil, nil), t(x, t(x, nil, nil), nil)) ;&lt;br /&gt;# etc......No&lt;br /&gt;def completely_balanced_trees(node_count):&lt;br /&gt;    if node_count == 0:&lt;br /&gt;        return [None]&lt;br /&gt;    else:&lt;br /&gt;        right_count  = (node_count - 1) / 2&lt;br /&gt;        left_count = (node_count - 1) - right_count&lt;br /&gt;        left_trees = completely_balanced_trees(left_count)&lt;br /&gt;        right_trees = completely_balanced_trees(right_count)&lt;br /&gt;&lt;br /&gt;        trees = []&lt;br /&gt;        for left in left_trees:&lt;br /&gt;            for right in right_trees:&lt;br /&gt;                trees.append(("x", left, right))&lt;br /&gt;                if left_count != right_count:&lt;br /&gt;                    trees.append(("x", right, left))&lt;br /&gt;&lt;br /&gt;        return trees&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;OK, I'm using tuples for trees again.  So sue me.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-406753535915123219?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/406753535915123219/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=406753535915123219' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/406753535915123219'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/406753535915123219'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/12/99-problems-python-55.html' title='99 problems - python - 55'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-6211788777276267732</id><published>2008-12-20T21:13:00.000-08:00</published><updated>2008-12-20T21:43:24.995-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='babylon 5'/><title type='text'>Babylon 5: not so cautious, not so optimistic</title><content type='html'>So the general rule for our tv watching these days is that we pretty much just watch tv series that are complete and available in their entirety on netflix.  It turns out there are only a limited number of these.  After a while you start considering things that have been on your "maybe" list for a while.  One very big maybe for me has been Babylon 5.  It shows up on all of these top 10 best scifi series lists and people rave about it having one of the best multi season story arcs.  Ever.&lt;br /&gt;&lt;br /&gt;Having heard that the first season is a bit weak and the things really start to get good in season 2 I asked my wife for some patience and and decided to dig in.  &lt;br /&gt;&lt;br /&gt;And, well, so far it's been worse than I could have imagined.  Terrible set designs.  Unbelievably bad diaglog.  Very long in the tooth special effects.  Unimaginitive aliens.  Recycled scifi tropes.  Wooden acting.  And we are only two episodes away from season 2 starting.  And I have to admit I'm really intrigued.  Is there really any chance that things turn around *so* dramatically?&lt;br /&gt;&lt;br /&gt;I'm a total fanboy for Farscape.  In my opinion, the first season there was pretty weak (not Babylon 5 weak, but not great).  And yet it slowly grew into one of my all time favorite series.  And this was a show that had serious problems throughout but had some amazing highs to make up for it.  So I do believe that things can turn around for B5 and that I will like it.  &lt;br /&gt;&lt;br /&gt;But man, this first season has been so extremely painful.  I'm afraid my wife will get revenge on me by making me watch Sex in the City.  Oh, god, the horror.  The horror....&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-6211788777276267732?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/6211788777276267732/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=6211788777276267732' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/6211788777276267732'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/6211788777276267732'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/12/babylon-5-not-so-cautious-not-so.html' title='Babylon 5: not so cautious, not so optimistic'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-988373667989709123</id><published>2008-12-20T06:50:00.000-08:00</published><updated>2008-12-20T07:00:02.672-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='emacslisp'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='emacs'/><title type='text'>emacs python mode from scratch: stage 18 - imenu</title><content type='html'>&lt;p&gt;So first of all, what *is* &lt;a href="http://www.gnu.org/software/emacs/elisp/html_node/Imenu.html"&gt;imenu&lt;/a&gt;. &lt;br /&gt;&lt;p&gt;... read, read .... &lt;br /&gt;&lt;p&gt;So it partly seems like a one file only form of etags.  But if you do M-x imenu-add-menubar-index it will add an "index" menu to the menu bar with a list of all files for easy access.  Being a bit of a mouse-ophobe, I'm not so sure how I'd like that.  Also you have to hit rescan whenever you change the contents of the file (or set up imenu-auto-rescan).&lt;br /&gt;&lt;p&gt;Seems like another case of having more than one way to do things.  I'll probably stick with etags, but for completeness here is a brief overview of the imenu support functions.&lt;br /&gt;&lt;p&gt;So there is really just one function:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;python-imenu-create-index&lt;br /&gt; &lt;p&gt;Go to the beginning of the file.  look for "def and "class"    references (unless in comments or string) and add them to the list.&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;p&gt;Hmm, I wonder if something else more wonderful is going on here.  Doesn't really seem like functionality worth supporting.&lt;br /&gt;&lt;p&gt;Perhaps if you had it set up to go off a simple mouse motion as described &lt;a href="http://www.emacswiki.org/emacs-en/ImenuMode"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(if (featurep 'xemacs)&lt;br /&gt;     (global-set-key [(shift button3)] 'imenu) ; XEmacs&lt;br /&gt;   (global-set-key [S-mouse-3] 'imenu)) ; GNU Emacs&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Also looks like it integrates in with ido to some extent.  Maybe there is some goodness from that.&lt;br /&gt;&lt;p&gt;Any way, that's enough on that topic.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-988373667989709123?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/988373667989709123/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=988373667989709123' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/988373667989709123'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/988373667989709123'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/12/emacs-python-mode-from-scratch-stage-18.html' title='emacs python mode from scratch: stage 18 - imenu'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-7462570475783495260</id><published>2008-12-17T20:20:00.003-08:00</published><updated>2008-12-17T20:25:10.488-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='emacslisp'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='emacs'/><title type='text'>emacs python mode from scratch: stage 17 - ffap</title><content type='html'>This is a small bit of code, but it's another little gem that I didn't know exists.  There is only one function:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;python-module-path&lt;br /&gt;    &lt;p&gt;This function uses emacs.modpath to determine the module path for use with ffap-alist&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;What this gets us is support for M-x find-file-at-point.&lt;br /&gt;&lt;br /&gt;It's not *quite* as cool as I first anticipated because the file path finder seems to get confused about what is actually a module, but if point is on a module name on an import line then it will pop you directly over to the python lib directory and open the module.  If you already have etags set up to do this then you probably won't be *too* amazed, but this is a nice extra way to accomplish the same thing.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-7462570475783495260?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/7462570475783495260/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=7462570475783495260' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/7462570475783495260'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/7462570475783495260'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/12/emacs-python-mode-from-scratch-stage-17.html' title='emacs python mode from scratch: stage 17 - ffap'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-2329739752387217341</id><published>2008-12-17T20:10:00.000-08:00</published><updated>2008-12-17T20:24:39.848-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='emacslisp'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='emacs'/><title type='text'>emacs python mode from scratch: stage 16 - completion</title><content type='html'>This is a collection of functions that uses emacs.py to find a list of likely completions for module.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;python-imports&lt;br /&gt;python-find-imports&lt;br /&gt;python-symbol-completions&lt;br /&gt;python-partial-symbol &lt;br /&gt;python-complete-symbol&lt;br /&gt;python-try-complete&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;python-imports&lt;br /&gt;   &lt;p&gt;A list of python import statements in the buffer&lt;br /&gt;&lt;li&gt;python-find-imports&lt;br /&gt;   &lt;p&gt;Populate the above list.  search by looking for "^import" or "^from"   (skipping those in comments or strings).&lt;br /&gt;   &lt;p&gt;Then reverse the list, clean out text properties and change \n to \\n   so that output doesn't end up wrong.&lt;br /&gt;&lt;li&gt;python-symbol-completions&lt;br /&gt;   &lt;p&gt;Run emacs.complete(symbol, python-imports) and return the list of   completions (in another buffer)&lt;br /&gt;&lt;li&gt;python-partial-symbol&lt;br /&gt;   &lt;p&gt;Use a regular expression to go backward and thereby find the complete   symbol before point.  This seems a little odd to me.  I seems like   it would be easier to just trust the syntax-table that has already   been defined for the mode and just do backward-word from the current   position (with a save-excursion).  As a guess I'd say it's so that   it can treat "." as part of the symbol and skip over that to the   beginning of a module/class name.&lt;br /&gt;&lt;li&gt;python-complete-symbol&lt;br /&gt;   &lt;p&gt;Find the list of completions (using python-symbol-completions)   or just scroll the completions window if they've already been found.&lt;br /&gt;&lt;li&gt;python-try-complete&lt;br /&gt;   &lt;p&gt;"hippie-expand" version for doing symbol completions.   Basically "he-" does all the heavy lifting hereit just needs to be&lt;br /&gt;   given the python-symbol-completions and python-partial-symbol functions.&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-2329739752387217341?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/2329739752387217341/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=2329739752387217341' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/2329739752387217341'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/2329739752387217341'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/12/emacs-python-mode-from-scratch-stage-16.html' title='emacs python mode from scratch: stage 16 - completion'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-3116400702747789800</id><published>2008-12-13T19:27:00.001-08:00</published><updated>2008-12-13T19:31:43.461-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='99 problems'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>99 problems - python - 54A</title><content type='html'>Check whether a given term represents a binary tree&lt;br /&gt;&lt;br /&gt;In Prolog or Lisp, one writes a predicate to do this.&lt;br /&gt;&lt;br /&gt;Example in Lisp:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;* (istree (a (b nil nil) nil))&lt;br /&gt;T&lt;br /&gt;* (istree (a (b nil nil)))&lt;br /&gt;NIL&lt;br /&gt;def is_tree(t):&lt;br /&gt;    if t == None:&lt;br /&gt;        return True&lt;br /&gt;    elif type(t) == tuple and len(t) == 3:&lt;br /&gt;        (val, left, right) = t&lt;br /&gt;        return is_tree(left) and is_tree(right)&lt;br /&gt;    return False&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Is using lispy tuple for representing tree cheating?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-3116400702747789800?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/3116400702747789800/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=3116400702747789800' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/3116400702747789800'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/3116400702747789800'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/12/99-problems-python-54a.html' title='99 problems - python - 54A'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-8784253612249020096</id><published>2008-12-13T07:31:00.001-08:00</published><updated>2008-12-13T19:31:59.447-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='99 problems'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>99 problems - python - 50</title><content type='html'>Huffman codes.&lt;br /&gt;&lt;br /&gt;We suppose a set of symbols with their frequencies, given as a list of fr(S,F) terms. Example: [fr(a,45),fr(b,13),fr(c,12),fr(d,16),fr(e,9),fr(f,5)]. Our objective is to construct a list hc(S,C) terms, where C is the Huffman code word for the symbol S. In our example, the result could be Hs = [hc(a,'0'), hc(b,'101'), hc(c,'100'), hc(d,'111'), hc(e,'1101'), hc(f,'1100')] [hc(a,'01'),...etc.]. The task shall be performed by the predicate huffman/2 defined as follows:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;# stats: [(letter, count)]&lt;br /&gt;# return: [(count, letter OR (left, right)]&lt;br /&gt;def make_tree(stats):&lt;br /&gt;    pool = sorted([(count, letter) for letter, count in stats])&lt;br /&gt;&lt;br /&gt;    while len(pool) &gt; 1:&lt;br /&gt;        first = pool.pop(0)&lt;br /&gt;        second = pool.pop(0)&lt;br /&gt;&lt;br /&gt;        node = (first[0]+second[0], (first, second))&lt;br /&gt;        pool.append(node)&lt;br /&gt;        pool.sort()&lt;br /&gt;&lt;br /&gt;    return pool[0]&lt;br /&gt;&lt;br /&gt;def codes_from_tree(node, path, results):&lt;br /&gt;    count, letter_or_trees = node&lt;br /&gt;    letter = left = right = None&lt;br /&gt;    if type(letter_or_trees) == tuple:&lt;br /&gt;        left, right = letter_or_trees&lt;br /&gt;    else:&lt;br /&gt;        letter = letter_or_trees&lt;br /&gt;&lt;br /&gt;    if letter:&lt;br /&gt;        results.append((letter, path))&lt;br /&gt;    else:&lt;br /&gt;        codes_from_tree(left, path+"0", results)&lt;br /&gt;        codes_from_tree(right, path+"1", results)&lt;br /&gt;&lt;br /&gt;def huffman(stats):&lt;br /&gt;    results = []&lt;br /&gt;    tree = make_tree(stats)&lt;br /&gt;    codes_from_tree(tree, "", results)&lt;br /&gt;    &lt;br /&gt;    #to sort by huffman code value&lt;br /&gt;    #results.sort(key=lambda x: int(x[1], 2))&lt;br /&gt;    results.sort()&lt;br /&gt;&lt;br /&gt;    return results&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That solution *feels* a little hacky for me, but I can't think of why it bothers me.  It might be that the last time I wrote this was in java, and it was a lot more work.  Maybe this just seems too easy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-8784253612249020096?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/8784253612249020096/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=8784253612249020096' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/8784253612249020096'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/8784253612249020096'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/12/99-problems-python-50.html' title='99 problems - python - 50'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-8909790492287698910</id><published>2008-12-12T21:24:00.000-08:00</published><updated>2008-12-12T21:27:40.110-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='emacslisp'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='emacs'/><title type='text'>emacs python mode from scratch: stage 15 - info look</title><content type='html'>&lt;p&gt;Probably the main lesson of my explorations of the python mode is that there is a lot of functionality available that I had never heard of before.  info-look is a good case in point.&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;python-after-info-look&lt;br /&gt;   &lt;p&gt;This function essentially let's you hook into the info system to look for documentation.&lt;br /&gt;   &lt;p&gt;As far as I can tell the main use is for calling info-lookup-symbol (C-h S) and having the info page for the python entity show up.&lt;br /&gt;  &lt;p&gt;   You need to make sure the info flavor of python documentation is installed on your system.  (And hopefully it takes you less time than it did me to realize it wasn't).&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;p&gt;Not much else to say on this.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-8909790492287698910?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/8909790492287698910/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=8909790492287698910' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/8909790492287698910'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/8909790492287698910'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/12/emacs-python-mode-from-scratch-stage-15.html' title='emacs python mode from scratch: stage 15 - info look'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-1831359134121603359</id><published>2008-12-08T22:52:00.000-08:00</published><updated>2008-12-08T22:56:53.188-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='emacslisp'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='emacs'/><title type='text'>emacs python mode from scratch: stage 14 - context sensitive help</title><content type='html'>&lt;p&gt;Here are the relevant variables and function in the context-sensitive help section&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;VARS:&lt;br /&gt;(defconst db-python-dotty-syntax-table&lt;br /&gt;(defvar view-return-to-alist)&lt;br /&gt;(defvar db-python-imports)&lt;br /&gt;&lt;br /&gt;FUNCS:&lt;br /&gt;python-describe-symbol&lt;br /&gt;python-send-receive&lt;br /&gt;python-eldoc-function&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;db-python-dotty-syntax-table&lt;br /&gt;  &lt;p&gt;This creates an altered syntax table that treats "." as "symbol constituent".  In other words, "." is treated as part of the variable name rather than as an other type of syntax.&lt;br /&gt;&lt;li&gt;view-return-to-alist&lt;br /&gt;  &lt;p&gt;Used by python-describe symbol.  Consists of a window and help-return-method&lt;br /&gt;&lt;li&gt;db-python-imports&lt;br /&gt;  &lt;p&gt;Used below when calling emacs.* helper functions.&lt;br /&gt;&lt;li&gt;python-describe-symbol&lt;br /&gt;  &lt;p&gt;C-c C-f is bound to this and will do a python help(module|func|etc) for thing at point.  It dumps out the standard python documentation in another temporary buffer. It uses emacs.py to get the help.  Somewhat useful, but i usually have a python prompt open already.&lt;br /&gt;&lt;li&gt;python-send-receive&lt;br /&gt;  &lt;p&gt;Convenience function for sending a command to the python interpreter and reading the result.&lt;br /&gt;&lt;li&gt;python-eldoc-function&lt;br /&gt;  &lt;p&gt;Use emacs's own documentation reader to populate the message line with brief description of function and args.  this is something that I had not noticed before and am glad I came across it while perusing the code.  &lt;br /&gt;&lt;p&gt;If you have a python session going and you either run M-x eldoc-mode or you set up a hook such as&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  (add-hook 'python-mode-hook 'turn-on-eldoc-mode)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;  &lt;p&gt;then this will automatically run the eldoc help facility.  Not sure how useful it will be day to day (maybe it's just developer eye candy), but I've seen this sort of thing while running in slime or in the haskell mode and now at least I see where it is coming from.  Seems like it only really works with core python libraries.  Strangely doesn't seem to know about the functions in the actual file you are currently working in.&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Only 4 sections left to walk through.  I'm actually pretty eager to be done with this, but I definitely want to finish (however shallow my exploration of this mode is).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-1831359134121603359?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/1831359134121603359/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=1831359134121603359' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/1831359134121603359'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/1831359134121603359'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/12/emacs-python-mode-from-scratch-stage-14.html' title='emacs python mode from scratch: stage 14 - context sensitive help'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-2554682927839913001</id><published>2008-12-07T21:34:00.000-08:00</published><updated>2008-12-07T22:01:01.577-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iq'/><category scheme='http://www.blogger.com/atom/ns#' term='intelligence'/><category scheme='http://www.blogger.com/atom/ns#' term='brain games'/><title type='text'>Brains games that make you cry</title><content type='html'>I think almost as long as I've been a programmer I've tried making little games to test my reflexes and challenge my memory and other cognitive faculties.  It's just been a given to me that: (a) you can train your brain in a manner similar to lifting weights or jogging and (b) this is a highly desirable goal.  &lt;br /&gt;&lt;br /&gt;The problem is that I've never really had enough faith in the games that I created that the effort I put into them (both writing and training with them) would be worth it.  Doesn't stop me from getting interested in the idea anew every once in a while.   And it seems that there is a general growing interest in these types of games, growing research that they work and (best of all) an increasing number of open source games that are created specifically to help you train your brain.&lt;br /&gt;&lt;br /&gt;Of these sorts of games and research results it *seems* that the one with the most credibility is the "n-back task" as described in &lt;a href="http://www.wired.com/science/discoveries/news/2008/04/smart_software"&gt;this&lt;/a&gt; wired article and as implemented in &lt;a href="http://brainworkshop.sourceforge.net/"&gt;Brain Workshop&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Besides the research, one of the things that gives this game credibility is that it is really challenging and not very game like.  It really does feel like exercise (in the sense of forcing yourself to go to the gym because you know it is good for you, a necessary evil).  And honestly the first few passes through are almost laughably hard.  You are trying to maintain a memory of positions and letter values being thrown out you and notice if the position or letter value is the same as n turns ago.  &lt;br /&gt;&lt;br /&gt;And the funny thing is that even after a couple of days of not really spending too much time on it, I'm definitely getting better at it.  It's actually a weird feeling.  The first few times through is just chaos and then all of a sudden you start moving from a slight guess that, yes, I think they did say "C" 2 turns ago and are now repeating it, to being really confident.  But of course as soon as you get good at 2-back, it graduates you to 3-back.&lt;br /&gt;&lt;br /&gt;Now the problem is how do I test if I am indeed getting smarter?  I'll let you know if my brain seems like it has kicked it up a notch or two.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-2554682927839913001?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/2554682927839913001/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=2554682927839913001' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/2554682927839913001'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/2554682927839913001'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/12/brains-games-that-make-you-cry.html' title='Brains games that make you cry'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-1706732174581437963</id><published>2008-12-05T20:45:00.001-08:00</published><updated>2008-12-05T20:46:26.308-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='emacslisp'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='emacs'/><title type='text'>emacs python mode from scratch: stage 13 - python inferior mode</title><content type='html'>&lt;p&gt;My plan was to look at context sensitive help next but it dependeds on a python inferior process so let's get that working next.&lt;br /&gt;&lt;p&gt;Here are the function/variables of relevance:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(defcustom python-python-command "python"&lt;br /&gt;(defcustom python-jython-command "jython"&lt;br /&gt;(defvar python-command python-python-command&lt;br /&gt;(defvar python-buffer nil&lt;br /&gt;(defconst python-compilation-regexp-alist&lt;br /&gt;(defvar inferior-python-mode-map&lt;br /&gt;(defvar inferior-python-mode-syntax-table&lt;br /&gt;(define-derived-mode inferior-python-mode comint-mode "Inferior Python"&lt;br /&gt;(defcustom inferior-python-filter-regexp "\\`\\s-*\\S-?\\S-?\\s-*\\'"&lt;br /&gt;(defun python-input-filter (str)&lt;br /&gt;(defun python-args-to-list (string)&lt;br /&gt;(defvar python-preoutput-result nil&lt;br /&gt;(defvar python-preoutput-leftover nil)&lt;br /&gt;(defvar python-preoutput-skip-next-prompt nil)&lt;br /&gt;(defun python-preoutput-filter (s)&lt;br /&gt;(defun run-python (&amp;optional cmd noshow new)&lt;br /&gt;(defun python-send-command (command)&lt;br /&gt;(defun python-send-region (start end)&lt;br /&gt;(defun python-send-string (string)&lt;br /&gt;(defun python-send-buffer ()&lt;br /&gt;(defun python-send-defun ()&lt;br /&gt;(defun python-switch-to-python (eob-p)&lt;br /&gt;(defun python-send-region-and-go (start end)&lt;br /&gt;(defcustom python-source-modes '(python-mode jython-mode)&lt;br /&gt;(defvar python-prev-dir/file nil&lt;br /&gt;(defun python-load-file (file-name)&lt;br /&gt;(defun python-proc ()&lt;br /&gt;(defun python-set-proc ()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;The main mechanism for creating a python inferior process appears to be creating a derived mode based on comint-mode (defined in comint.el).  Most of the code then is defining helper functions for making this mode python aware.&lt;br /&gt;&lt;p&gt;So without further ago, here is a brief look at the above functions:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;python-python-command&lt;br /&gt;&lt;li&gt;python-jython-command&lt;br /&gt;   &lt;p&gt;Variables which allow customization of what command to use for invoking python (jython)&lt;br /&gt;&lt;li&gt;python-command&lt;br /&gt;   &lt;p&gt;The actual command (probably one of the above) that will actually be executed by run-python&lt;br /&gt;&lt;li&gt;python-buffer&lt;br /&gt;   &lt;p&gt;The python buffer that will be the target of code issued from files in python-mode&lt;br /&gt;&lt;li&gt;python-compilation-regexp-alist&lt;br /&gt;   &lt;p&gt;compilation-error-regexp-alist is set to this value.  This is set by inferior-python-mode and is used by compile.el.  This regular expression basically matches a python exception stacktrace:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;   Traceback (most recent call last):&lt;br /&gt;     File "foo.py", line 13, in &lt;module&gt;&lt;br /&gt;       1/0&lt;br /&gt;   ZeroDivisionError: integer division or modulo by zero&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;   &lt;p&gt;Honestly I'm a little confused by this since I don't think this regex actually matches the above and I'm not clear what purpose "compile" would need with it.&lt;br /&gt;&lt;li&gt;inferior-python-mode-map&lt;br /&gt;   &lt;p&gt;Add a few keys for loading a file and doing pychecker. The key sequence for pychecker seems superfluous here. I never feel like kicking off pychecker while i'm in an interacive session.&lt;br /&gt;&lt;li&gt;inferior-python-mode-syntax-table&lt;br /&gt;   &lt;p&gt;Adjust syntax table so that single quotes don't mess up the syntax coloring.  "." is the syntax table entry for punctuation-like things.&lt;br /&gt;&lt;li&gt;inferior-python-mode&lt;br /&gt;   &lt;p&gt;This is where the python interactive mode is defined.  It overrides the default commint-mode. There is a comment here that the "python-mode" type things (keybindings, etc) should be inherited from python mode itself.&lt;br /&gt;   &lt;p&gt;The basic customization is to adjust some regular expressions that are used by commint to decide what sort of things get maintained in the history, what the "prompt" looks like (&gt;&gt;&gt;), etc.&lt;br /&gt;&lt;li&gt;inferior-python-filter-regexp&lt;br /&gt;   &lt;p&gt;The default is not to save things in the history if they are 3 or less in length.  I hadn't noticed this behavior before.&lt;br /&gt;   &lt;p&gt;I'm not sure if there is some rhyme or reason why sometimes they use the rx style and sometime they use traditional emacs style regexps (in this case "\\`\\s-*\\S-?\\S-?\\s-*\\'").&lt;br /&gt;&lt;li&gt;python-input-filter&lt;br /&gt;   &lt;p&gt;This basically is a wrapper for ignoring inferior-python-filter-regexp&lt;br /&gt;&lt;li&gt;python-args-to-list&lt;br /&gt;   &lt;p&gt;This will be used by run-python below to collect args for kicking off a python process.  Doesn't work with quoted white space. Don't know emacs lisp well at all but it seems like a complicated way to just tokenize on SPACE and TAB.&lt;br /&gt;&lt;li&gt;python-preoutput-result&lt;br /&gt;   &lt;p&gt;Not sure what "_emacs_out" is about, and not sure I care to spend the time tracking this down.  (Possibly for emacs.py)&lt;br /&gt;&lt;li&gt;python-preoutput-leftover&lt;br /&gt;   &lt;p&gt;related to _emacs_out&lt;br /&gt;&lt;li&gt;python-preoutput-skip-next-prompt&lt;br /&gt;   &lt;p&gt;This (as well as previous 2 functions) are used by python-preoutput-filter&lt;br /&gt;&lt;li&gt;python-preoutput-filter&lt;br /&gt;   &lt;p&gt;This function appears to clean up the output a bit and prevent spurious &gt;&gt;&gt; ... ... &gt;&gt;&gt; from littering the output.  I'm going to punt on this. I'm not especially interested in the logic here but it is interesting passing to see a complex function needed to make the python mode behave in a friendly fashion.&lt;br /&gt;&lt;li&gt;run-python&lt;br /&gt;   &lt;p&gt;Start a new python process or use an existing one if available. runs python-command with -i.&lt;br /&gt;&lt;li&gt;python-send-command&lt;br /&gt;   &lt;p&gt;A wrapper around python-send-string that is used by python-send-region and python-load-file&lt;br /&gt;&lt;li&gt;python-send-region&lt;br /&gt;   &lt;p&gt;Copy a section of code to temporary file and evaluate it. Can't do it directly in the interpreter since empty lines will confuse it&lt;br /&gt;&lt;li&gt;python-send-string&lt;br /&gt;   &lt;p&gt;Evaluate a python string in the buffer.  This function checks for trailing \n or intermediate \t and throws in an extra \n so that the command is properly terminated in a way that the interpreter is expecting.&lt;br /&gt;&lt;li&gt;python-send-buffer&lt;br /&gt;   &lt;p&gt;Wrap python-send-region but use the entire buffer as the region&lt;br /&gt;&lt;li&gt;python-send-defun&lt;br /&gt;   &lt;p&gt;Send a region but use beginning-of-defun/end-of-defun to delimit the region&lt;br /&gt;&lt;li&gt;python-switch-to-python&lt;br /&gt;   &lt;p&gt;Create a new python process if necessary and switch to it. Giving it an argument makes it go to the end of the buffer&lt;br /&gt;&lt;li&gt;python-send-region-and-go&lt;br /&gt;   &lt;p&gt;Combine python-send-region and python-switch-to-python&lt;br /&gt;&lt;li&gt;python-source-modes&lt;br /&gt;   &lt;p&gt;A list of mode names so we can automatically tell if the buffer has python code in it&lt;br /&gt;&lt;li&gt;python-prev-dir/file&lt;br /&gt;   &lt;p&gt;A cache of the directory and file used by python-load-file so that it has a default value if necessary&lt;br /&gt;&lt;li&gt;python-load-file&lt;br /&gt;   &lt;p&gt;Import a python file in its entirety into the python process. Uses emacs.py if the file ends in .py otherwise uses "execfile"&lt;br /&gt;&lt;li&gt;python-proc&lt;br /&gt;   &lt;p&gt;Return to or create and move to a python process&lt;br /&gt;&lt;li&gt;python-set-proc&lt;br /&gt;   &lt;p&gt;Associate the python-buffer with the current buffer (which is a python process).  This is used by things like python-send-region so they know where to send output.&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;p&gt;I think unless you understand the details of how comint works (which I don't) it's hard to get a strong feel for how the inferior-python-mode works.  As always it's interesting to see all of the crazy details that have to be taken care of to get a simple looking thing like an embedded python  interpreter working.&lt;br /&gt;&lt;p&gt;That was a very cursory overview of how the python process inferior mode works (even by my already lax standards).  I admit it, I'm getting a little exhausted by this project.  But I'm going to keep slogging through and finish doing at least a cursory inspection of every line in python.el.&lt;br /&gt;&lt;p&gt;Another thing that is becoming clear is that, while this process is helping me understand emacs and emacslisp, I have a lot of work to do, and at some point I really need to just sit down and learn emacslisp in a focused way.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-1706732174581437963?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/1706732174581437963/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=1706732174581437963' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/1706732174581437963'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/1706732174581437963'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/12/emacs-python-mode-from-scratch-stage-13.html' title='emacs python mode from scratch: stage 13 - python inferior mode'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-1691912666778315441</id><published>2008-12-04T21:39:00.000-08:00</published><updated>2008-12-04T21:41:04.443-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sushi'/><title type='text'>Is it possible that I like sushi merely as a means to confound my wife?</title><content type='html'>Sometimes I wonder....&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-1691912666778315441?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/1691912666778315441/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=1691912666778315441' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/1691912666778315441'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/1691912666778315441'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/12/is-it-possible-that-i-like-sushi-merely.html' title='Is it possible that I like sushi merely as a means to confound my wife?'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-8443876122659878008</id><published>2008-12-02T22:48:00.000-08:00</published><updated>2008-12-02T22:54:42.804-08:00</updated><title type='text'>Choose Two</title><content type='html'>I was cracking wise with my co-workers the other day and later in the day I related the hi-jinx to my wife.  I had proffered the following theory on the relative combination of desirable attributes in your mate.&lt;br /&gt;&lt;br /&gt;"Gentleman, you've got cute, smart and nice.  And you get to choose two."&lt;br /&gt;&lt;br /&gt;My attractive, intelligent wife confirms that this is a sound theory.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-8443876122659878008?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/8443876122659878008/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=8443876122659878008' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/8443876122659878008'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/8443876122659878008'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/12/choose-two.html' title='Choose Two'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-5518634058246003879</id><published>2008-11-25T16:06:00.000-08:00</published><updated>2008-12-13T19:32:25.532-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='99 problems'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>99 problems - python - 49</title><content type='html'>Gray codes.&lt;br /&gt;&lt;br /&gt;An n-bit Gray code is a sequence of n-bit strings constructed according to certain rules. For example,&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;n = 1: C(1) = ['0','1'].&lt;br /&gt;n = 2: C(2) = ['00','01','11','10'].&lt;br /&gt;n = 3: C(3) = ['000','001','011','010',´110´,´111´,´101´,´100´].&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Find out the construction rules and write a predicate with the following specification:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def gray(n):&lt;br /&gt;    if n == 0:&lt;br /&gt;        yield ""&lt;br /&gt;    else:&lt;br /&gt;        base = list(gray_code(n-1))&lt;br /&gt;&lt;br /&gt;        for code in base:&lt;br /&gt;            yield "0"+code&lt;br /&gt;&lt;br /&gt;        for code in reversed(base):&lt;br /&gt;            yield "1"+code&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-5518634058246003879?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/5518634058246003879/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=5518634058246003879' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/5518634058246003879'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/5518634058246003879'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/11/99-problems-python-49.html' title='99 problems - python - 49'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-2160489196146673792</id><published>2008-11-22T13:17:00.001-08:00</published><updated>2008-12-13T19:32:25.532-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='99 problems'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>99 problems - python - 48</title><content type='html'>Truth tables for logical expressions (3).&lt;br /&gt;&lt;br /&gt;Generalize problem P47 in such a way that the logical expression may contain any number of logical variables. Define table/2 in a way that table(List,Expr) prints the truth table for the expression Expr, which contains the logical variables enumerated in List.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Example:&lt;br /&gt;* (table (A,B,C) (A and (B or C) equ A and B or A and C))&lt;br /&gt;true true true true&lt;br /&gt;true true fail true&lt;br /&gt;true fail true true&lt;br /&gt;true fail fail true&lt;br /&gt;fail true true true&lt;br /&gt;fail true fail true&lt;br /&gt;fail fail true true&lt;br /&gt;fail fail fail true&lt;br /&gt;&lt;br /&gt;def int_to_bool_tuple(x,size):&lt;br /&gt;    result = []&lt;br /&gt;    while x:&lt;br /&gt;        result.insert(0, (x &amp; 1) == 1)&lt;br /&gt;        x &gt;&gt;= 1&lt;br /&gt;    return ([False]*(size-len(result))) + result&lt;br /&gt;    &lt;br /&gt;def table2(var_count, func):&lt;br /&gt;    for x in range(2**var_count):&lt;br /&gt;        bools = int_to_bool_tuple(x,var_count)&lt;br /&gt;        for b in bools:&lt;br /&gt;            print "%-5s" % b,&lt;br /&gt;        print func(*bools)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As I was exploring ideas for this solution I noticed that python 2.6 has a bin() function which gives a string representation of a number as binary.  Need to upgrade to 2.6 I guess.  Of course if I'm going to upgrade, might as well wait for 3.0.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-2160489196146673792?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/2160489196146673792/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=2160489196146673792' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/2160489196146673792'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/2160489196146673792'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/11/99-problems-python-48.html' title='99 problems - python - 48'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-8771798404836176785</id><published>2008-11-22T09:30:00.000-08:00</published><updated>2008-12-13T19:32:25.533-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='99 problems'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>99 problems - python - 46</title><content type='html'>Define predicates and/2, or/2, nand/2, nor/2, xor/2, impl/2 and equ/2 (for logical equivalence) which succeed or fail according to the result of their respective operations; e.g. and(A,B) will succeed, if and only if both A and B succeed.&lt;br /&gt;&lt;br /&gt;A logical expression in two variables can then be written as in the following example: and(or(A,B),nand(A,B)).&lt;br /&gt;&lt;br /&gt;Now, write a predicate table/3 which prints the truth table of a given logical expression in two variables. &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def and_(x,y): return x and y&lt;br /&gt;def or_ (x,y): return x or y&lt;br /&gt;def not_(x): return not x&lt;br /&gt;def nand_(x,y): return not (x and y)&lt;br /&gt;def nor_(x,y): return not (x or y)&lt;br /&gt;def xor_(x,y): return x ^ y&lt;br /&gt;def impl_(x,y):return not x or y&lt;br /&gt;def equ_(x,y): return x == y&lt;br /&gt;&lt;br /&gt;def bool_table(func):&lt;br /&gt;    inputs = [(x,y) for x in (True,False) for y in (True,False)]&lt;br /&gt;    for x,y in inputs:&lt;br /&gt;        print "%-5s %-5s %-5s" % (x,y,func(x,y))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note: I'm just going to skip 47 since it's pretty much a noop based on how python already works, and how I solved this problem.  Unless I'm missing something subtle.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-8771798404836176785?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/8771798404836176785/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=8771798404836176785' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/8771798404836176785'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/8771798404836176785'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/11/99-problems-python-46.html' title='99 problems - python - 46'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-935434098312075980</id><published>2008-11-21T21:43:00.000-08:00</published><updated>2008-11-21T21:50:51.323-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pushups'/><category scheme='http://www.blogger.com/atom/ns#' term='fitness'/><title type='text'>100 Pushups: week 6 (8th try)</title><content type='html'>It just occurred to me that I've been doing this crazy 100 pushups thing since at least July.  I'm still humorously far from my goal, but damn if I'm still making some progress.  For instance this week after doing my various reps (150+ pushups in 20+ increments) I was able to get to 53 which is a new record for me.  &lt;br /&gt;&lt;br /&gt;But nevertheless it is still pretty funny that I've been doing the 6th week of a 6 week program for 8 weeks now.  And it *only* took me 6 weeks or so to finish week 5.  But I did finish.&lt;br /&gt;&lt;br /&gt;The idea of doing 100 pushups seemed sort of comical to me when I started this bad boy, but now it's my mission.  I just hope I finish this decade sometime.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-935434098312075980?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/935434098312075980/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=935434098312075980' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/935434098312075980'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/935434098312075980'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/11/100-pushups-week-6-8th-try.html' title='100 Pushups: week 6 (8th try)'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-2555965239216019944</id><published>2008-11-18T22:37:00.000-08:00</published><updated>2008-11-22T09:45:23.705-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='learning'/><title type='text'>Pragmatic Thinking and Learning</title><content type='html'>I've just started &lt;a href="http://www.pragprog.com/titles/ahptl/pragmatic-thinking-and-learning"&gt;this book&lt;/a&gt; and while I think that I will get lots of interesting things out of it, it has already set off some alarm bells with me.  The same alarm bell that goes off when I hear someone quote the "fact" that we only use 10% of out brains (well, maybe that's true for you...).&lt;br /&gt;&lt;br /&gt;In any case the author seems to stick pretty close to the Betty (Drawing on the Right Side of the Brain) Edwards L/R distinction which I've grown skeptical of over the years.  That was a low buzz no real alarm, just be wary. &lt;br /&gt;&lt;br /&gt;But next came a reference to the"Unskilled and Unaware" paper.  I've always thought that result sounded pretty reasonable, but today I came across &lt;a href="http://www.overcomingbias.com/2008/11/all-are-unaware.html"&gt;this&lt;/a&gt;.  &lt;br /&gt;&lt;br /&gt;Hopefully the book doesn't continue to be full of too many debunked/sketchy studies.  &lt;br /&gt;&lt;br /&gt;I actually expect I will like the book and recommend it, but I just wish you could get a clear, unambiguous picture of state of the art learning/creativity techniques with minimal "woo".&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-2555965239216019944?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/2555965239216019944/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=2555965239216019944' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/2555965239216019944'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/2555965239216019944'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/11/pragmatic-thinking-learning.html' title='Pragmatic Thinking and Learning'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-7482125446143984882</id><published>2008-11-16T07:04:00.000-08:00</published><updated>2008-12-13T19:32:25.533-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='99 problems'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>99 problems - python - 41</title><content type='html'>Given a range of integers by its lower and upper limit, print a list of all even numbers and their Goldbach composition.&lt;br /&gt;&lt;br /&gt;In most cases, if an even number is written as the sum of two prime numbers, one of them is very small. Very rarely, the primes are both bigger than say 50. Try to find out how many such cases there are in the range 2..3000. &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;# using goldbach as defined previously&lt;br /&gt;def goldbach_list(start,end,threshold=None):&lt;br /&gt;    start = max([start,4])&lt;br /&gt;    for n in range(start,end+1):&lt;br /&gt;        if n % 2 == 0:&lt;br /&gt;            i,j = goldbach(n)&lt;br /&gt;            if threshold == None or i &gt; threshold:&lt;br /&gt;                print "%d = %d + %d" % (n, i, j)&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-7482125446143984882?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/7482125446143984882/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=7482125446143984882' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/7482125446143984882'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/7482125446143984882'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/11/99-problems-python-41.html' title='99 problems - python - 41'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-6807433723210612574</id><published>2008-11-16T06:39:00.000-08:00</published><updated>2008-12-13T19:32:25.533-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='99 problems'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>99 problems - python - 40</title><content type='html'>Goldbach's conjecture. Goldbach's conjecture says that every positive even number greater than 2 is the sum of two prime numbers. Example: 28 = 5 + 23. It is one of the most famous facts in number theory that has not been proved to be correct in the general case. It has been numerically confirmed up to very large numbers (much larger than we can go with our Prolog system). Write a predicate to find the two prime numbers that sum up to a given even integer.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;# using primes_range from previous problem&lt;br /&gt;def goldbach(n):&lt;br /&gt;    assert n % 2 == 0&lt;br /&gt;&lt;br /&gt;    for i in primes_range(2, n):&lt;br /&gt;        for j in primes_range(i, n):&lt;br /&gt;            if i + j == n:&lt;br /&gt;                return (i,j)&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-6807433723210612574?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/6807433723210612574/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=6807433723210612574' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/6807433723210612574'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/6807433723210612574'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/11/99-problems-python-40.html' title='99 problems - python - 40'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-1034998657435335458</id><published>2008-11-15T22:30:00.000-08:00</published><updated>2008-12-13T19:32:25.533-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='99 problems'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>99 problems - python - 39</title><content type='html'>A list of prime numbers&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;# using is_prime from earlier problem&lt;br /&gt;def primes_range(start,end):&lt;br /&gt;    return [n for n in range(start, end+1) if is_prime(n)]&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-1034998657435335458?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/1034998657435335458/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=1034998657435335458' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/1034998657435335458'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/1034998657435335458'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/11/99-problems-python-39.html' title='99 problems - python - 39'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-4035416349072278249</id><published>2008-11-15T21:08:00.000-08:00</published><updated>2008-12-13T19:32:25.534-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='99 problems'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>99 problems - python - 37</title><content type='html'>Calculate Euler's totient function phi(m) (improved). See problem 34 for the definition of Euler's totient function. If the list of the prime factors of a number m is known in the form of problem 36 then the function phi(m) can be efficiently calculated as follows: Let ((p1 m1) (p2 m2) (p3 m3) ...) be the list of prime factors (and their multiplicities) of a given number m. Then phi(m) can be calculated with the following formula:&lt;br /&gt;&lt;br /&gt;phi(m) = (p1 - 1) * p1 ** (m1 - 1) + (p2 - 1) * p2 ** (m2 - 1) + (p3 - 1) * p3 ** (m3 - 1) + ...&lt;br /&gt;&lt;br /&gt;Note that a ** b stands for the b'th power of a. Note: Actually, the official problems show this as a sum, but it should be a product. &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;# using prime_factors_mult from earlier&lt;br /&gt;def totient_phi2(n):&lt;br /&gt;    if n == 1:&lt;br /&gt;        return 1&lt;br /&gt;&lt;br /&gt;    def product(lst):&lt;br /&gt;        return reduce(lambda x,y: x*y, lst)&lt;br /&gt;    return product([((p-1)*(p**(m-1)))  for (p, m) in prime_factors_mult(n)])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I'm pretty sure that "reduce" will be leaving python 3.  We hardly knew ye.  I don't understand that removal.  Of course I'll just be adding it right the hell back in.  And no one can stop me!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-4035416349072278249?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/4035416349072278249/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=4035416349072278249' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/4035416349072278249'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/4035416349072278249'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/11/99-problems-python-37.html' title='99 problems - python - 37'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-3455045307233728454</id><published>2008-11-15T19:41:00.001-08:00</published><updated>2008-12-13T19:32:25.534-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='99 problems'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>99 problems - python - 36</title><content type='html'>Determine the prime factors of a given positive integer.&lt;br /&gt;&lt;br /&gt;Construct a list containing the prime factors and their multiplicity.&lt;br /&gt;&lt;br /&gt;Example:&lt;br /&gt;&lt;br /&gt;* (prime-factors-mult 315)&lt;br /&gt;((3 2) (5 1) (7 1))&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#using prime_factors from the previous problem&lt;br /&gt;import itertools&lt;br /&gt;def prime_factors_mult(n):&lt;br /&gt;    return [(x, len(list(y))) for x,y in itertools.groupby(prime_factors(n))]&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-3455045307233728454?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/3455045307233728454/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=3455045307233728454' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/3455045307233728454'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/3455045307233728454'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/11/99-problems-python-36.html' title='99 problems - python - 36'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-2811298352622696375</id><published>2008-11-15T15:55:00.000-08:00</published><updated>2008-12-13T19:32:25.534-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='99 problems'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>99 problems - python - 35</title><content type='html'>Determine the prime factors of a given positive integer. Construct a flat list containing the prime factors in ascending order.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def prime_factors(n):&lt;br /&gt;    factor = 2&lt;br /&gt;    while n &gt; 1:&lt;br /&gt;        if n % factor == 0:&lt;br /&gt;            yield factor&lt;br /&gt;            n = n / factor&lt;br /&gt;        else:&lt;br /&gt;            factor += 1&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This ended up being simpler and more beautiful than I was expecting.  I think I'm falling in love with python all over again.  *snif*&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-2811298352622696375?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/2811298352622696375/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=2811298352622696375' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/2811298352622696375'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/2811298352622696375'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/11/99-problems-python-35.html' title='99 problems - python - 35'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-4147008101691205748</id><published>2008-11-15T14:26:00.000-08:00</published><updated>2008-12-13T19:32:25.535-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='99 problems'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>99 problems - python - 34</title><content type='html'>Calculate Euler's totient function phi(m). Euler's so-called totient function phi(m) is defined as the number of positive integers r (1 &lt;= r &lt; m) that are coprime to m. Example: m = 10: r = 1,3,7,9; thus phi(m) = 4. Note the special case: phi(1) = 1.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;# using coprime defined previously&lt;br /&gt;def totient_phi(m):&lt;br /&gt;    if m == 1:&lt;br /&gt;        return 1&lt;br /&gt;    return len([r for r in range(1,m) if coprime(r,m)])&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-4147008101691205748?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/4147008101691205748/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=4147008101691205748' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/4147008101691205748'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/4147008101691205748'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/11/99-problems-python-34.html' title='99 problems - python - 34'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-9135658751136418746</id><published>2008-11-15T06:36:00.000-08:00</published><updated>2008-11-15T06:40:33.201-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='emacslisp'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='emacs'/><title type='text'>emacs python mode from scratch: stage 12 - bicycle repair man</title><content type='html'>&lt;p&gt;For this session we'll get &lt;a href="http://bicyclerepair.sourceforge.net/"&gt;bicycle repair man&lt;/a&gt; support working.&lt;br /&gt;&lt;p&gt;First let's see if we can get brm working by itself and then see how it integrates into emacs.&lt;br /&gt;&lt;p&gt;To start it I ran:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;M-x python-setup-brm&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;It took a little googling and experimentation to figure out how to use this.  But I finally figured out that if you select some text then the menus do their thing.&lt;br /&gt;&lt;p&gt;This is a strange little tool.  I guess I'm so used to refactoring with global search and replaces that this seems overly constraining.  But perhaps it's just unfamiliar. I suspect that if you got used to it then you could learn to like it, but I'm skeptical.   One behavior that was very counter intuitive is that you can't use the normal emacs undo to revert the refactoring change. You have to do it via the menus (or presumably the brm console). In any case let's look at the code that runs brm.&lt;br /&gt;&lt;p&gt;Firstly it does an autoload of &lt;a href="something"&gt;pymacs&lt;/a&gt;.&lt;br /&gt;&lt;p&gt;I've never really played with this before but you can see that it is loaded correctly and working by doing the following in *scratch*:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(pymacs-eval "2 + 2")&lt;br /&gt;4&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;The second prep step is to get the bicycle repair man module itself loaded:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(autoload 'brm-init "bikemacs")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Which I'm assuming works since I was able to do a refactoring&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;python-setup-brm&lt;br /&gt;  &lt;p&gt;This code is mostly just busy work to set up of menus.&lt;br /&gt;  &lt;p&gt;Strangely this code ends with an (error ... ) embedded in an (error ...).&lt;br /&gt;  Not sure what that is about.&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;p&gt;Interestingly in the comments the author of python.el expresses doubts that brm is useful.&lt;br /&gt;Note to self, I should also look at ropemacs.  Looks like &lt;a href="http://rope.sourceforge.net/ropemacs.html"&gt;rope&lt;/a&gt; has more features and if I recall an earlier experiment correctly does autocomplete of python variables/modules as well.&lt;br /&gt;&lt;p&gt; Any way, onward and upward.  Next stop, context sensitive help.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-9135658751136418746?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/9135658751136418746/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=9135658751136418746' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/9135658751136418746'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/9135658751136418746'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/11/emacs-python-mode-from-scratch-stage-11_15.html' title='emacs python mode from scratch: stage 12 - bicycle repair man'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-4166402663726745169</id><published>2008-11-15T06:21:00.000-08:00</published><updated>2008-12-13T19:32:25.535-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='99 problems'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>99 problems - python - 33</title><content type='html'>Determine whether two positive integer numbers are coprime. Two numbers are coprime if their greatest common divisor equals 1. &lt;br /&gt;&lt;br /&gt;Using gcd as defined in the previous problem:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def coprime(m,n):&lt;br /&gt;    return gcd(m,n) == 1&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-4166402663726745169?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/4166402663726745169/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=4166402663726745169' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/4166402663726745169'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/4166402663726745169'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/11/99-problems-python-33.html' title='99 problems - python - 33'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-8473038285632581553</id><published>2008-11-12T21:42:00.000-08:00</published><updated>2008-12-13T19:32:47.280-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='99 problems'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>99 problems - python - 32</title><content type='html'>Determine the greatest common divisor of two positive integer numbers. &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def gcd(m,n):&lt;br /&gt;    m,n = sorted([m,n])&lt;br /&gt;&lt;br /&gt;    while m != 0:&lt;br /&gt;        m, n = (n % m), m&lt;br /&gt;&lt;br /&gt;    return n&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Humorously, I resisted using a recursive algorithm.  I don't know why, but it just seemed more reasonable to use a straight iterative one.  Also I really like how m,n = (n%m), m saves us from having to declare a temporary variable.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-8473038285632581553?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/8473038285632581553/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=8473038285632581553' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/8473038285632581553'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/8473038285632581553'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/11/99-problems-python-32.html' title='99 problems - python - 32'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-6417557514387027101</id><published>2008-11-12T19:22:00.000-08:00</published><updated>2008-12-05T20:46:49.707-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='emacslisp'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='emacs'/><title type='text'>emacs python mode from scratch: stage 11 - skeleton</title><content type='html'>&lt;p&gt;We are actually getting to the point where this mode would actually be useful for developing with.&lt;br /&gt;&lt;p&gt;Next let's get &lt;a href="http://www.emacswiki.org/cgi-bin/wiki/SkeletonMode"&gt;skeleton&lt;/a&gt; support working now.&lt;br /&gt;&lt;p&gt;Here are the new functions I've copied over:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;python-use-skeletons&lt;br /&gt;python-skeletons&lt;br /&gt;python-mode-abbrev-table&lt;br /&gt;def-python-skeleton&lt;br /&gt;def-python-skeleton if&lt;br /&gt;define-skeleton python-else&lt;br /&gt;def-python-skeleton while&lt;br /&gt;def-python-skeleton for&lt;br /&gt;def-python-skeleton try/except&lt;br /&gt;define-skeleton python-target&lt;br /&gt;def-python-skeleton try/finally&lt;br /&gt;def-python-skeleton def&lt;br /&gt;def-python-skeleton class&lt;br /&gt;python-default-template "if"&lt;br /&gt;python-expand-template&lt;br /&gt;&lt;/pre&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;python-use-skeletons&lt;br /&gt;    &lt;p&gt;This controls whether or not skeleton functions are added to the python-mode-abbrev-table&lt;br /&gt;&lt;li&gt;python-skeletons&lt;br /&gt;    &lt;p&gt;A list of all the skeletons.  apparently used when working on compound statements.&lt;br /&gt;&lt;li&gt;python-mode-abbrev-table&lt;br /&gt;    &lt;p&gt;This is where the actual abbrevs are stored&lt;br /&gt;&lt;li&gt;def-python-skeleton&lt;br /&gt;    &lt;p&gt;A macro for getting a skeletons "registered" in the abbrev system. First it creates a name for the skeleton of the form: python-insert-foo. Then if adds this name to the pyton-mode-abbrev-table.  Finally it calls define-skeleton and creates the skeleton.&lt;br /&gt;&lt;li&gt;Each of the following create the appropriate skeleton by name&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def-python-skeleton if&lt;br /&gt;def-python-skeleton while&lt;br /&gt;def-python-skeleton for&lt;br /&gt;def-python-skeleton try/except&lt;br /&gt;def-python-skeleton try/finally&lt;br /&gt;def-python-skeleton def&lt;br /&gt;def-python-skeleton class&lt;br /&gt;&lt;/pre&gt;&lt;li&gt;define-skeleton python-else&lt;br /&gt;    &lt;p&gt;else clause used by if and other else-able statements. One reason I would find it hard to get excited about using this particular skeleton/template system is that it seems sort of ridiculous to be prompted after a for loop or a while loop, etc for an else clause.   I can count on 1 hand how many times i've ever used an else clause other than part of an if cascade.  It seems like an unnecessary burden to make the user consider this case every time they are writing a for loop.&lt;br /&gt;&lt;li&gt;define-skeleton python-target&lt;br /&gt;    &lt;p&gt;Helper used by try/except&lt;br /&gt;&lt;li&gt;python-default-template&lt;br /&gt;    &lt;p&gt;Variable that defines what template to use by default in python-expand-template&lt;br /&gt;&lt;li&gt;python-expand-template&lt;br /&gt;   &lt;p&gt;This function is for allowing a skeleton to be used directly as a key sequence rather than as an abbrev.  Interestingly if defaults to the "if" template but then let's you update what the default is.&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;p&gt; I'm not totally sold on the value of skeletons for python. The syntax is already so minimal that the distraction of writing code through the minibuffer doesn't seem like a big win but it's something i'd like to explore in detail sometime and see if it works for me if i give it a chance.&lt;br /&gt;&lt;p&gt; Next let's get bicycle repair man support working.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-6417557514387027101?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/6417557514387027101/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=6417557514387027101' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/6417557514387027101'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/6417557514387027101'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/11/emacs-python-mode-from-scratch-stage-11.html' title='emacs python mode from scratch: stage 11 - skeleton'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-4442943505424177061</id><published>2008-11-08T22:36:00.000-08:00</published><updated>2008-12-13T19:32:47.280-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='99 problems'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>99 problems - python - 31</title><content type='html'>Determine whether a given integer number is prime. &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import math&lt;br /&gt;def is_prime(n):&lt;br /&gt;    for test in range(2, int(math.sqrt(n) + 1)):&lt;br /&gt;        if n % test == 0:&lt;br /&gt;            return False&lt;br /&gt;&lt;br /&gt;    return True&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;(no, I'm not skipping problems, the original set skips too)&lt;br /&gt;&lt;br /&gt;BTW, below is the prime number generator function in Haskell in case you haven't seen it before.  For my money it's about the most beautiful line (or 2) of code I've ever seen.  It almost gives me goose bumps when I see it.&lt;br /&gt;&lt;br /&gt;It's deceptively simple looking, but the more you look at it the more your mind is guaranteed to be blown as it sinks in how subtly cool this is&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;primes = sieve [2..]&lt;br /&gt;    where sieve (p:xs) = p : sieve [x | x&lt;-xs, x `mod` p /= 0]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;* (guarantee not binding any where)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-4442943505424177061?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/4442943505424177061/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=4442943505424177061' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/4442943505424177061'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/4442943505424177061'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/11/99-problems-python-31.html' title='99 problems - python - 31'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-7682387281923644442</id><published>2008-11-08T22:29:00.000-08:00</published><updated>2008-12-13T19:32:47.280-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='99 problems'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>99 problems - python - 28</title><content type='html'>Sorting a list of lists according to length of sublists&lt;br /&gt;&lt;br /&gt;a) We suppose that a list contains elements that are lists themselves. The objective is to sort the elements of this list according to their length. E.g. short lists first, longer lists later, or vice versa.&lt;br /&gt;&lt;br /&gt;Example:&lt;br /&gt;* (lsort '((a b c) (d e) (f g h) (d e) (i j k l) (m n) (o)))&lt;br /&gt;((O) (D E) (D E) (M N) (A B C) (F G H) (I J K L))&lt;br /&gt;&lt;br /&gt;b) Again, we suppose that a list contains elements that are lists themselves. But this time the objective is to sort the elements of this list according to their length frequency; i.e., in the default, where sorting is done ascendingly, lists with rare lengths are placed first, others with a more frequent length come later.&lt;br /&gt;&lt;br /&gt;Example:&lt;br /&gt;* (lfsort '((a b c) (d e) (f g h) (d e) (i j k l) (m n) (o)))&lt;br /&gt;((i j k l) (o) (a b c) (f g h) (d e) (d e) (m n))&lt;br /&gt;&lt;br /&gt;(a) it should be enough to do&lt;br /&gt;&lt;pre&gt;sorted(lst_of_lsts, key=lambda x: len(x))&lt;/pre&gt;&lt;br /&gt;If you want the lists of same length to be alphabetically sorted as well then you could do:&lt;br /&gt;&lt;pre&gt;sorted(sorted(lst_of_lsts), key=lambda x: len(x))&lt;/pre&gt;&lt;br /&gt;(b)&lt;pre&gt;&lt;br /&gt;def length_frequency_sort(lst_of_lists):&lt;br /&gt;    freqs = {}&lt;br /&gt;&lt;br /&gt;    for lst in lst_of_lists:&lt;br /&gt;        freqs.setdefault(len(lst), 0)&lt;br /&gt;        freqs[len(lst)] += 1&lt;br /&gt;&lt;br /&gt;    return sorted(lst_of_lists, key=lambda x: freqs[len(x)])&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-7682387281923644442?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/7682387281923644442/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=7682387281923644442' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/7682387281923644442'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/7682387281923644442'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/11/99-problems-python-28.html' title='99 problems - python - 28'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-7752684799254475474</id><published>2008-11-08T22:10:00.000-08:00</published><updated>2008-12-13T19:32:47.281-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='99 problems'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>99 problems - python - 27</title><content type='html'>Group the elements of a set into disjoint subsets.&lt;br /&gt;&lt;br /&gt;a) In how many ways can a group of 9 people work in 3 disjoint subgroups of 2, 3 and 4 persons? Write a function that generates all the possibilities and returns them in a list.&lt;br /&gt;&lt;br /&gt;Example:&lt;br /&gt;* (group3 '(aldo beat carla david evi flip gary hugo ida))&lt;br /&gt;( ( (ALDO BEAT) (CARLA DAVID EVI) (FLIP GARY HUGO IDA) )&lt;br /&gt; ... )&lt;br /&gt;&lt;br /&gt;b) Generalize the above predicate in a way that we can specify a list of group sizes and the predicate will return a list of groups.&lt;br /&gt;&lt;br /&gt;Example:&lt;br /&gt;* (group '(aldo beat carla david evi flip gary hugo ida) '(2 2 5))&lt;br /&gt;( ( (ALDO BEAT) (CARLA DAVID) (EVI FLIP GARY HUGO IDA) )&lt;br /&gt; ... )&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def combination(lst, size):&lt;br /&gt;    #defined as in previous problem&lt;br /&gt;&lt;br /&gt;def grouped_combos(lst, sizes):&lt;br /&gt;    assert len(lst) == sum(sizes)&lt;br /&gt;&lt;br /&gt;    if not sizes:&lt;br /&gt;        yield []&lt;br /&gt;    else:&lt;br /&gt;        for combo in combinations(lst, sizes[0]):&lt;br /&gt;            rest = sorted(list(set(lst) - set(combo)))&lt;br /&gt;            for rest_combo in grouped_combos(rest, sizes[1:]):&lt;br /&gt;                yield [combo] + rest_combo&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I have a sorted thrown in since using set gave unreliable ordering after the set difference.  I really love recursion.  It seems like the above would be quite annoying without it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-7752684799254475474?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/7752684799254475474/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=7752684799254475474' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/7752684799254475474'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/7752684799254475474'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/11/99-problems-python-27.html' title='99 problems - python - 27'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-8757270064021153859</id><published>2008-11-08T16:49:00.001-08:00</published><updated>2008-12-13T19:32:47.281-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='99 problems'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>99 problems - python - 26</title><content type='html'>Generate the combinations of K distinct objects chosen from the N elements of a list In how many ways can a committee of 3 be chosen from a group of 12 people? We all know that there are C(12,3) = 220 possibilities (C(N,K) denotes the well-known binomial coefficients). For pure mathematicians, this result may be great. But we want to really generate all the possibilities in a list.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def combinations(lst, size):&lt;br /&gt;    if len(lst) &lt; size:&lt;br /&gt;        return&lt;br /&gt;    elif size == 0:&lt;br /&gt;        yield []&lt;br /&gt;    else:&lt;br /&gt;        # keep first&lt;br /&gt;        for combo in combinations(lst[1:], size-1):&lt;br /&gt;            yield [lst[0]] + combo&lt;br /&gt;        # skip first&lt;br /&gt;        for combo in combinations(lst[1:], size):&lt;br /&gt;            yield combo&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;After I wrote this I remembered that python's test/test_generators.py also had an implementation of this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; def gcomb(x, k):&lt;br /&gt;...     "Generate all combinations of k elements from list x."&lt;br /&gt;...&lt;br /&gt;...     if k &gt; len(x):&lt;br /&gt;...         return&lt;br /&gt;...     if k == 0:&lt;br /&gt;...         yield []&lt;br /&gt;...     else:&lt;br /&gt;...         first, rest = x[0], x[1:]&lt;br /&gt;...         # A combination does or doesn't contain first.&lt;br /&gt;...         # If it does, the remainder is a k-1 comb of rest.&lt;br /&gt;...         for c in gcomb(rest, k-1):&lt;br /&gt;...             c.insert(0, first)&lt;br /&gt;...             yield c&lt;br /&gt;...         # If it doesn't contain first, it's a k comb of rest.&lt;br /&gt;...         for c in gcomb(rest, k):&lt;br /&gt;...             yield c&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Which is amazingly similar.  I guess in python there really is OBWTDI (one best way to do it).&lt;br /&gt;&lt;br /&gt;So anyway I think from here on out the questions will continue to be less and less "trivial".&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-8757270064021153859?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/8757270064021153859/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=8757270064021153859' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/8757270064021153859'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/8757270064021153859'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/11/99-problems-python-26.html' title='99 problems - python - 26'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-4962909031731452996</id><published>2008-11-08T14:07:00.000-08:00</published><updated>2008-12-13T19:32:47.281-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='99 problems'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>99 problems - python - 25</title><content type='html'>Generate a random permutation of the elements of a list.&lt;br /&gt;&lt;br /&gt;Once again python provides us with a solution, so we don't have to code at all.&lt;br /&gt;&lt;br /&gt;random.shuffle(lst)&lt;br /&gt;&lt;br /&gt;Since this works in place you may want to copy your list first before operating on.&lt;br /&gt;&lt;br /&gt;Keep your shirt on, these do get interesting after a while.  But it is nice to see how many "problems" are already solved for us.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-4962909031731452996?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/4962909031731452996/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=4962909031731452996' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/4962909031731452996'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/4962909031731452996'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/11/99-problems-python-25.html' title='99 problems - python - 25'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-8963752160899195505</id><published>2008-11-07T22:36:00.000-08:00</published><updated>2008-12-13T19:32:47.282-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='99 problems'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>99 problems - python - 24</title><content type='html'>Lotto: Draw N different random numbers from the set 1..M. &lt;br /&gt;&lt;br /&gt;No reason to use anything other than random.sample(population, count)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-8963752160899195505?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/8963752160899195505/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=8963752160899195505' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/8963752160899195505'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/8963752160899195505'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/11/99-problems-python-24.html' title='99 problems - python - 24'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-3489613477597135456</id><published>2008-11-07T22:28:00.000-08:00</published><updated>2008-11-07T22:32:54.877-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='emacslisp'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='emacs'/><title type='text'>emacs python mode from scratch: stage 10 - pychecker</title><content type='html'>&lt;p&gt;My next step is to get pychecker working.  Turns out it's not much code.  Here are the functions:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;python-check-command&lt;br /&gt;   &lt;p&gt;the default string to use as the command for running pychecker&lt;br /&gt;&lt;li&gt;python-saved-check-command&lt;br /&gt;   &lt;p&gt;a cache of the command above and the name of the file we are&lt;br /&gt;   currently working with&lt;br /&gt;&lt;li&gt;python-check&lt;br /&gt;   &lt;p&gt;Pretty straightforward but a couple of things to note.&lt;br /&gt;   &lt;p&gt;I wasn't aware that the "interactive" command took straight elisp as an argument.  I had just assumed you had to use the magic argument strings.&lt;br /&gt;   &lt;p&gt;It's a little strange that they use an old school emacs lisp regular expression ("(\\([^,]+\\), line \\([0-9]+\\))") rather than the rx style they seemed to use everywhere else.&lt;/ul&gt;&lt;br /&gt;&lt;p&gt;A note on pychecker.  I find this utility fascinating.  For good and bad reasons.  It's really quite useful.  C-c C-w is reflexively typed after saving a file.  And the fact that it is so useful makes it extra confusing that it it seems sort of abandoned (at least last I checked).&lt;br /&gt;&lt;p&gt;As a future project I'd like to look at it (or one of it's cohorts, pylint, pyflakes, etc) and see how it works and perhaps complain less and help more,but it seems strangely abandoned now.  I did start to take a look at one point and it seemed very complex so I ran away with my tail between my legs.  But fortunately I have a short memory for scary things and I'll likely return to it someday.&lt;br /&gt;&lt;p&gt;Getting pychecker working required less code than I suspected.&lt;br /&gt;&lt;p&gt;Next I'll do skeleton support which if nothing else is a lot more lines of code.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-3489613477597135456?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/3489613477597135456/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=3489613477597135456' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/3489613477597135456'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/3489613477597135456'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/11/emacs-python-mode-from-scratch-stage-10.html' title='emacs python mode from scratch: stage 10 - pychecker'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-2401206575151120055</id><published>2008-11-05T21:41:00.000-08:00</published><updated>2008-11-05T21:43:41.025-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='99 problems'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>99 problems - python - 23</title><content type='html'>Extract a given number of randomly selected elements from a list. &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import random&lt;br /&gt;def random_select(lst, n):&lt;br /&gt;    for x in range(n):&lt;br /&gt;        yield random.choice(lst)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Depending on how you interpret the problem you may just want to use random.sample&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-2401206575151120055?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/2401206575151120055/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=2401206575151120055' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/2401206575151120055'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/2401206575151120055'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/11/99-problems-python-23.html' title='99 problems - python - 23'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-7666043802817060889</id><published>2008-11-05T21:36:00.000-08:00</published><updated>2008-11-05T21:37:37.433-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='99 problems'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>99 problems - python - 22</title><content type='html'>Create a list containing all integers within a given range.&lt;br /&gt;&lt;br /&gt;OK, just use range().  Anything else would be silly.   Depending on how you interpret this problem you may need to add one to the second parameter.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-7666043802817060889?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/7666043802817060889/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=7666043802817060889' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/7666043802817060889'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/7666043802817060889'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/11/99-problems-python-22.html' title='99 problems - python - 22'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-4694249079377904111</id><published>2008-11-03T21:53:00.000-08:00</published><updated>2008-11-03T21:56:21.294-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='99 problems'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>99 problems - python - 21</title><content type='html'>Insert an element at a given position into a list. &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def insert_at(x, lst, n):&lt;br /&gt;    return lst[:n-1] + [x] + lst[n-1:]&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-4694249079377904111?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/4694249079377904111/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=4694249079377904111' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/4694249079377904111'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/4694249079377904111'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/11/99-problems-python-21.html' title='99 problems - python - 21'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-3821242294401062234</id><published>2008-11-02T20:59:00.000-08:00</published><updated>2008-11-02T21:01:02.458-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='99 problems'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>99 problems - python - 20</title><content type='html'>Remove the K'th element from a list.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def remove_at(lst, n):&lt;br /&gt;    return lst[:(n-1)] + lst[n:]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The simplest thing to do would be to just use .pop(), but I assuming that a non-destructive operation would be preferred in this case.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-3821242294401062234?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/3821242294401062234/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=3821242294401062234' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/3821242294401062234'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/3821242294401062234'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/11/99-problems-python-20.html' title='99 problems - python - 20'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2921404189269723440.post-4534470130665201519</id><published>2008-11-02T14:45:00.001-08:00</published><updated>2008-11-02T14:46:26.082-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='99 problems'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>99 problems - python - 19</title><content type='html'>Rotate a list N places to the left.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def rotate(lst, n):&lt;br /&gt;    n = n % len(lst)&lt;br /&gt;    x, y = lst[:n], lst[n:]&lt;br /&gt;    return y + x&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2921404189269723440-4534470130665201519?l=dustbunnylair.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dustbunnylair.blogspot.com/feeds/4534470130665201519/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=4534470130665201519' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/4534470130665201519'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2921404189269723440/posts/default/4534470130665201519'/><link rel='alternate' type='text/html' href='http://dustbunnylair.blogspot.com/2008/11/99-problems-python-19.html' title='99 problems - python - 19'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width=
