First and Rest in Ruby

2010 January 31
by avdi

A common pattern when doing functional-style programming is to split a list into first and rest components, otherwise known as head and tail (or car and cdr in Lisp). first is the first item in the list, or nil if the list is empty. rest is a zero or more-length list containing the remaining elements in the original list.

For instance, here’s a recursive reimplementation of Ruby’s Array#join method which splits the joined list into first and rest:

def rec_join(list, delim)
  case list.size
  when 0 then ""
  when 1 then list.first.to_s
  else list.first.to_s + delim + rec_join(list[1..-1], delim)
  end
end

rec_join([:foo, :bar, :baz], ', ') # => "foo, bar, baz"

In this example we used list.first to get the first element, and list[1..-1] to get the rest. But is this a robust way to split an array into head and tail components? Let’s see:

zero = []
one  = [0]
many = [0,1,2,3,4]

first, rest = zero.first, zero[1..-1]
first                           # => nil
rest                            # => nil
first, rest = one.first, one[1..-1]
first                           # => 0
rest                            # => []
first, rest = many.first, many[1..-1]
first                           # => 0
rest                            # => [1, 2, 3, 4]

Hmmm. We said that rest should always be a list with zero or most elements; but when given an empty list to start with, this technique gives us nil as the tail of the list. It’s also a bit ugly. Let’s see if we can do better.

first, rest = *zero
first                           # => nil
rest                            # => nil
first, rest = *one    
first                           # => 0
rest                            # => nil
first, rest = *many        
first                           # => 0
rest                            # => 1

Now we’re using the splat operator to break the source list into it’s parts. The code is much more concise, but the results are very wrong. In this version rest is never a list!

However, a slight refinement and we have exactly what we want:

first, *rest = *zero
first                           # => nil
rest                            # => []
first, *rest = *one    
first                           # => 0
rest                            # => []
first, *rest = *many        
first                           # => 0
rest                            # => [1, 2, 3, 4]

Here we’ve added a splat operator to the left side of the variable assignment as well, telling Ruby to collect all remaining values into an Array assigned to the rest variable. This version works exactly as we intended, and is still concise. I first saw this technique used in the DataMapper codebase, and it’s the most elegant way I’ve come across to break a Ruby Enumerable object into first and rest components.

Bookmark and Share
Creative Commons License
This work, unless otherwise expressly stated, is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License.
blog comments powered by Disqus