How does ruby do the + operator? - ruby

Ruby does not support incrementing variables like variable++. I saw this behaviour that:
2 ++ 4
gives 6. In fact, any number of + signs between two variables are treated as one single +. How does ruby manage that? And since ruby does that, can it be taken as the reason for the unavailability of the ++ operator?

This:
2 ++ 4
is parsed as:
2 + (+4)
so the second + is a unary plus. Adding more pluses just adds more unary + operators so:
2 ++++++ 4
is seen as:
2 + (+(+(+(+(+(+4))))))
If you provide your +# method in Fixnum:
class Fixnum
def +#
puts 'unary +'
self
end
end
then you can even see it happen:
> 2 ++ 4
unary +
=> 6

Instead of ++ use +=
Example:
a=2
a+=3
puts a
5

Related

Ruby: evaluation order depends on whitespace and seemingly redundant parens, why?

I'm trying to understand the behaviour of arithmetic methods in Ruby. 1 .+ 2 .* 3 and 1 .+ (2) .* 3 and 1. + 2. * 3 and 1. + (2). * 3 and 1.+2.*3 all evaluate to 7, which means :+ is called before :*, but 1.+(2).*3 evaluates to 9, which means :* is called before :+. Redefining both methods confirms that that's what's happening:
class Integer
alias_method :add, :+
alias_method :mul, :*
def + other
print :+
self.add other
end
def * other
print :*
self.mul other
end
end
puts 1 .+ 2 .* 3
puts 1 .+ (2) .* 3
puts 1. + 2. * 3
puts 1. + (2). * 3
puts 1.+2.*3
puts 1.+(2).*3
Output:
*+7
*+7
*+7
*+7
*+7
+*9
Why does it happen? Please point me where to find the relevant documentation. Thanks in advance.
Let's give an example that might be easier to understand
puts (2) + 3
vs
puts(2) + 3
In the first case, puts is followed by a space. Ruby takes it as omitted parentheses and evaluates the rest as the argument. It's equvalent to puts((2) + 3). 5 is printed. In the 2nd case, puts is immediately followed by a left parenthesis and ruby takes that as the beginning of method arguments. It's equivalent to (puts(2)) + 3. It will print a 2 and then fail when trying to evaluate nil + 3.
Now lets look at your examples
puts 1 .+ 2 .* 3
puts 1 .+ (2) .* 3
puts 1. + 2. * 3
puts 1. + (2). * 3
puts 1.+2.*3
In the first 6 cases, no method call is immediately followed by (, all the parentheses are redundant. They are evaluated right to left. Note that not * before +, but right to left.
puts 1 .* 2 .+ 3 #=> 5
In the last example puts 1.+(2).*3, the ( immediately follows .+, so 2 is an argument to .+. The result is then multiplied by 3.

Is the assignment operator really "just" an operator?

My question was triggered by this discussion on SO, which did not lead to an answer that would really explain the issue. I am "rewriting" it here in a slightly different way, because I want to make it more clear what the real problem is and therefore hope to get an answer here.
Consider the following two Ruby expressions:
1 * a - 3
1 && a = 3
From the Ruby precedence table, we know that of the operators mentioned here, * has the highest precedence, followed by -, then by && and finally by =.
The expressions don't have parenthesis, but - as we can verify in irb, providing a suitable value for a in the first case - they are evaluated as if the bracketing were written as (1*a) - 3, respectively 1 && (a=3).
The first one is easy to understand, since * binds stronger than -.
The second one can't be explained in this way. && binds stronger than =, so if precedence only would matter, the interpretation should be (1 && a) = 3.
Associativity (= is right-associative and - is left-associative) can't explain the effect either, because associativity is only important if we have several operators of the same kind (such as x-y-z or x=y=z).
There must be some special rule in the assignment operator, which I did not find in the docs I checked in particular the docs for assignment and syntax.
Could someone point out, where this special behaviour of the assignment operator is documented? Or did I miss / misunderstand something here?
From the doc: https://ruby-doc.org/docs/ruby-doc-bundle/Manual/man-1.4/syntax.html#assign
Assignment expression are used to assign objects to the variables or such. Assignments sometimes work as declarations for local variables or class constants. The left hand side of the assignment expressions can be either:
variables
variables `=' expression
On the right there is an expression, so the result of the expression is assigned to the variable.
So, you should look for expressions (*) before following the precedence.
1 && a = 3 are basically two "chained" expressions:
3 and 1 && 3
Maybe it is more readable as:
1 && a = 3 + 4 where the expressions are 3 + 4 and 1 && 7, see:
1 && a = 3 + 4 #=> 7
1 && 7 #=> 7
res = 1 && a = 3 + 4
res #=> 7
(*) The precedence table also helps to find the expression (Find the precedence table in the linked doc at the Operator expressions paragraph):
What's above the = in the table "forms" an expression to be assigned by =, what's below does not.
For example:
1 + 3 and 2 + 4 #=> 4
a = 1 + 3 and b = 2 + 4 #=> 4
(a = 1 + 3) and (b = 2 + 4) #=> 4
a = (1 + 3 and b = 2 + 4) #=> 6
You can also check these examples respect to the precedence table:
1 && 3 #=> 3
1 && a = 3 #=> 3
a #=> 3
3 and 1 #=> 3
3 and b = 1 #=> 3
b #=> 1
2 ** c = 2 + 1 #=> 8
c #=> 3
d = 2 ** 3
d #=> 8
e = 3
e **= 2
e #=> 9
I think the understanding of 1 && (a = 3) is, understandably, mislead.
a = false
b = 1
b && a = 3
b
=> 1
a
=> 3
Why is a being assigned to in the && expression when a is false? Should the && expression not return when encountering a false value? Spoiler, it does return!
Taking a step back, we think of the purpose of the && operator to control the flow of logic. Our disposition to the statement
1 && a = 3
is to assume the entire statement is returned if a is nil or false. Well no, the interpreter is evaluating like so:
(1 && a) = 3
The interpreter does not raise a if it is nil or false nor does it return the left side if a is nil or false
a = nil
1 && a
=> nil # a was returned
The interpreter returns the variable, this is why the original statement can be read:
a = 3
due to 1 && a returning a which is a variable that can be assigned to by the = operand on the second half of the statement.
TLDR
In your origin example: 1 is neither nil nor false so the variable a is returned in (1 && a) which is subsequently assigned in a = 3
Probably because the other interpretation does not work:
irb(main):003:0> (1 && a) = 3
Traceback (most recent call last):
3: from /home/w/.rbenv/versions/2.7/bin/irb:23:in `<main>'
2: from /home/w/.rbenv/versions/2.7/bin/irb:23:in `load'
1: from /home/w/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/irb-1.2.3/exe/irb:11:in `<top (required)>'
SyntaxError ((irb):3: syntax error, unexpected '=', expecting `end')
(1 && a) = 3
^
So, perhaps Ruby parenthesizes 1 && a = 3 in the only way that is legally interpretable by the language.

Ruby while and if loop issue

x = 16
while x != 1 do
if x % 2 == 0
x = x / 2
print "#{x} "
end
break if x < 0
end
Hi, the result I get from above is 8 4 2 1 . Is there any way to remove the space at the end?
One of Rubys main features is its beauty - you can shorten that loop to a nice one liner when using an array:
x = 16
arr = []
arr.push(x /= 2) while x.even?
puts arr.join(' ')
# => "8 4 2 1"
* As sagarpandya82 suggested x.even? is the same as using x % 2 == 0, leading to even more readable code
Don't print the values into the loop. Put them into a list (array) then, after the loop, join the array items using space as glue.
x = 16
a = []
while x != 1 do
if x % 2 == 0
x = x / 2
a << x
end
break if x < 0
end
puts '<' + a.join(' ') + '>'
The output is:
<8 4 2 1>
As #Bathsheba notes in a comment, this solution uses extra memory (the array) to store the values and also the call to Array#join generates a string that doubles the memory requirements. This is not an issue for small lists as the one in the question but needs to be considered the list becomes very large.
loop.reduce([[], 16]) do |(acc, val), _|
break acc if val <= 1
acc << val / 2 if val.even?
[acc, val / 2]
end.join ' '
if x != 0
print " "
end
is one way, having dropped the suffixed space from the other print. I/O will always be the performance bottleneck; an extra if will have a negligible effect on performance, and the extra print will merely contribute to the output stream which is normally buffered.

Why do newlines within parenthesis change arithmetic results?

Why do the following expressions resolve the way they do? Brackets should have higher precedence than newlines, shouldn't they?
3 - ( 1 + 1 )
# => 1
3 - ( 1
+ 1 )
# => 2
Omitting the plus also lets the expression evaluate to 2:
3 - ( 1
1 )
# => 2
If I declare as a continuous newline (escaped) or move the plus to the first line, desired behavior is achieved:
3 - ( 1 \
+ 1 )
# => 1
3 - ( 1 +
1 )
# => 1
It is because Ruby recognizes a new line as an end of an expression unless the expression is incomplete. For example,
(1
+ 1)
is the same as
(1;
+1)
which is the same as +1 since the last expression within the parentheses is returned. And that is further the same as 1.
When you have a + at the end of a line, the expression is incomplete, and hence continues to the next line. That makes:
3 - ( 1 +
1 )
to be interpreted as 3 - (1 + 1).
If you have code in the brackets then every line will be threat as separated code line of code if you won't end it with \ or start new one with math operator.
So in your example:
def plus_minus_if_you_understand_this_the_problem_is_solved_i_guess
3 - (1
1 )
end
Means I have number 3 and want to subtract expression in brackets. In the brackets I have line #1 number 1 and in #2 line number 1 again and as it's last line of expression it's retuned by Ruby (like in def last item before end is returned. So:
( 3 # doing nothing
1 ) # returns 1
Also take a look bellow. Again, this part of code returns 2 as it is the last item in brackets:
( puts "hello!"
2 ) => 2

Which of the following postfix notations correctly represents infix sum 1+2+3+4?

I am testing an infix-to-postfix-to-infix converter and found some kind of uncertainty. For example, a simple infix sum
1 + 2 + 3 + 4
can be converted to postfix one
1 2 + 3 + 4 +
assuming that operators with equal precedence are not accumulated. If they are then I get
1 2 3 4 + + +
On the other hand, all the following postfix expressions can be converted to the initial sum
1 2 + 3 + 4 +
1 2 + 3 4 + +
1 2 3 4 + + +
Are all these postfix expressions correct?
UPDATE1
If you would make such converter, to which form would you choose? I need to choose one for testing.
You need to define an extra constraint.
Mathematically, your postfix expressions are all the same. But on a computer integer addition is not really commutative because of overflow.
Replace 1 2 3 4 with a b c d and consider the possibility of overflow. Most programming languages define that a + b + c + d must be evaluated left-to-right so that a b + c + d + is the only correct translation.
Only when you define that the order of evaluation is 'unspecified' all the postfix versions are equivalent. That was the case for (older) C Compilers.
Yep, all correct. They correspond to the following bracketed infix expressions:
((1 + 2) + 3) + 4
(1 + 2) + (3 + 4)
1 + (2 + (3 + 4))
+ is confusing - it is commutative, so in fact, every result seems correct.
Consider replacing + with other operators: 1 a 2 b 3 c 4.
The correct result here, for left-associative operators, is
1 2 a 3 b 4 c
So, in your case, I'd expect 1 2 + 3 + 4 +

Resources