What does *args do?

I sometimes see funcs declared with def fun (blah, *args)

What does the *args do? Thanks!

Hi,

It’s called a “splat”. A variable at the end of the function call with
an
asterisk at the start [or elsewhere in Ruby 1.9?] “takes the slack”, as
such, and allows an indefinite number of extra arguments. For example:

def fun blah, *args
puts “blah: #{blah.inspect}, args: #{args.inspect}”
end
=> nil

fun 1
blah: 1, args: []
=> nil

fun 1, 2
blah: 1, args: [2]
=> nil

fun 1, 2, 3
blah: 1, args: [2, 3]
=> nil

Cheers,
Arlen.

Philip B. wrote:

I sometimes see funcs declared with def fun (blah, *args)

What does the *args do? Thanks!

Normally, if you define a method with two parameter variables, for
instance:

def show(x, y) #x, y are parameter variables
puts x
puts y
end

show(1, 2)

–output:–
1
2

…then you must call the method with two arguments(1 and 2 are the
arguments). Otherwise you get an error, for example:

show(1, 2, 3)

–output:–
‘show’: wrong number of arguments (3 for 2) (ArgumentError)

The * allows you to call a method and specify more arguments:

def show(x, *arr)
puts x

arr.each do |elmt|
puts elmt
end

end

show(1, 2, 3)
puts
show(1, 2, 3, 4)

–output:–
1
2
3

1
2
3
4

You still need to call show() with at least one argument so that ruby
can assign the first argument to the x parameter variable, but after
that you can specify any number of arguments in the method call. The
extra arguments are gathered up into an array and then assigned to the
variable name after the *, in this case that would be: arr.

If you have a method call like the following:

show(1, 2, 3, 4) #def show(x, *arr)

ruby assigns the argument 1 to the parameter variable x, and then ruby
creates an array to hold 2, 3, 4:

[2, 3, 4]

and then ruby assigns that array to the parameter variable arr:

arr = [2, 3, 4]

In effect the * in this definition:

def show(x, *arr)

says to ruby, "Please assign the first argument in the method call to x,
then gather up any additional arguments, stick them into an array, and
assign the array to the variable name to my right.

Hi there,

On Thu, Feb 21, 2008 at 12:04 PM, Stedwick [email protected]
wrote:

Got it, thanks. I love the term “splat” lol. Personally, I’m a big fan
of passing hashes {} as arguments. http://www.philipbrocoum.com/munch/

In that case, the syntax which Rails often uses in Ruby might be of
interest
to you:

def fn(a, b, opthash)
p a
p b
p opthash
end
=> nil
fn 1, 2, {:a => false}
1
2
{:a=>false}
=> nil
fn 1, 2, :a => false, :joker => 92
1
2
{:joker=>92, :a=>false}
=> nil

If you just append hash arguments to the end of a function call, it’ll
combine it into a hash and use it as the last parameter.

Arlen

Got it, thanks. I love the term “splat” lol. Personally, I’m a big fan
of passing hashes {} as arguments.

Stedwick
http://www.philipbrocoum.com/munch/

Hi,

On Fri, Feb 22, 2008 at 1:34 AM, Mark B. [email protected] wrote:

You can even do multiple assignment this way:
b, c, d, e = *a
b # => 1
c # => 2
d # => 3
e # => 4

It’s worth noting that this behaviour can be achieved without the splat
operator:

b, c, d, e = a # => [1, 2, 3, 4]
b # => 1
c # => 2
d # => 3
e # => 4

Arlen

What does the *args do? Thanks!

As well as the previously posted explanations of *args appearing in
a method argument list, it can also appear in a method body. In that
case, the array gets turned into a list.

That is, if a = [1, 2, 3, 4] then you can use *a to represent the list
of
values (here 1, 2, 3, 4). This is useful for adding the values to an
array:

[5, *a] # => [5, 1, 2, 3, 4]
[5, a] # => [5, [1, 2, 3, 4]]

In Ruby 1.8 this can only be at the end of the array, but in 1.9 it can
be anywhere:
[*a, *a] # => [1, 2, 3, 4, 1, 2, 3, 4] (in 1.9)

Also, if you want to pass the values as separate arguments to another
method:

puts *a # => same as: puts 1, 2, 3, 4
puts a # => same as: puts [1, 2, 3, 4]

You can even do multiple assignment this way:
b, c, d, e = *a
b # => 1
c # => 2
d # => 3
e # => 4

Note that this works with hashes, too. If a is a hash, then *a is the
equivalent
of:
b = a.to_a
*b
if you see what I mean.

Arlen C. wrote:

c # => 2
d # => 3
e # => 4

I don’t like this inconsistency in the language. Left is a list, right
should be a list. Making it mate with both list and array can introduce
tricky inconsistencies. I am in favor of dropping the latter and
always’d require splat for multiple assignment. I think that is too
dynamic for an untyped, dynamic language like Ruby.

e.g.

def not_broken_up a
my_a, my_nil1 = a
if my_a != a
puts “Gotcha!”
else
p my_a
end
end

not_broken_up 1 # -> 1
not_broken_up “hi” # -> “hi”
not_broken_up({1=>1}) # -> {1->1}
not_broken_up [1] # -> Gotcha!
not_broken_up [1,2] # -> Gotcha!
not_broken_up [[1]] # -> Gotcha!

