26 June, 2009

Lambdas and closures.

Two of the smartest things about JavaScript are lambdas and closures, which were taken from Scheme.

Why should you care about this? Because, using lambdas and closures, you can do things that would either be impossible or difficult without. An example?

Math.random = (function () {
    var random = Math.random;
    return function (min, max) {
        return max ? (random() * (max - min + 1) + min) : random();
    };
}());

What does this allow you to do? Well, hopefully you know that in JavaScript, you can use Math.random() to return a pseudo-random float between 0.0 and 1.0. This is very useful, of course, as you can then manipulate that value in various ways.

Something I had need of is a function that will return a pseudo-random number within a range. In other words, I wanted to call Math.random(1, 6) to simulate a dice-roll (returning a number between 1 and 6).

"What? How?! You're overwriting Math.random, yet you still need to be able to call it!", you may be saying. That is all true, of course. Even though I have to overwrite Math.random, I still want access to the old version. This is possible (and easy) with closures.

First, I create an anonymous function (lambda) and execute it immediately. Inside I create a reference to Math.random in the variable random. I can now refer to the old Math.random within the anonymous function.

Because I'm assigning this to a variable, whatever the lambda returns will be put into Math.random. In this case, I return a function, which has access to the variable random. This is called a closure.

Now, within the returned function, I can use random to refer to the old Math.random, allowing me to perform all the wizardry I need. Aren't lambdas and closures wonderful?

Here's some examples of how to use it:

// Returns a pseudo-random float between 0.0 and 1.0:
Math.random();

// Returns a pseudo-random float between 5.0 and 8.0:
Math.random(5, 8);

// Returns a pseudo-random int between 5 and 8:
Math.floor(Math.random(5, 8));

Note:  Math.floor is significant. Math.round would produce incorrect numbers.

No comments:

Post a Comment