<?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'><id>tag:blogger.com,1999:blog-2921404189269723440</id><updated>2009-11-10T21:46:26.777-08:00</updated><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'/><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=26&amp;max-results=25'/><author><name>dustbunny</name><uri>http://www.blogger.com/profile/01565895340687581821</uri><email>noreply@blogger.com</email></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>117</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><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'/&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='https://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=3200711900597152008' title='2 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:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='04073157226041411956'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>2</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'/&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='https://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:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='04073157226041411956'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>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'/&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='https://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:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='04073157226041411956'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>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'/&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='https://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:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='04073157226041411956'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>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'/&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='https://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:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='04073157226041411956'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>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'/&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='https://www.blogger.com/comment.g?blogID=2921404189269723440&amp;postID=5282634755582881211' title='23 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:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='04073157226041411956'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>23</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'/&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='https://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:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='04073157226041411956'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>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'/&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='https://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:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='04073157226041411956'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>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'/&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='https://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:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='04073157226041411956'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>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'/&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='https://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:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='04073157226041411956'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>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'/&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='https://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:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='04073157226041411956'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>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'/&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='https://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:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='04073157226041411956'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>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'/&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='https://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:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='04073157226041411956'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>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'/&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='https://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:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='04073157226041411956'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>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'/&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='https://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:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='04073157226041411956'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>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'/&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='https://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:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='04073157226041411956'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>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'/&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='https://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:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='04073157226041411956'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>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'/&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='https://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:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='04073157226041411956'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>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'/&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='https://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:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='04073157226041411956'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>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'/&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='https://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:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='04073157226041411956'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>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'/&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='https://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:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='04073157226041411956'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>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'/&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='https://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:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='04073157226041411956'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>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'/&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='https://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:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='04073157226041411956'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>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'/&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='https://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:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='04073157226041411956'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>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'/&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='https://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:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='04073157226041411956'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry></feed>