This series explores CoffeeScript, a new programming language that is built on top of JavaScript and offers a clean syntax. CoffeeScript compiles into efficient JavaScript. In addition to running the JavaScript in a web browser, you can use it with technologies such as Node.js for server applications. In Part 1, you learned to set up the CoffeeScript compiler and use it to create code that's ready to run in a browser or server.
In this article, explore the CoffeeScript language by solving several programming problems from Project Euler (see Resources for more information about Project Euler). The examples walk you through the use of functions, ranges, comprehensions, block statements, arrays, and some object-oriented aspects of CoffeeScript.
Download the source code used in this article.
Functions, ranges, and comprehensions
The first Project Euler problem you'll tackle is Problem 6 (see Resources), which asks you to calculate the sum of the squares of the first natural numbers, the square of the sum of those same numbers, and then give the difference. For this problem you will use the CoffeeScript Read-Evaluate-Print-Loop (REPL). Listing 1 shows the code and corresponding output from the REPL session.
Listing 1. Problem 6 from the REPL
coffee> square = (x) -> x*x [Function] coffee> sum = (nums) -> nums.reduce (a,b) -> a+b [Function] coffee> diff = (nums) -> (square sum nums) - (sum nums.map square) [Function] coffee> diff [1..100] 25164150 |
Breakdown:
- In the REPL, define a function. (As mentioned in Part 1, CoffeeScript celebrates the functional programming
nature of JavaScript, discarding much of JavaScript's C-like syntax
that makes it cumbersome to do elegant functional programming.)
The example in Listing 1 defines a function called
squareand states that it takes a single parameter and returns the product of multiplying that parameter by itself (squaring it). The REPL then tells you that you've defined a function. - Define another function called
sumthat takes a single parameter: an array.Sumthen invokes thereducemethod on the array.The
reducemethod is not added by CoffeeScript but is part of JavaScript proper (added in JavaScript 1.8). Thereducemethod is similar to the reduce function in Python or the fold function in Haskell or Scala. It takes a function and goes from left to right on the array, applying the function to the previous value returned by the fold and the next value in the array. CoffeeScript's compact function syntax makesreduceeasy to use. In this case, the function passed to reduce is specified by(a,b) -> a + b. This is a function that takes two values and adds them together, thus causing all of the elements of the array to be added together. - Create a function called
diff, which takes an array of numbers, calculates two sub-expressions, then subtracts them. The first passes the array to the sum function and then takes the result and passes it to thesquarefunction.CoffeeScript allows you to omit parentheses in many cases so there can be no confusion. For example,
square sum numsis equivalent tosquare(sum(nums)). The second sub-expression invokes themapmethod of the array, which is another JavaScript 1.8 method that takes another function as its input. It then applies that function to each member of the array, creating a new array from the results. The example in Listing 1 uses the square function as the input parameter to map, giving you an array whose elements are the squares of the input array. Then, you simply pass this to the sum function to get the sum of the squares. - Pass the appropriate array of numbers into the
difffunction to solve Problem 6, using a range[1..100].This range is equivalent to an array of all of the numbers from 1 to 100, inclusive. If you had wanted it to be exclusive you would have written
[1...100], using three periods instead of two. Passing this todiffgives you the solution to Problem 6.
Let's back up a little and look at Project Euler Problem 1 (see Resources), which asks you to sum up all of the integers less than 1000 that are divisible by either 3 or 5. You might think this would be the easiest of the Project Euler problems. You could easily solve it just using functions and ranges, as in Problem 6. With the CoffeeScript comprehensions feature, however, you can create the elegant solution in Listing 2.
Listing 2. Solving Problem 1 using comprehensions
coffee> (n for n in [1..999] when n % 3 == 0 or n % 5 == 0).reduce (x,y) -> x+y 233168 |
It's always nice to solve a problem with a single line of code, and
CoffeeScript's concise syntax lends itself to one-liners. The solution in
Listing 2 creates a list of all of the integers that are
multiples of 3 or 5 by using a comprehension. It is generated by starting
with the range [1..999] but using only the
values that are divisible by 3 or 5. Another
reduce is then used to sum the values up. The
REPL evaluates this one line of code and prints the solution to the
problem.
The next section tackles slightly more difficult problems and explores CoffeeScript further.
Block statements, arrays, and files
Project Euler Problem 4 (see Resources) asks you to find the largest palindrome that is the product of two three-digit numbers. There are many ways to solve this problem; Listing 3 shows one way.
Listing 3. Testing for palindromes
isPal = (n) ->
digits = []
while n > 0
digits.push(n % 10)
n = Math.floor(n / 10)
len = digits.length
comps = [0..len/2].map (i) -> digits[i] == digits[len-i-1]
comps.reduce (a,b)-> a && b
vals = []
vals.push(x*y) for x in [100..999] for y in [100..999]
pals = vals.filter (n) -> isPal(n)
biggest = pals.reduce (a,b) -> Math.max(a,b)
console.log biggest
|
- Define a function called
isPal, which will test if a number is a palindrome. This function is a little more complicated than those defined so far. It has seven lines of code in its body.You probably noticed that CoffeeScript does not use curly brackets ({ }) or any other explicit mechanism to denote the beginning and ending of the function. It uses whitespace (indentation), much like Python, and starts with the same notation to denote a function: the parameter list followed by the greater than arrow (->). Then you create an empty array for the digits of the numbers and begin a while loop. This loop is similar to the while loop from JavaScript (or C, Java, and so on). The predicate (
n > 0) does not require parentheses around it. The body of the loop is indented further to indicate that it is part of the loop. Inside the loop you chop off the last digit of the number, push it to the front of the array, and then divide the number by 10, discarding the remainder. This results in an array of the digits of the original number. You could also replace the loop simply withdigits = new String n, convertingnto a string. The rest of the code works as-is. - After you have the array of digits, create an array that is half the
length of this array. Use the
mapfunction to turn each element of this array into a Boolean value corresponding to whether the digits equidistant from the beginning and ending of the array are equal.If all of these come out true, then you have a palindrome. To test the work, simply use another reduce function, this time 'and-ing' together the Booleans.
- Now that you have the
isPalfunction defined, use it to test for palindromes. Test all of the numbers that are the product of two three-digit numbers.- Create two comprehensions, each ranging from the smallest three-digit number (100) to the largest (999).
- For each of these, take the product and put it into an array.
- Use another
reducefunction to find the largest element in the array. Finally, this element is printed out using console.log. - Save this to a file.
Listing 4 shows how to execute and time the solution.
Listing 4. Executing the solution for Problem 4
$ time coffee p4.coffee 906609 real 0m2.132s user 0m2.106s sys 0m0.022s |
The script in Listing 4 takes over two seconds to execute on a fast computer, largely due to the compound comprehension that generates 899*899 = 808,201 values to test (many of them duplicates). As an additional exercise, you can optimize the Listing 3 code. (Hint: Converting the number to a string is actually much faster.)
Project Euler Problem 22 (see Resources) requires you to perform a complex calculation on a list of strings. You will read the list from a file, parse the contents into a list, sort it and convert each string to a number, and then sum the products of each number and its position in the list. Problem 22 lets you see how files work in CoffeeScript. There's also some string manipulation and more array magic. Listing 5 shows the solution.
Listing 5. Working with files in CoffeeScript
path = "/path/on/your/computer/names.txt"
fs = require "fs"
contents = fs.readFileSync path, "utf8"
strs = contents.split(",")
names = strs.map (str) -> str[1 .. str.length - 2]
names = names.sort()
vals = names.map (name) -> name.split("")
vals = vals.map (list) ->
list.map (ch) -> 1 + ch.charCodeAt(0) - 'A'.charCodeAt(0)
vals = vals.map (list) ->
list.reduce (a,b) -> a+b
vals = ((i+1)*vals[i] for i in [0...vals.length])
total = vals.reduce (a,b) -> a+b
console.log total
|
- Save the file. There's a link in the problem description, or you can
use the source code included with this
article. Change the value of the path variable to the absolute path to
the file on your computer.
CoffeeScript does not include any special libraries for working with files. Instead, it leverages node.js and its
fsmodule. Load thefsmodule using therequirefunction. - Read the contents of the file using
fs.readFileSync. The file looks like "MARY", "PATRICIA", and so on. It starts out as a single string, so use thesplitmethod to break it apart at the commas.Each string still has double quotes (") at the beginning and end of the string. To get rid of them, use the
mapfunction and replace each string with a slice. Ifstris a string, thenstr[1 .. str.length -2]is a substring starting with the second character and ending with the next to last character. The code will precisely remove the first and last characters—those pesky double quotes. Slicing strings can be very convenient. - After you have a list of strings without double quotes, you're ready
to sort. Use array's
sortmethod. You need to convert the strings to a number where each character becomes its place in the alphabet (A -> 1, B -> 2, C -> 3, and so on).- Turn each string into an array of characters using the
splitmethod again. - Map each character to a numeric value using the
charCodeAtmethod. - Add up these numeric values using another
reduceoperation.
- Turn each string into an array of characters using the
- Multiply each number by its position in the list and sum these
together using another comprehension. Create a new array where each
element is generated by multiplying an element from the previous array
along by its position. Sum up the elements of this array by using one
more
reduceoperation, and print out the total.
Once again, you can save the work to a file, and then execute and time it, as shown in Listing 6:
Listing 6. Timing the execution of Problem 22
$ time coffee p22.coffee 871198282 real 0m0.133s user 0m0.115s sys 0m0.013s |
It takes less than 0.2 seconds to execute the solution to Problem 22. It took almost as many lines of English to describe what was to be calculated as it did lines of code to perform the calculation. This is a great example of CoffeeScript's succinct syntax. You can imagine the example taking many more lines of code in other programming languages.
In this section, you have solved more difficult problems using some of the important features of CoffeeScript. The next section examines a crucial feature in CoffeeScript: object-oriented programming.
As mentioned in Part 1, a major pain point of JavaScript is its "unusual" style of object-oriented programming (OOP). It is unusual simply because class-based OOP is so overwhelmingly common. CoffeeScript makes class-based OOP.
Your next challenge is to use CoffeeScript's OOP to solve Project Euler Problem 35 (see Resources). In Problem 35, you're told that a circular prime is a special kind of prime number with the property that you can perform any rotation of its digits and still have a prime number. The code in Listing 7 uses OOP to calculate the number of circular primes less than one million.
Listing 7. Counting circular primes
class PrimeSieve
constructor: (@max) ->
@nums = [2..@max]
for p in @nums
d = 2*p
while p != 0 and d <= @max
@nums[d-2] = 0
d += p
isPrime: (n) -> @nums[n-2] != 0
thePrimes: -> @nums.filter (n) -> n != 0
class CircularPrimeGenerator extends PrimeSieve
genPerms = (num) ->
s = new String num
x = (for i in [0 ... s.length]
s[i+1 ... s.length].concat s[0..i])
x.map (a) -> parseInt(a)
isCircularPrime : (n) ->
perms = genPerms(n)
len = perms.length
primePerms = perms.filter (p) => @isPrime(p)
len == primePerms.length
theCircularPrimes: ->
(p for p in @thePrimes() when @isCircularPrime(p))
max = process.argv[2]
generator = new CircularPrimeGenerator max
console.log "Number of circular primes less than #{max} is
#{generator.theCircularPrimes().length}"
|
- Create a class called
PrimeSieve, which implements the classic Sieve of Erasthones algorithm for calculating all of the primes less than a particular value.The
@sign denotes a property of a class and is also shorthand for'this.'. Therefore,@nums = [2..@max]is equivalent tothis.nums = [2 .. this.max].Class methods are indicated by their name followed by a semicolon and the function definition. The first method, called
constructor, is a constructor for the class. For example, newPrimeSieve(100)would cause the constructor to be invoked with 100 passed in asmaxand assigned tothis.max. Our constructor builds the sieve and stores the primes in the@numsmember variable. It then declares two more methods:isPrimeandthePrimes. ThethePrimesmethod uses an array filter to remove the composite numbers from@nums. - Declare a subclass of
PrimeSieve, calledCircularPrimeGenerator. CoffeeScript usesclass ... extendssyntax, like many popular OOP languages. This class will inherit the constructor, member variables, and methods ofPrimeSieve. It has the:-
genPermsmethod for generating all of the permutations of a given number by rotating the digits of the number. -
isCircularPrimemethod that generates all of the permutations of a given number and removes any numbers in the list of permutation that are not a prime. If the filtered list has all of the same elements as the unfiltered list, then the number must be a circular prime. theCircularPrimesmethod that generates a list of all of the circular primes using a comprehension.
@thePrimesmethod defined in the superclass and then simply filter out the primes that are not circular.After you have two classes defined, you're ready to use them to solve the problem.
-
-
Listing 7 was written to
take a command-line argument that is the max value to be used for
calculating the primes and the circular primes. All command-line
arguments can be accessed using
process.argv. The first two values in this array are the command and the script, so it isprocess.argv[2]that contains the first parameter to be used by the script.Create an instance of
CircularPrimeGeneratorusing the max value passed in to the script.Print out the number of circular primes that were found using console.log.
In this example, you used another convenient feature of CoffeeScript: string-interpolation to create the string that is passed to console.log.
In this article, you explored many of the features of CoffeeScript. Its concise syntax and functional programming features let you elegantly implement many common algorithms. At the same time, CoffeeScript also provides simplified object-oriented programming. For whatever kind of problem you need to solve, you'll find that CoffeeScript's syntax simplifies your task.
Stay tuned for the next part of this series, which will get more pragmatic and use CoffeeScript for client-side scripting in a web application.
| Description | Name | Size | Download method |
|---|---|---|---|
| Article source code | cs2.zip | 19KB | HTTP |
Information about download methods
Learn
- "Your first cup of CoffeeScript" series (developerWorks): Explore
the popular CoffeeScript programming language, which is built on top of
JavaScript.
- "Your first cup of CoffeeScript, Part 1: Getting started" (developerWorks, December 2011): Learn about the perks of CoffeeScript for developers, set up the CoffeeScript compiler, and use it to create code that is ready to run in a browser or server.
-
Project Euler: Explore challenging
mathematical and computer programming problems that will require more than
just mathematical insights to solve.
- Project Euler Problem 1: Add all the natural numbers below one thousand that are multiples of 3 or 5.
- Project Euler Problem 4: Find the largest palindrome made from the product of two three-digit numbers.
- Project Euler Problem 6: What is the difference between the sum of the squares and the square of the sums?
- Project Euler Problem 22: What is the total of all the name scores in the file of first names?
- "Just what is Node.js?" (developerWorks, May 2011): Understand how
Node is a server-side JavaScript interpreter that changes the notion of
how a server should work.
-
Node.js: Use this launching point to
learn more about the application.
- "Use Node.js as a full cloud environment development stack"
(developerWorks, April 2011): See how Node.js can be combined with cloud
technologies.
- "High-performance Web development with Google Web Toolkit and
Eclipse" (developerWorks, October 2009): Compiling into JavaScript
is not a new idea. So if you are a fan of the Java programming language,
you should read this article.
- "All aboard! An introduction to Rails 3" (developerWorks, March
2010): CoffeeScript is now part of Ruby on Rails. Check out the other new
features in Rails in this article.
-
CoffeeScript project
on Github: Start here to learn about CoffeeScript.
- "Create Ajax applications for the mobile Web" (developerWorks,
March 2010): Get more information about using Ajax in mobile web
applications.
-
developerWorks Web
development zone: Find articles covering various web-based
solutions. See the Web
development technical library for a wide range of technical
articles and tips, tutorials, standards, and IBM Redbooks.
-
developerWorks
podcasts: Listen to interesting interviews and discussions for
software developers.
-
developerWorks technical events and webcasts: Stay current with
developerWorks technical events and webcasts.
- developerWorks on
Twitter: Join today to follow developerWorks tweets.
- developerWorks
on-demand demos: Watch demos ranging from product installation and
setup for beginners to advanced functionality for experienced
developers.
Get products and technologies
-
Node.js: Download Node.js
and start easily building scalable network programs.
- IBM product
evaluation versions: Download or explore
the online trials in the IBM SOA Sandbox and get your hands on
application development tools and middleware products from DB2, Lotus,
Rational, Tivoli, and WebSphere.
Discuss
- The developerWorks
community: Connect with other developerWorks users while exploring
the developer-driven blogs, forums, groups, and wikis.
- Find other developerWorks members interested in web development.

Michael Galpin is a software engineer at Google. He is a co-author of the book Android in iPractice and a frequent contributor to developerWorks. For a sneak peek at what he's up to next, check out his blog or follow him on Twitter @michaelg or Google+ Michael Galpin.



