What Does the (Unary) * Operator Do in This Ruby Code

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



Leave a reply



Submit