I don’t like this inconsistency in the language. Left is a list, right should be a list. Making it mate with both list and array can introduce tricky inconsistencies. I am in favor of dropping the latter and always’d require splat for multiple assignment. I think that is too dynamic for an untyped, dynamic language like Ruby.

I think it makes perfect sense. If you are doing multiple assignments,
do this:

a = b = c = “hi”

But it’s pretty cool to be able to do this:
a, b, c = [“hi”, “hello”, “wassa”]

Mark,

thanks for your perfect explanation of the problem, you clearly make my
point. :slight_smile:

Mark B. wrote:

setting each value, and:

a, b, c = “hi”

To make inconsistencies even more stupid, we could specify that

a,b,c,d,e = “aeiou”

results in a==“a”, b==“e”, c==“i”, d==“o”, e==“u”.

or

a,b,c,d = 5

could be interpreted as a bit-wise split assignment such that a==0,
b==1, c==0, d==1. I see no point in allowing an array to be treated
specially, even more so as there is the one-character splat operator
available.

it would seem that the way arrays automagically expand would result in b
becoming 1, but actually b becomes the array [1, 2, 3] meaning that
automagic expansion is not happening. So even the inconsistency is
inconsistent!

Yeah, thanks for this perfect example of the matter!

The semantics of assignment should not change just because the class of
something on the rhs changes.

My words.

Was this discussed already so that we know what Matz thinks about it, or
should we bring this topic up on ruby-core?

  • Matthias

Philip B. wrote:

I think it makes perfect sense. If you are doing multiple assignments,
do this:

a = b = c = “hi”

But it’s pretty cool to be able to do this:
a, b, c = [“hi”, “hello”, “wassa”]

The problem is: what is the value of a after:

a, b, c = d

?
The answer is: it depends. If d is an array, then a becomes d[0],
otherwise a becomes d. Since you can write:

a, b, c = “hi”, “hello”, “wassa”

setting each value, and:

a, b, c = “hi”

sets a to “hi” and the other two become nil and since [“hi”, “hello”,
“wassa”] is a single object, it seems natural to expect that:

a, b, c = [“hi”, “hello”, “wassa”]

should set a to the array and set b & c to nil, but it doesn’t. Why
should a be set differently in the following cases:

a, b, c = [“hi”, “hello”, “wassa”]
a, b, c = [“hi”, “hello”, “wassa”], “hey”

You would expect that each of the following would result in a ending up
with the value of d:

a = d
a, b = d
a, b, c = d

but it doesn’t. If d is an array, then “a = d” sets a to the value of
d, but the others set it to d[0]. If d is not an array, then a takes the
value of d in all cases. Having an object change implicitly into
something else like this is definitely a gotcha.

Further, consider this example:

a = [[1, 2, 3]]
b, c, d = *a

What is the value of b? Since the multiple assignment is “equivalent”
to:

b, c, d = [1, 2, 3]

it would seem that the way arrays automagically expand would result in b
becoming 1, but actually b becomes the array [1, 2, 3] meaning that
automagic expansion is not happening. So even the inconsistency is
inconsistent!

The semantics of assignment should not change just because the class of
something on the rhs changes.

I still don’t see a problem. The power of Ruby is that you CAN do
anything you want, not that you SHOULD do anything you want. To use
the canonical example, I CAN do this:

class Fixnum
def +(other)
puts “I’ve ruined your program, hahaha!”
end
end

Suddenly, the addition operator is totally inconsistent and retarded.
Many people think that Ruby shouldn’t even allow you to do this. But,
that’s the whole point of Ruby: you CAN. I’m sure that somewhere,
somehow, for somebody, in some situation, there’s a reason why you
would want to change the addition operator. I can’t think of one, but
does that mean we should disallow it?

It’s kind of similar to what I dislike about Python. It’s great to
have a consistent whitespace formatting schema, and I certainly SHOULD
in my programs, but I just don’t want to be FORCED to do so.

Anyway, I’ve kind of gotten sidetracked.

The point is, if you don’t like the “strange” way that Ruby does
assignments using commas, just don’t use them. Just do it the normal
way:

a = [1,2,3]
b = 4
c = 5

However, for the people who like the convenience, is there anything
wrong with allowing the “strange”, format?

Anyway, even though you are technically correct, I see it as a
complete non-issue.

Also, out of curiosity, if this

a, b, c = [1, 2, 3]

does not mean this

a = 1
b = 2
c = 3

then what on earth should it mean? All I can think of is giving an
error and crashing your program…

On Fri, Feb 22, 2008 at 7:49 AM, Stedwick [email protected]
wrote:

then what on earth should it mean?
The suggestion seems to be that it should mean:

a = [1,2,3]
b = nil
c = nil

just as one would expect from a,b,c = d for any case where d is not an
array. The effect you want would still be available from a, b, c =
*[1,2,3]

a, b, c = [1, 2, 3]
implies
a = [1,2,3]
b = nil
c = nil

That’s even more confusing because that suggests I could do the
following:

a = [1, 2, 3]
b =
c =

which would set both b and c to nil. but theres nothing there! it
shouldnt be set to anything unless i say so.

However, I totally agree that
a, b, c = *[1,2,3]
should work as well, and should in fact be the “right” way of doing it
explicitly.