Rediscovering closures and nested functions

When you’ve spent years coding pretty much everything in Java, it’s hard to break out of the Java way of doing things. It means that you tend to forget that other languages might have things called closures, for example. Here’s how a closure looks in Python:


lambda x:dosomethingto(x,anothervariable)

The neat thing is that this closure can be passed around like a first class object. Also, anothervariable, bound in the outer scope, is accessible to the lambda expression when it is invoked later. You can do stuff like:


if isinstance(somevalue, int) or isinstance(somevalue, float):
  isfunny=lambda x:log(x)>somevalue
else:
  isfunny=lambda x:isalpha(x) and islower(x)

funnyitems=[item for item in mylist if isfunny(item)]
seriousitems=[item for item in mylist if not isfunny(item)]

Sure there are other ways to do the same thing. But this is arguably more elegant.

There’s also nested functions, which are kind of like closures. Rather than contriving an example, I’ll give one from the book I’m reading, Programming Collective Intelligence. In chapter 8, a crossvalidation function is defined. Forgetting the specifics and purpose of this function, just know that the crossvalidate function returns low numbers for good solutions and high numbers for bad solutions. Earlier in the book, we’d already been introduced to optimisation problems and cost functions. The cost functions take a single argument, which is a potential solution to be costed. The crossvalidate function takes a bunch of parameters, so for this and other reasons it can’t be used directly. But you can do something like this:


def createcostfunction(algf, data):
  def costf(scale):
    sdata=rescale(data,scale)
    return crossvalidate(algf,sdata,trials=10)
  return costf

So now your costf function knows about algf and data, despite not having them as parameters. That is, the bound variables to algf and data are available to costf when it is called at some later time:


costf=createcostfunction(knearest,mydata)
annealingoptimise(wdomain,costf,step=2)

So when annealingoptimise eventually invokes costf, costf has access to knearest and data. That is the bindings of algf to knearest and data to mydata live on after the execution of createcostfunction completes. Cool.