Closures without thinking. Was Re: A not stupid question (no questions are stupid)
Enrico Colombini escribió:
> As for your intriguing second example, I am well aware of the power of
> closures on the rational level (that is a closure over 'tab', right?),
> I still haven't reached the stage where I could use them without thinking
> things over first (despite reading "The little lisper"). I suppose a bit
> exercise is in order.
Yes, exactly. Ah, the little lisper... yes, indeed.
While closures can do wierd and wonderful things, there is a really simple
idiom that can certainly be used without much thought. The situation is
that you have a function of two or more arguments, and the function is
going to think about one of those arguments quite a bit before it produces
a result; however, it is quite normal for that argument to be repeated. An
example of this situation is the search pattern in a string library which
compiles regexes, but I'm sure you can think of many more examples.
One way to do this is to memoise the result of the thought in a hidden
table (dictionary, whatever) and this is quite common in regex libraries.
However, functional languages give another alternative: just build a
function and let the library user worry about keeping it or not.
Here is a really simple example. The pattern we're working with is this:
function(a, b) --- do something -- return v end
function(a) return function(b) -- do something -- return v end end
This creates a function with a "pre-applied". (In Lua 4.0 you also have to
replace all the instances of "a" in -- do something -- with "%a". In Lua
4.1 this is not necessary.)
function logarithm(a, b)
-- computes log base a of b
return log(b) / log(a)
function logmaker(a) return function (b)
return log(b) / log(%a)
But I don't have to compute log(a) every time, so I move it up a level:
local loga = log(a)
return log(b) / %loga
And now I can define:
log2 = logmaker(2)
log10 = logmaker(10)
which are functions, so I can use them normally:
bits = log2(foo)
digits = log10(foo)
This is quite efficient. Lua does not make a separate copy of the code for
each function produced; all it does is create what is called a "closure"
which consists of a pointer to the code and the "closed variables". So what
you get is sort of like this:
log2: ->unnamed function 1
log10: ->unnamed function 1
unnamed function 1:
parameter list: (b)
return log(b) / %loga
Hope that helps.
> local loga = log(a)
> return function(b)
> return log(b) / %loga
Thanks for the examples, I'll try to learn thinking more 'closurely' at the
first suitable project (I've found that actually using concepts in real
work makes them stick much better in one's mind).