What does *args do?

S

Stedwick

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

What does the *args do? Thanks!
 
A

Arlen Cuss

[Note: parts of this message were removed to make it a legal post.]

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:
blah: 1, args: []
=> nilblah: 1, args: [2]
=> nilblah: 1, args: [2, 3]
=> nil
Cheers,
Arlen.
 
7

7stud --

Philip said:
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.
 
A

Arlen Cuss

[Note: parts of this message were removed to make it a legal post.]

Hi there,

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:
1
2
{:a=>false}
=> nil1
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
 
M

Mark Bush

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.
 
A

Arlen Cuss

[Note: parts of this message were removed to make it a legal post.]

Hi,

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
 
M

Matthias Wächter

Arlen said:
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

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!
 
S

Stedwick

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"]
 
M

Mark Bush

Philip said:
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.
 
M

Matthias Wächter

Mark,

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

Mark said:
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"

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.
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!

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
 
S

Stedwick

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...
 
C

Christopher Dicely

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?

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]
 
S

Stedwick

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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,284
Messages
2,571,413
Members
48,106
Latest member
JamisonDev

Latest Threads

Top