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.
nicely done.
nicely done.
first, *rest = *many
do you need the splat on the right hand side?
seems to work without it. 🙂
I was reading the Twitter post about first and rest and I immediately thought about the splat operator as Ruby’s idiomatic head/tail version. Having learnt programming with a function language in college (OCaml), it seems very natural to do things this way in Ruby. The one bit I really crave for though is proper pattern matching.
This post is 3 years old so times may’ve changed, but I’d like to point out that you don’t need the splat on the right hand side.
first, *rest = [1, 2, 3]
is sufficient.