Accepting or Passing a Variable Number of Arguments

Problem

You want to write a method that can accept any number of arguments. Or maybe you want to pass the contents of an array as arguments into such a method, rather than passing in the array itself as a single argument.

Solution

To accept any number of arguments to your method, prefix the last argument name with an asterisk. When the method is called, all the "extra" arguments will be collected in a list and passed in as that argument:

def sum(*numbers) puts "I'm about to sum the array #{numbers.inspect}" numbers.inject(0) { |sum, x| sum += x } end sum(1, 2, 10) # I'm about to sum the array [1, 2, 10] # => 13 sum(2, -2, 2, -2, 2, -2, 2, -2, 2) # I'm about to sum the array [2, -2, 2, -2, 2, -2, 2, -2, 2] # => 2 sum # I'm about to sum the array [] # => 0

To pass an array of arguments into a method, use the asterisk signifier before the array you want to be turned into "extra" arguments:

to_sum = [] 1.upto(10) { |x| to_sum << x } sum(*to_sum) # I'm about to sum the array [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # => 55

Bad things happen if you forget the asterisk: your entire array is treated as a single "extra" argument:

sum(to_sum) # I'm about to sum the array [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]] # TypeError: Array can't be coerced into Fixnum

 

Discussion

Why make a method take a variable number of arguments, instead of just having it take a single array? It's basically for the convenience of the user. Consider the Kernel#printf method, which takes one fixed argument (a format string), and then a variable number of inputs to the format string:

printf('%s | %s', 'left', 'right') # left | right

It's very rare that the caller of printf already has her inputs lying around in an array. Fortunately, Ruby is happy to create the array on the user's behalf. If the caller does already have an array of inputs, it's easy to pass the contents of that array as "extra" arguments by sticking the asterisk onto the appropriate variable name:

inputs = ['left', 'right'] printf('%s | %s', *inputs) # left | right

As you can see, a method can take a fixed number of "normal" arguments and then a variable number of "extra" arguments. When defining such a method, just make sure that the last argument is the one you prefix with the asterisk:

def format_list(header, footer='', *data) puts header puts (line = '-' * header.size) puts data.join(" ") puts line puts footer end cozies = 21 gaskets = 10 format_list("Yesterday's productivity numbers:", 'Congratulations!', "#{cozies} slime mold cozies", "#{gaskets} Sierpinski gaskets") # Yesterday's productivity numbers: # -------------------------------- # 21 slime mold cozies # 10 Sierpinski gaskets # -------------------------------- # Congratulations!

You can use the asterisk trick to call methods that don't take a variable number of arguments. You just need to make sure that the array you're using has enough elements to provide values for all of the method's required arguments.

You'll find this especially useful for constructors that take many arguments. The following code initializes four Range objects from four arrays of constructor arguments:

ranges = [[1, 10], [1, 6, true], [25, 100, false], [6, 9]] ranges.collect { |l| Range.new(*l) } # => [1..10, 1…6, 25..100, 6..9]

Категории