Unary operators behavior - ruby

Had some weird results when redefining the unary + operator in Ruby on the Fixnum class. Not exactly sure why things are happening the way they are (specifically line 009).
irb:003> class Fixnum
irb:004> def +# #define unary +
irb:005> 15
irb:006> end
irb:007> end
=> nil
irb:008> 2
=> 2
irb:009> +2
=> 2
irb:010> +(2)
=> 15
irb:011> ++2
=> 15

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.

This answer adds additional details to mu's answer.
Just learned about ruby's Ripper class, and it shows what's happening pretty clearly:
require 'ripper'
p Ripper.sexp('2') # => [:program, [[:#int, "2", [1, 0]]]]
p Ripper.sexp('+2') # => [:program, [[:#int, "+2", [1, 0]]]]
p Ripper.sexp('+(2)') # => [:program, [[:unary, :+#, [:paren, [[:#int, "2", [1, 2]]]]]]]
p Ripper.sexp('++2') # => [:program, [[:unary, :+#, [:#int, "+2", [1, 1]]]]]

Related

What is the difference between .length and .length() in Ruby?

I am just wondering if .length() is just an alias of .length because they return the same result:
[38] pry(main)> temp = [1,2,3]
=> [1, 2, 3]
[40] pry(main)> temp.length
=> 3
[41] pry(main)> temp.length()
=> 3
There is no difference. In Ruby, you can omit the parentheses around arguments in many cases.
temp.length
temp.length()
are identical. The same is true for
temp.push(4)
temp.push 4
In Ruby, when you define or call (execute, use) a method,
you can omit the parentheses.
So these lines mean exactly the same:
temp.length
temp.length()
http://ruby-for-beginners.rubymonstas.org/bonus/parentheses.html
Just an addition to these answers.
You can also omit parentheses on method definitions as well, not only method calls.
For example:
def length
1
end
def length()
2
end
http://ruby-for-beginners.rubymonstas.org/bonus/parentheses.html

Double splat on `nil`

My understanding is that a single splat on a non-array object calls to_a and then dissociates the elements apart. And since nil.to_a is defined to be [], the following conversion happens:
[:foo, *nil, :bar]
# => [:foo, *nil.to_a, :bar]
# => [:foo, *[], :bar]
# => [:foo, :bar]
By analogy, I thought that a double splat on a non-hash object calls to_h and then dissociates the key-value pairs apart. And since nil.to_h is defined to be {}, I expected the following conversion to happen:
{"foo" => 1, **nil, "bar" => 2}
# => {"foo" => 1, **nil.to_h, "bar" => 2}
# => {"foo" => 1, **{}, "bar" => 2}
# => {"foo" => 1, "bar" => 2}
But actually, it raises an error: no implicit conversion of nil into Hash. Why does it behave like that?
Edit I am not asking about the reasoning behind the design. I am asking where my thinking is wrong regarding double splat.
Well it's our human being super power to recognize patterns and predict things. However it's not always true. This is one example. Ruby is not consistent in splat and double splat. Your way of thinking is a good way to "remember" but it's not exactly the way Ruby works on splats.
See this bug report for more detail. In this bug report, Ruby's author Matz rather to remove the feature of being able to splat nil than add double splat to nil.
The reason *nil works is because the splat operator works on anything that responds to to_a, and nil.to_a returns []. The reason **nil doesn't work is that nil doesn't respond to to_hash, which is to_a's double-splat counterpart.
If you wanted this behavior, you could monkey-patch NilClass:
class NilClass
def to_hash
{}
end
end
{ "foo" => 1, **nil, "bar" => 2 }
# => { "foo" => 1, "bar" => 2 }

How to return two separate arrays of keys and values

