metacall()()()™ + project Euler

metacall, at last!

Contents

I shall use a simple form of metacall to create a better solution that will be resilient to change, but in the same time non-intrusive.

Which means: Not getting in the way of the solution of the original problem. This is very important quality for every architectural pattern to be adopted. Let us at last, dive into metacall usage.

//
// improvement of the previous solution
// sum_n is implemented by following the metacall pattern
// start and keep the result of [0..1000] 
  euler1solution(1000)
// keep the result for divisor 3
       ('+',3)
// add to previous result using divisor 5
       ('+',5)
// final adjustment, remove from what is
// stored, for divisor 3*5
       ('-',3*5)
// use the result and show it
       ( function ( rez ) {
            alert(rez);
        });
//

This is now more usable. And rather more elegant too. One can ‘stream’ in any number of call’s to compute sum_n(), and then show the accumulated result by using a callback. And of course, implementation might change in any direction without affecting the usage. We could, for example, implement sending input to server, in next version. Above is trivial to implement.

function print (s) {
    return setTimeout(function () { 
        document.body.innerHTML += "<li>"+s+"</li>" ; }, 1
              ) ;
};
// building block of a solution, we keep it here for simplicity
  function sum_n ( max, div ) {
  var rez = 0 ;
   while ( max-- > div) if ( max % div== 0 )  rez += max ;
    return rez ;
  }
// (c) 2010 by DBJ.ORG. MIT licence
// CallStream pattern/idiom
function euler1solution ( max_ ) {
    var rezult = 0, limit = max_ ;
    euler1solution = function (){
          var command = arguments[0], divizor = arguments[1],
              divmap = [] ;
          
        switch ( typeof command ) {
            case 'string' :
                
                divmap[""+divizor] = divizor ;
                
                   if ('+' == command ) {
                       rezult += sum_n ( limit,divizor ) ;
                   } else if ('-' == command ) {
                       rezult -= sum_n ( limit, divizor ) ;
                   } else {
                       rezult = "Illegal command" ;
                   }
                break;
            case 'function':
                  try {
                      rezult = command(rezult,limit, divmap );
                  } catch (x) {
                      rezult = "Exception: " + x + ", from: " + command ;
                  }
                break;
			default :
                rezult = 'illegal argument type: ' + typeof(command) ;
                break;
        }
             return euler1solution;
    }
   return euler1solution;
}
// usage
euler1solution(1000)('+',3)('+',5)('-',3*5)
( function (rez,lim,divs) {
return print(
    "For limit:" + lim + ", and divizors: {" + divs + "}, Result is:" + rez
     ) ;
}) ;

But why stop here?

We are basing our design on a metacall idiom and our solution is thus ultimately changeable. And somehow strangely monadic in nature too.

There is a real possibility, not just a hint, of being able to easily compose new solution form atomic particles of logic. Above initial “sketch”, I could extend to have something like “tool-bench”, for the whole expandable family of similar solutions to “Euler-ian” problems.

// non-trivial call-stream based solution
// compute solution to problem no. 1
euler_bench("id01")
        ('+',sum_n, 1000, 3)
        ('+',sum_n, 1000, 5 )
        ('-',sum_n,1000,3*5)
        ( show_current, 'sum_n' )
// in the same metacall now compute 
// the solution to the problem no. 2
     ( solution_2, 4*1e6 )
     (solution_2, 2*1e6)
     (show_current, 'solution_2') ;
//
// each solution has unique ID
function show_current ( rez_id ) { 
/* 
   compute and show the stored rezults for each 
   solution before this is called 
*/ 
}
//

Here we have an euler_bench(), metacall based, mediator pattern, which saves results of solutions to two projecteuler.net problems, and shows the current result when required.

An argument to euler_bench(), is an id of the result data-set for managing the state of the solution so that we can instantiate and use one or more euler_bench( id ), in the same time in the same scope.

Non-trivial Territory

For this last “release” to work, we would have to implement “behind” few non-trivial state management mechanisms. For example each solution function, like .e.g. sum_n(), would have to emit the result to separate “result stack”. One result-stack for one euler-ian solution. otherwise final expressions will not work, since they will use results from disparate solutions.

And, as above, all this named result stacks will have to be clustered under one id, like “id01” in the example above. For this to work I will have the each solution function (aka: fundamental indivisible particle of logic) return an array of values so that calling mechanism can record its results properly associated with the worker who made it and its input parameters.

// MIT (c) 2010 by DBJ.ORG
// Solution to projecteuler.net problem 1
  function sum_n ( max, div ) {
  var rez = 0, m = max ;
   while ( max-- > div) if ( max % div== 0 )  rez += max ;
// Structure returned is used by 
// final expression callback 
// to compute the results
    return [ 
     "sum_n" ,// solution UID
      rez ,   // what is the final result
      m,div   // variable number of input parameters
      ] ;
  }
// solution to the euler problem no: 2
// Find sum of even valued terms 
// in the fibonacci_sequence up to X
function solution_2 ( X )
{
var rez ;
// solution here
// Structure returned is used by 
// final expression callback 
// to compute the results
    return 
    [ "solution_2" , // solution UID
       rez , // what is the final result
       X    // variable number of input parameters
    ] ;
}
// here we can have any number of 
// solution functions implemented as the two above
// results of each solution used 
// in a call-stream are stored in 
// a separate named data-set
// by using solution UID

Also. Callbacks we send, can be used to control the behavior of the solution “behind”. For example, we can have a callback that will erase the current result data-set, callback that will send the current result data-set to server, another one that can record the time passed, etc. But, all of these will not change the callers experience at all. If properly micro-designed metacall based interfacing mechanism stays intact.

Onto the next page more usage and hopefully some  helpful thinking.