Ringo Starr and the Fibonacci Sequence: Multiple Assignments in Ruby

How Ruby code can be refactored using multiple assignments.

How Ruby code can be refactored using multiple assignments.

Ruby allows more than one variable to be assigned at a time. This post will explore some of the situations where this can be helpful and also warn you of a potential pitfall.

One use of multiple assignment is to simply do in one line what would otherwise be done in two or more. Instead of:

drummer   = "Ringo Starr"
bassist   = "Cliff Burton"
guitarist = "Jimi Hendrix"

you can write:

drummer, bassist, guitarist = "Ringo Starr", "Cliff Burton", "Jimi Hendrix"

You can also swap values without having to use an intermediate variable, so if you think Jimi Hendrix should play the drums while Ringo Starr plays the guitar, instead of:

intermediate = drummer
drummer = guitarist
guitarist = intermediate

you can write:

drummer, guitarist = guitarist, drummer

However, if you want Mick Jagger to both sing and play the bongos, you may run into the potential pitfall I warned you about above. At first you might think that the following would set both variables to the same value:

singer, bongos = "Mick Jagger"

Instead this will set the singer variable to "Mick Jagger", and the bongos variable to nil.

As a demonstration of how multiple assignment can be used when refactoring code, let’s take a look at the following function I found on Stack Overflow (original post here):

def fib (n)
  return 0 if n == 0

  x = 0
  y = 1

  (1..n).each do
    z = (x + y)
    x = y
    y = z
  end

  return y
end

This function calculates the nth value of the Fibonacci sequence, where each term is the sum of the two preceding terms. The first thing I notice is that the two lines where x and y are initialised can be combined into a single line:

def fib (n)
  return 0 if n == 0

  x, y = 0, 1

  (1..n).each do
    z = (x + y)
    x = y
    y = z
  end

  return y
end

This saves a line of code, but also notice we’re using an intermediate variable, z, when setting x and y in the each loop. We can refactor this to:

def fib (n)
  return 0 if n == 0

  x, y = 0, 1

  (1..n).each do
    x, y = y, (x + y)
  end

  return y
end

which saves another two lines of code and eliminates the intermediate variable. Getting rid of the intermediate variable also improves the efficiency of the code, as there’s now one less variable to be held in memory and for the garbage collector to deal with when we’re finished.

Now that the each loop contains only one statement, I’d be tempted to refactor it to:

def fib (n)
  return 0 if n == 0

  x, y = 0, 1

  (1..n).each { x, y = y, (x + y) }

  return y
end

for a total saving of five lines of code and a variable, over our original function.

Please let me know the uses you’ve put multiple assignments to - why not leave a comment below?


Related Posts

Let's Work Together

We would love to hear from you so let's get in touch!

CONTACT US TODAY!