Please explain how this piece of code can return two arrays.
def keysAndValues(data)
[data.keys, data.values]
end
keysAndValues method does return a single array (of two arrays inside of it), but its output can be interpreted as two arrays. Let me explain:
single_array = ["hello", "world"]
puts single_array # => ["hello", "world"]
first_element, second_element = single_array
puts first_element # => "hello"
puts second_element # => "world"
The reason this notation is possible is the implementation of Ruby's assignment (=) operator. Thus, calling a, b = keysAndValues(data) makes both variables a and b filled. But beware, although the outcome technically makes sense this might be unexpected in some situations:
first, second = 1 # first is 1, second is nil
There are also some other uses for multiple assignment, consider the following case:
a, b, *c = [1, 2, 3, 4] # note the asterisk symbol here
puts a # => 1
puts b # => 2
puts c # => [3,4]
Ruby supports parallel assignment. This is a simple example:
foo, bar = 1, 2
foo # => 1
bar # => 2
Your method is returning two values inside an array:
keys_and_values(
{
a: 1,
b: 2
}
).size # => 2
which are assigned via = to the two values on the left side of the equation. The values in the array are references to where the keys and values sub-arrays are located:
foo, bar = keys_and_values(
{
a: 1,
b: 2
}
)
foo.object_id # => 70159936091440
bar.object_id # => 70159936091420
Ruby isn't unique with supporting parallel assignment; It is used in other languages too. Any decent Ruby manual will talk about this. It's good to understand because the assignment to variables, or passing multiple parameters to methods is something you'll encounter repeatedly in Ruby. Do a search for more information.
Also, in Ruby we don't name methods using camelCase, such as "keysAndValues". Instead we use snake_case: keys_and_values.

What does the syntax [*a..b] mean in Ruby?

NOTE: mischa's splat on GitHub has lots of cool interactive examples of * in action.
By googling, I found that one way to iterate over a range of numbers in Ruby (your classic C-style for loop)
for (i = first; i <= last; i++) {
whatever(i);
}
is to do something like this
[*first..last].each do |i|
whatever i
end
But what exactly is going on with that [*first..last] syntax? I played around with irb and I see this:
ruby-1.9.2-p180 :001 > 0..5
=> 0..5
ruby-1.9.2-p180 :002 > [0..5]
=> [0..5]
ruby-1.9.2-p180 :003 > [*0..5]
=> [0, 1, 2, 3, 4, 5]
ruby-1.9.2-p180 :004 > *0..5
SyntaxError: (irb):4: syntax error, unexpected tDOT2, expecting tCOLON2 or '[' or '.'
*0..5
^
Everything I've read online discusses the unary asterisk as being useful for expanding and collapsing arguments passed to a method, useful for variable length argument lists
def foo(*bar)
bar
end
foo 'tater' # => ["tater"]
foo 'tater', 'tot' # => ["tater", "tot"]
and I get that, but I don't see how it applies to the expansion being done in my block example above.
To be clear, I know that The Ruby Way is to iterate over an array or collection, not to use the array length and iterate with an integer index. However, in this example, I really am dealing with a list of integers. :)
[*1..10]
is the same thing as
(1..10).to_a # call the "to array" method
Instances of the Array class you have created implement Enumerable so your loop works. On classes that define a to_a method, you can use the splat operator syntax with brackets. Splat does a lot more than just call #to_a though, and would be worth a Google search on its own.
Now, in your case, the Range class itself is already an Enumerable so you could just do:
(first..last).each do |v|
...
end
[first..last] is an array containing only 1 range object. [*first..last] is an array containing the elements of that range having been sent in as an argument list. * works in the context of an argument list.
It is called a splat operator. If you use it within certain positions like an argument position or an array, it will expand into its elements:
a = [1]
[*a, 3] # => [1, 3]
b = [1, 2]
[*b, 3] # => [1, 2, 3]
You can't use it bare or in ranges:
*a..3 # => error.
(*a..3) # => error.
When you have something that is not an array, it returns itself:
a = 1
[*a, 3] # => [1, 3]

Are there Ruby precedence issues with using Proc.call vs. Proc.[]?

Recently I was having a discussion with a friend about Ruby's Proc. You can call a Proc in one of several ways. One way is to invoke Proc.call:
p = Proc.new { |x| "hello, #{x}" }
p.call "Bob"
=> "hello, Bob"
Another is to use braces, Proc.[]:
p ["Bob"]
=> "hello, Bob"
Are there any potential precedence issues here, or are these two statements completely interchangeable? If not, can you provide an example of a context where different results would be provided?
The #call technique allows the operator precedence to potentially obscure intent:
p = Proc::new do |a1| Proc::new do |a2| "#{a1.inspect}:#{a2.inspect}" end end
p.call([1,2,3]).call [1]
=> => "[1, 2, 3]:[1]"
p.call [1,2,3][1]
=> #<Proc:0x7ffa08dc#(irb):1>
p.call([1,2,3])[1]
=> "[1, 2, 3]:1"
p[[1,2,3]][[1]]
=> "[1, 2, 3]:[1]"
The [] syntax makes the syntactic association of the arguments to the method more robust, but you'd achieve the same effect by putting parentheses around the arguments to Proc#call.

Resources