CoffeeScript - gregorymorrison/euler1 GitHub Wiki

CoffeeScript, introduced in 2009, is a modernization of JavaScript, overhauled to add much of the pleasantry of languages such as Python, Ruby, and Haskell. The language actually gets converted to JavaScript for execution, so CoffeeScript is, in effect, just syntactic sugar on top of JS.

My first effort was this standard imperative version of Euler1. It took me maybe 10 minutes from knowing nothing about the language to write this. CS's function signatures borrow from Haskell. And its comments are simply strings that are defined but not used, as in Python:

'Euler1 in CoffeeScript'

euler1 = (size) ->
    retval = 0

    for i in [1..size-1]
        if i % 3 is 0 or i % 5 is 0
            retval += i

    return retval

alert "euler1 = " + euler1(1000)

You'll notice immediately that whitespace is significant here, if you're into that sort of thing.

Here's a recursive version. Like Ruby, there are no return statements - a function implicitly returns anything that is evaluated in its last line:

'Euler1 in CoffeeScript'

euler1 = (n) ->
    if n is 0
        0
    else if n % 3 is 0 or n % 5 is 0
        n + euler1(n-1)
    else
        euler1(n-1)

alert euler1(999)

But if you write functions like the above, your code is bad and so are you. You should be using tail recursion with an accumulator. One of the main points here is that the function euler() is deterministic - it will always return the same output for a given input. This is accomplished in part by the absence of any variable manipulation - there are instead only functions which return values. The other main point is that this recursion uses tail call optimization - it's written in such a way that an intelligent compiler can optimize its stack usage to be O(n) instead of O(n!). In English, this means that your program will probably not crash even for hugely recursive calls.

'Euler1 in CoffeeScript'

euler1 = (n, acc=0) ->
    if n is 0
        acc
    else if n%3 is 0 or n%5 is 0
        euler1(n-1, acc+n)
    else
        euler1(n-1, acc)

alert "euler1 = " + euler1(999)

CoffeeScript supports Functional programming. Here is an example. Note that there is no local state or variable manipulation. All that we've defined in the runtime is the function itself. It's written all in one line for maximum obnoxiousness:

'Euler1 in CoffeeScript'

euler1 = (size) -> (i for i in [1..size-1] when i%3 is 0 or i%5 is 0).reduce (x,y) -> x+y

alert euler1(1000)

Well, yuck. Let's break that down into the canonical operations, Map, Filter, and Reduce. Map and Filter are implemented as List Comprehensions, and Reduce is a built-in function. Map here is a tautology and thus serves no purpose, though it's included anyway for illustration. Also notice that collections are simply passed in as an object.

'Euler1 in CoffeeScript'

myMap    = (size) -> i for i in [1..size]
myFilter = (ints) -> i for i in ints when i % 3 is 0 or i % 5 is 0
myReduce = (ints) -> ints.reduce (x,y) -> x+y

alert myReduce(myFilter(myMap(999)))

If you're unfamiliar with reduce here, it's a curious beast. Notice that it is being performed on the collection ints, which uses it in the lambda (x,y) -> x+y.  What are x and y? The lambda expects two parameters. Each element of ints is iterated through and passed in as x. For the first call, reduce passes nothing in as y, so the runtime initializes y with a value appropriate for the lambda. Since our lambda function is simply addition, y is initialized as 0. For all subsequent calls, reduce passes the return value of the last call in as y. This is an example of partial function application. In this way, reduce is able to take a collection and convert it into a single object.

So, you're already a JavaScript whiz - why do you need to learn yet another language, especially one that does nothing new? Following is the JS that was generated by the preceding CS example. Because 7 lines of CS equals 29 lines of JS - that's why:

'Euler1 in CoffeeScript';
var myFilter, myMap, myReduce;

myMap = function(size) {
  var i, _results;
  _results = [];
  for (i = 1; 1 <= size ? i <= size : i >= size; 1 <= size ? i++ : i--) {
    _results.push(i);
  }
  return _results;
};

myFilter = function(ints) {
  var i, _i, _len, _results;
  _results = [];
  for (_i = 0, _len = ints.length; _i < _len; _i++) {
    i = ints[_i];
    if (i % 3 === 0 || i % 5 === 0) _results.push(i);
  }
  return _results;
};

myReduce = function(ints) {
  return ints.reduce(function(x, y) {
    return x + y;
  });
};

alert(myReduce(myFilter(myMap(999))));

CoffeeScript's home page includes an interactive code editor, complete with samples, which lets you immediately play with the language. Nothing to install - brilliant! To run, just put your code in the online editor and click the big Run button:

233168