Accumulators

We've looked at some simple array functions which add filtering and pipes to arrays. Another useful tool is the accumulator. Accumulators aid in building up a single result by iterating over a collection. Many common operations such as summing up the elements of an array can be implemented using an accumulator instead of a loop.

Recursion is popular within functional programming languages and many of them actually offer an optimization called "tail call optimization". A language that supports this provides optimizations for functions using recursion in which the stack frame is reused. This is very efficient and can easily replace most loops. Details on whether tail call optimization is supported in any JavaScript interpreter are sketchy. For the most part it doesn't seem like it is but we can still make use of recursion.

The problem with for loops is that the control flow through the loop is mutable. Consider this rather easy-to-make mistake:

let result = "";
let multiArray = [[1,2,3], ["a", "b", "c"]];
for(vari=0; i<multiArray.length; i++)
  for(var j=0; i<multiArray[i].length; j++)
    result += multiArray[i][j];

Did you spot the error? It took me several attempts to get a working version of this code I could break. The problem is in the loop counter in the second loop, it should read as follows:

let result = "";
let multiArray = [[1,2,3], ["a", "b", "c"]];
for(let i=0; i<multiArray.length; i++)
  for(let j=0; j<multiArray[i].length; j++)
    result +=multiArray[i][j];

Obviously this could be somewhat mitigated through better variable naming but we would like to avoid the problem altogether.

Instead we can make use of an accumulator, a tool for combining multiple values from a collection into a single value. We've rather missed Westeros for a couple of patterns so let's get back to our mythical example land. Wars cost a great deal of money but fortunately there are a great number of peasants to pay taxes and finance the lords in their games for the throne.

Implementation

Our peasants are represented by a simple model which looks like the following:

let peasants = [
  {name: "Jory Cassel", taxesOwed: 11, bankBalance: 50},
  {name: "VardisEgen", taxesOwed: 15, bankBalance: 20}];

Over this set of peasants we have an accumulator which looks like the following:

TaxCollector.prototype.collect = function (items, value, projection) {
  if (items.length> 1)
    return projection(items[0]) + this.collect(items.slice(1), value, projection);
  return projection(items[0]);
};

This code takes a list of items, an accumulator value, and a function that projects the value to be integrated into the accumulation.

The projection function looks something like the following:

function (item) {
  return Math.min(item.moneyOwed, item.bankBalance);
}

In order to prime this function, we simply need to pass in an initial value for the accumulator along with the array and projection. The priming value will vary but more often than not it will be an identity; an empty string in the case of a string accumulator and a 0 or 1 in the case of mathematical ones.

Each pass through the accumulator shrinks the size of the array over which we are operating. All this is done without a single mutable variable.

The inner accumulation can really be any function you like: string appending, addition, or something more complicated. The accumulator is somewhat like the visitor pattern except that modifying values in the collection inside an accumulator is frowned upon. Remember that functional programming is side-effect-free.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset