Today’s Ruby Tuesday takes a look at Array Decomposition.
Ruby gives you some ability to destructure, or decompose, an Array
into its component pieces.
Say we have an Array
which represents a point in two-dimensional Cartesian space, we can decompose that array into its x
and y
coordinates, by doing an assignment of x
and y
to the point represented by the given Array
, if we wrap them in parenthesis.
(x, y) = [1, -1] # => [1, -1] x # => 1 y # => -1
If we want to decompose only part of the Array
, and save off anything at the end, we can use a *
before the last variable in the assignment.
(_, second, *rest) = [:a, :b, :c, :d] # => [:a, :b, :c, :d]
The capturing even works, if there are less items in the array than there are variables to decompose into.
(_, second, *rest) = [:a] # => [:a] second # => nil rest # => []
We can also use Array Decomposition in method arguments to decompose a passed in Array
to individual variable for specific elements.
def cartesian_point((x, y)) puts "x: #{x}, y: #{y}" end # => :cartesian_point cartesian_point([1, -1]) # x: 1, y: -1 # => nil
Above I mentioned that we can capture the “rest” of the Array
we aren’t wanting to decompose at this point by using a *
(called the “splat” operator).
(head, *rest) = [1, 2, 3, 4, 5] # => [1, 2, 3, 4, 5] head # => 1 rest # => [2, 3, 4, 5]
So let’s see what we get when we use the splat to capture the rest of an Array
that is smaller than what is at the end.
(first, *tail) = [1] # => [1] first # => 1 tail # => []
We get an empty list.
Let’s see what we get on the decomposition for an empty list, into the items, and a capturing “rest” variable.
(h, *t) = [] # => [] h # => nil t # => []
We get a nil
as part of the binding, and we still get an empty array the “rest” of the items.
This means we can use destructing to handle recursing a list, and have a guard clause that checks if we received and empty Array
by checking if the head of the Array
is nil
, and then just recurse by passing in the tail as the argument when recursing.
def recurse_list((head, *tail)) if (head != nil) puts head recurse_list(tail) else puts "all done" end end # => :recurse_list recurse_list([1, 2, 3, 4, 5, 6, 7]) # 1 # 2 # 3 # 4 # 5 # 6 # 7 # all done # => nil
–Proctor