Tuesday, August 24, 2010

ECMAScript - turning a binary function into a variadic one

In my previous post, I mentioned that it's easy to take a binary JavaScript function and turn it into a variadic one. Here's the helper function to do that:
function variadic(fun, val0f, val1f) {
return function() {
var len = arguments.length;
if (len == 0) {
return val0f();
} else if (len == 1) {
if (val1f != undefined) {
return val1f(arguments[0]);
}
else {
return arguments[0];
}
} else {
var tmp = fun(arguments[0], arguments[1]);
for (index = 2; index < len; index++) {
tmp = fun(tmp, arguments[index]);
}
return tmp;
}
};
}
The 'variadic' function takes three parameters:
  1. the binary function;
  2. a function that returns the value for the variadic version when called with zero arguments
  3. a function that returns the value for the variadic version when called on a single. If not specified, the first argument to the variadic function is returned as is when called on a single argument.
For example, the summation operator is defined as
var sum = variadic(function(x,y) {return x+y;},
function() { return 0;},
function(x) { return x;});

while the equivalent of Scheme's / function is defined as
var div = variadic(function(x,y) {return x/y; },
function() { throw "not enough arguments to div"; },
function(x) { return 1/x;});
It is now possible to call
div.apply(this, [1,2,3,4,5])
to get 0.008333333333333333.


4 comments:

Jon-Carlos Rivera said...

Math.max() isn't a binary function.

Math.max(1,2,3,4,5) // => 5

Dominique Boucher said...

@Jon-Carlos You are absolutely right. I was totaly mistaken on that one. Thanks!

Cristian Sanchez said...

Since it's basically the reduce (or left fold) operation, you can make a version for simple functions using Array.prototype.reduce found in Javascript 1.8 or ECMA 5th edition.

var variadicify = function () {
var args = arguments;
return function () {
return Array.prototype.reduce.apply(arguments, args);
}
};
var findMax = variadicify(function (x,y) { return Math.max(x,y) }, Number.NEGATIVE_INFINITY);

findMax(1,2,3,4); // 4

var sum = variadicify(function (x, y) { return x + y }, 0);

sum() // 0
sum(1) // 1
sum(1,2,3,4) // 10

Dominique Boucher said...

@Christian Nice! I always forget that ECMAScript 5th edition includes the reduce function...