CS 214: Programming Languages
Spring 2009

Home|Syllabus|Schedule
<<|>>

Functional Processing of an Array
Ruby, Iteration 4

First of all, Haskell Lists are equivalent to Ruby Arrays. They're also equivalent to Java Lists and C++ vectors. Well, Java and C++ don't have functional processing—yet!

But all those cool list-processing functions you saw in Haskell are also available in Ruby.

Set Up

  1. Create a new folder _languages/ruby/iteration03.
  2. Create a new test-case class MapSelectInjectTest in this folder.
  3. Create a new Rakefile in this folder.

Check out the Ruby templates.

Ruby also has an interactive interpreter:

unix-%  irb

Run irb in the folder for this iteration.

Map

Here's a map:

>> [1, 2, 3].map { |x| x + 1 }

Try this in irb.

There are two significant differences between Haskell and Ruby that are revealed by this expression.

You can assert this result like so:

assert_equal [2, 3, 4], [1, 2, 3].map { |n| n + 1 }

Write a test method test_map_plus_one that make three assertions for this application of map using three different arrays.

Select

Haskell calls it filter; Ruby calls it select.

>> (1..20).to_a.select { |n| n % 2 == 0 }

If you're not sure what any subexpression does, try it in irb! (Or GHCi if it's Haskell!)

In Ruby it's (1..20).to_a; in Haskell it's [1..20].

Write a new test method test_select_evens that makes three assertions on three different arrays.

Inject

Haskell calls it foldl or foldr; Ruby calls it inject. A puzzle: which is it?!!?

>> (1..20).to_a.inject(0) { |sum, n| sum + n }

Unlike foldl and foldr, you have to be explicit about the accumulator that's collecting the folded value (sum in this example). Note carefully that the lambda just has to return the next value; don't set sum (or whatever accumulator you use) to be the new value!

Write a new test method test_inject_sum that makes three assertions with three different arrays.

Things for You to Do

Add new test methods to MapSelectInjectTest as described here. Make at least three assertions in each test method demonstrating the requested technique. (One of the assertions can (and should!) be on the empty array.)

  1. test_sum_of_squares should sum up the squares of the elements using both map and inject.
  2. test_sum_of_squares_of_positives should sum up the squares of the positive elements using select, map, and inject.
  3. test_sum_of_evens_with_select_and_inject should add together only the even numbers using both select and inject.
  4. test_sum_of_evens_with_only_inject should add together only the even numbers using only inject.
  5. test_order_matters looks like this:
    result = [0, 1, -1, 2, -2, 3, -3].map { |x| x + 1 }.select { |x| x > 1 }
    assert_equal [2, 3, 4], result
    # TODO: select, then map
    assert_equal [2, 3, 4], result
    
    Replace the TODO comment with an assignment to result which applies select first.
  6. test_exponents should compute a chain of exponents using only reverse, **, and inject.
  7. test_reverse_with_inject should reverse the elements of the list using only inject and array concatenation ([a] + [b]).

Questions to consider for an exam: