JavaScript - gregorymorrison/euler1 GitHub Wiki

JavaScript, the Little Language That Could, is no longer just for the browser, as there are general-purpose runtimes available for it. I chose to try out Rhino, a version that runs on the JVM. Notice here that my script uses the JVM's generic IO functionality to perform functions that would normally be outside of the scope of a simple DOM-scripting tool. It took me almost no time to write this Imperative version of Euler1 because the code is lifted almost verbatim from C and Java:

// Euler1 in JavaScript

importPackage(java.io);
importPackage(java.lang);

function euler1(x) {
    var i, result;
    result = 0;

    for (i=0; i<x; i++) {
        if (i%3==0 || i%5==0) {
            result += i;
        }
    }
    return result;
}

System.out.println (euler1 (1000));

JavaScript, introduced in 1995, is a Prototype language, a style of Object-Oriented Programming in which new objects are not instantiated from classes. Instead, they are cloned and modified copies of existing instances, typically of a type Object. At its heart, an object in JavaScript is nothing more than a key-value dictionary, which makes them trivial to modify at runtime. Let's rewrite the previous example using an object:

// Euler1 in JavaScript

importPackage(java.io);
importPackage(java.lang);

// new object instance
var euler1 = {};

// dynamically add new properties and behaviors to object
euler1.range = 1000;
euler1.solver = function(x){
    var i, result;
    result = 0;

    for (i=0; i<x; i++) {
        if (i%3==0 || i%5==0) {
            result += i;
        }
    }
    return result;
}
euler1.result = euler1.solver(euler1.range);

System.out.println(euler1.result);

JavaScript also lets you write in a Functional style. Functions are first-class members, and you can write your code in Map/Filter/Reduce/Lambda, etc, but it's a bit like putting lipstick on a pig. Let's try anyway. In the following example, I define Map/Filter/Reduce and then apply them to an array of ints. There is now no mutable state - only declarations and function calls. State is managed through the careful use of recursion with accumulators and tail-call optimization:

// Euler1 in JavaScript

importPackage(java.io);
importPackage(java.lang);

// Generic recursive Map, Filter, Reduce using Tail Call Optimization
// Accumulator parameter is optional and only used by recursive calls

// Map - transform each item to something else
var map = function(ls, f, acc) {
    if (typeof acc === "undefined") acc = [];
    if (ls.length == 0) {
        return acc;
    } else {
        return map(ls.slice(1,ls.length), f, [].concat(f(ls[0]), acc));
    }
};

// Filter - remove selected items
var filter = function(ls, f, acc) {
    if (typeof acc === "undefined") acc = [];
    if (ls.length == 0) {
        return acc;
    } else if (f(ls[0]) == true) {
        return filter(ls.slice(1,ls.length), f, [].concat(ls[0], acc));
    } else {
        return filter(ls.slice(1,ls.length), f, acc);
    }
};

// Reduce - calculate a value based on all items
var reduce = function(ls, f, acc) {
    if (ls.length == 0) {
        return acc;
    } else {
        return reduce(ls.slice(1,ls.length), f, f(ls[0], acc));
    }
};

// generate a list of ints
var range = function(i, acc) {
    if (typeof acc === "undefined") acc = [];
    if (i == 0) {
        return acc;
    } else {
        return range(i-1, [].concat(acc, [i]));
    }
};

// Now let's define specific versions of mapping/filtering/reducing functions
var myMap = function(ints) {
    return map(ints, function(val) { return val; });
};
var myFilter = function(ints) {
    return filter(ints,
        function(val) { if (val%3==0 || val%5==0) return true;
                        else return false; });
};
var myReduce = function(ints) {
    return reduce(ints,
        function(x, acc) { if (typeof acc === "undefined") return x;
                           else return acc+x; });
};

// Now apply everything
System.out.println( myReduce(myFilter(myMap( range(999) ))) );

To execute your script, simply pass it as an argument to Rhino:

$ rhino euler1.js
233168.0