What does the (unary) * operator do in this Ruby code?
The *
is the splat operator.
It expands an Array
into a list of arguments, in this case a list of arguments to the Hash.[]
method. (To be more precise, it expands any object that responds to to_ary
/to_a
, or to_a
in Ruby 1.9.)
To illustrate, the following two statements are equal:
method arg1, arg2, arg3
method *[arg1, arg2, arg3]
It can also be used in a different context, to catch all remaining method arguments in a method definition. In that case, it does not expand, but combine:
def method2(*args) # args will hold Array of all arguments
end
Some more detailed information here.
What's the point of unary plus operator in Ruby?
Perhaps it's just a matter of consistency, both with other programming languages, and to mirror the unary minus.
Found support for this in The Ruby Programming Language (written by Yukihiro Matsumoto, who designed Ruby):
The unary plus is allowed, but it has no effect on numeric operands—it simply returns the value of its operand. It is provided for symmetry with unary minus, and can, of course, be redefined.
Ruby's Unary * Operator
It's the splat-operator if you want to google it. It does transform an array into a list (so you can use an array as arguments to a method). It also does the opposite: it can 'slurp' a list into an array.
require 'date'
*date_stuff = 2012,2,29 # slurp
p date_stuff #=> [2012, 2, 29]
Date.new(*date_stuff) # regurgitate
Why would one use the unary operator on a property in ruby? i.e &:first
Read the answers in the duplicate questions for the meaning and usage of &:...
. In this case, entries
is an array, and there are three methods map
, sort_by
, and map
chained. sort_by(&:last)
is equivalent to sort_by{|x| x.last}
. map(&:first)
is the same as map{|x| x.first}
. The reason the first map
does not use &:...
is because (i) the receiver of accept_entry
is not e
, and (ii) it takes an argument e
.
Unary operators behavior
I suspect you're seeing a side effect of the parser's behavior in how it interprets numeric literals.
If we create our own class:
class C
def +@
11
end
end
and then look at some things:
> c = C.new
> +c
=> 11
> ++c
=> 11
That's exactly what we expect to happen. If we use your Fixnum
unary +
override and a Fixnum
variable:
> n = 23
> +n
=> 15
> ++n
=> 15
then again we see what you're expecting. In both cases, we see the result of calling the +@
method on a non-literal.
But when we look at +6
with your operator in place:
> +6
=> 6
the +@
method is not called. Similarly if we override -@
:
class Fixnum
def -@
'pancakes'
end
end
and see what it does:
> -42
=> 42
So what's going on here? Well, Ruby is seeing +6
and -42
not as 6.send(:+@)
and 42.send(:-@)
method calls but as single literals for positive six and negative forty-two.
If you start adding parentheses, +(6)
and -(42)
, then Ruby sees non-literal expressions and ends up calling the unary methods. Similarly when you double the unary operators.
Ruby unary operator `&` only valid on method arguments
The &
unary prefix ampersand operator "unpacks" a Proc
(or an object that can be converted to a Proc
by sending it the to_proc
message) into a block, passing it as if it had been passed as a block. Only message sends can have block arguments, ergo the &
unary prefix ampersand operator is only allowed for the last argument in an argument list to a message send.
Likewise, the "dual" unary prefix ampersand operator "packs" a block into a Proc
and is thus only allowed for the last parameter in a parameter list of either a block or a method definition.
Why is the splat/unary operator changing the assigned value a when p is called before *a = ?
Very interesting question! Ruby takes this expression:
p *a = "a"
and translates it to something like this:
temp = (a = "a")
p *temp
So the first thing that happens is that a
gets assigned to "a"
, and then the result of the assignment expression which is "a"
gets splatted and sent to p
. Since p
's default behaviour when sent multiple arguments is just to iterate over and print each one, you only see "a"
appear.
In short, it follows a "assign then splat" order of evaluation. So a
gets assigned to "a"
before the string gets splatted.
When you don't have a function call however, it is interpreted as something like this:
# *a = "a" gets interpreted as:
temp = "a"
a = *temp
This follows a "splat then assign" order of evaluation. So a
gets assigned after the string gets splatted.
You can see what's being received by a function by going like this:
def foo *args
puts args.inspect
end
foo *a = "a" # outputs ["a"]
a # outputs "a"
Hope this clears up what's going on!
In short (thanks to Mark Reed):
p *a = "a" # interpreted as: p(*(a = "a"))
*a = "a" # interpreted as: a = *("a")
meaning of * in ruby function
consider a following method
def user(user_name)
puts user_name
end
so when you call
user("RPV")
Output:
RPV
=> nil
but what if you pass more then one argument like
user("RPV", "Marek")
it will give an error
wrong number of arguments (2 for 1)
To avoid this kind of error splat(*) operator is helpful
def user(*user_name)
puts user_name
end
and when you pass more than one argument it handles converts it in array
user("RPV", "Marek")
output:
RPV
Marek
nil
it makes user_name as an array
def user(user_name)
p user_name
end
user("RPV", "Marek")
output:
["RPV", "Marek"]
Hope you got the use of it.
Related Topics
Why Is "Slurping" a File Not a Good Practice
How to Test If a String Is Basically an Integer in Quotes Using Ruby
Connecting Rails 3.1 With Multiple Databases
How to Install Sqlite3 For Ruby on Windows
Using Rails Serialize to Save Hash to Database
Nokogiri/Xpath Namespace Query
Add a Default Value to a Column Through a Migration
How to Search Within an Array of Hashes by Hash Values in Ruby
Measure the Distance Between Two Strings With Ruby
How to Find and Return a Duplicate Value in Array
How to Create a Deep Copy of an Object in Ruby
Which Ruby on Rails Is Compatible With Which Ruby Version
Difference Between Various Variables Scopes in Ruby
Error Installing Libv8: Error: Failed to Build Gem Native Extension