Is the assignment operator really "just" an operator? - ruby

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.

Related

I'm not sure why this simple code does not work

I want someone to enter number "1", "2", or "3", and if the number is correct, want to say "ok". If not, I want to say "enter 1 or 2 or 3".
This is my code:
puts "enter 1 or 2 or 3"
num = gets.to_i
if num == 1 or 2 or 3
puts "ok"
else
puts "enter 1 or 2 or 3"
end
When I enter an incorrect answer such as "e" or "p", it still says "ok".
Why is it the case?
Let's first examine why you are obtaining incorrect results.
If a equals false or nil (is falsy, meaning logically false), a or b returns the value of b. If a equals any other value (is truthy, meaning logically true), a or b returns a.
Suppose we have an expression a op1 b op2 c, where op1 and op2 are operators (e.g., a == b or c). This could be evaluated (a op1 b) op2 c or a op1 (b op2 c), where parentheses have the highest precedence.
The precedence of Ruby's operators (most implemented as methods) is given here. Note that == has higher precedence than or. Moreover, for any given operator op, a op b op c is evaluated (a op b) op c.
The expression num == 1 or 2 or 3 is therefore evaluated
((num == 1) or 2) or 3
Now consider the value of this expression, depending on the value of num.
num = 1
((num == 1) or 2) or 3 => (true or 2) or 3 => true or 3 => true
num != 1
((num == 1) or 2) or 3 => (false or 2) or 3 => 2 or 3 => 2
Here are some ways to obtain your desired result.
(num == 1) or (num == 2) or (num == 3)
(num == 1) || (num == 2) || (num == 3)
[1, 2, 3].include?(num)
[1, 2, 3] & [num] == [num]
([num] - [1, 2, 3]).empty?
Because of the precedence rules for operators, the parentheses are not needed in the first two expressions, but it can be argued they clarify the code, at least for some readers. (I would include them.)
Regarding the choice between using or or ||, see this SO queston, particularly the second answer. In practice, or is rarely used.
See Array#include?, Array#& and Array#-.
To round out the possibilities, one could conceivably use a case statement.
case num
when 1, 2, 3 then true
else false
end
If, as here, the acceptable values of num form a range, one could write either of the following.
num.between?(1, 3)
(1..3).cover?(num)
(num >= 1) && (num <= 3)
See Comparable#between and Range#cover?. Again, the parentheses in the latter are optional.
In your code, num == 1 or 2 or 3 evaluates to true always, as 2 is considered logically true, and using an or`` operator with logically true value returns atrue` result always.
The correct way to compare is like this
puts "enter 1 or 2 or 3"
num = gets.to_i
if num == 1 or num == 2 or num == 3
puts "ok"
else
puts "enter 1 or 2 or 3"
end
Here, you are comparing the value of variable with right literal.

Elixir: Return value from for loop

I have a requirement for a for loop in Elixir that returns a calculated value.
Here is my simple example:
a = 0
for i <- 1..10
do
a = a + 1
IO.inspect a
end
IO.inspect a
Here is the output:
warning: variable i is unused
Untitled 15:2
2
2
2
2
2
2
2
2
2
2
1
I know that i is unused and can be used in place of a in this example, but that's not the question. The question is how do you get the for loop to return the variable a = 10?
You cannot do it this way as variables in Elixir are immutable. What your code really does is create a new a inside the for on every iteration, and does not modify the outer a at all, so the outer a remains 1, while the inner one is always 2. For this pattern of initial value + updating the value for each iteration of an enumerable, you can use Enum.reduce/3:
# This code does exactly what your code would have done in a language with mutable variables.
# a is 0 initially
a = Enum.reduce 1..10, 0, fn i, a ->
new_a = a + 1
IO.inspect new_a
# we set a to new_a, which is a + 1 on every iteration
new_a
end
# a here is the final value of a
IO.inspect a
Output:
1
2
3
4
5
6
7
8
9
10
10

Ruby equivalent to JavaScript operator `||`

How can this be achieved in Ruby? Can it be done without repeating the variable?
Javascript:
b = a || 7
This assigns a if a is not 0 and 7 otherwise
One specific case is converting date.wday to 7 if it returns 0 (Sunday).
Just out of curiosity:
class Object
def javascript_or?(other)
(is_a?(FalseClass) || nil? || '' == self || 0 == self) ? nil : self
end
end
and:
a = b.javascript_or?(7)
There are only two falsy values in Ruby: nil and false. So, if you really want this approach
a = b == 0 ? 7 : b
is a plausible solution, because 0 can't be evaluated as false.
However, a better option for your need is cwday, and not wday. Then you don't need to make this comparison anymore, because it returns 1 for Monday, 2 for Tuesday, and finally 7 for Sunday, as you need.
date = Date.new(2016,19,6) # => Jun 19 2016, Sunday
date.cwday # => 7
For the particular case of 0 and 7:
a = (b + 6) % 7 + 1
:)
You can use ternary operator:
date.wday == 0 ? 7 : date.wday
What you're describing here is less of a logical problem and more of a mapping one:
WEEKDAY_MAP = Hash.new { |h,k| h[k] = k < 7 ? k : nil }.merge(0 => 7)
This one re-writes 1..6 to be the same, but 0 becomes 7. All other values are nil.
Then you can use this to re-write your day indicies:
b = WEEKDAY_MAP[a]
If at some point you want to tinker with the logic some more, that's also possible.

Difference between || and ||=? [duplicate]

This question already has answers here:
What does ||= (or-equals) mean in Ruby?
(23 answers)
What does the "||=" operand stand for in ruby [duplicate]
(1 answer)
Closed 9 years ago.
I am new to Ruby.
What is the difference between || and ||=?
>> a = 6 || 4
=> 6
>> a ||= 6
=> 6
Sounds like they are the same.
||= will set the left-hand value to the right hand value only if the left-hand value is falsey.
In this case, both 6 and 4 are truthy, so a = 6 || 4 will set a to the first truthy value, which is 6.
a ||= 6 will set a to 6 only if a is falsey. That is, if it's nil or false.
a = nil
a ||= 6
a ||= 4
a # => 6
x ||= y means assigning y to x if x is null or undefined or false ; it is a shortcut to x = y unless x.
With Ruby short-circuit operator || the right operand is not evaluated if the left operand is truthy.
Now some quick examples on my above lines on ||= :
when x is undefined and n is nil:
with unless
y = 2
x = y unless x
x # => 2
n = nil
m = 2
n = m unless n
m # => 2
with =||
y = 2
x ||= y
x # => 2
n = nil
m = 2
n ||= m
m # => 2
a ||= 6 only assigns 6 if it wasn't already assigned. ( actually, falsey, as Chris said)
a = 4
a ||= 6
=> 4
a = 4 || 6
=> 4
You can expand a ||= 6 as
a || a = 6
So you can see that it use a if a is not nil or false, otherwise it will assign value to a and return that value. This is commonly used for memoization of values.
Update
Thanks to the first comment for pointing out the true expansion of the ||= (or equal) operator. I learned something new and found this interesting post that talks about it. http://dablog.rubypal.com/2008/3/25/a-short-circuit-edge-case
Both expressions a = 6 || 4 and a ||= 6 return the same result but the difference is that ||= assigns value to variable if this variable is nil or false.

What's the fastest way to check if input String is a correct RPN expression?

I came across a task which makes you check if a String passed as an argument to your method/function is a correct statement in the Reverse Polish Notation sense. It can contain lowercase alphabet letters, operation signs and integers. Is there any faster way to check it than to read every character separately and actually try to evaluate the whole expression?
You don't have to evaluate the whole expression but you do need to split it into tokens, and you have to know the valence of every operator (that is, how many operands it takes). For simplicity, let the valence of an operand be 0; then do the following:
Set stack_size to 0;
For Each token In expression:
Set stack_size to stack_size + 1 - valence(token)
If stack_size <= 0: Report failure
If stack_size == 1: Report success
Else : Report failure
Examples using _ for unary minus.
expression: 3 4 + 1 * _
stack_size: 0 1 2 1 2 1 1 -> success
expression: 2 3 4 + 1 * _
stack_size: 0 1 2 3 2 3 2 2 -> failure (not 1 at the end)
expression: 2 3 + + 1 * _
stack_size: 0 1 2 1 0 -> failure (stack_size <= 0)
You can use a parse table to recognize reverse polish notation. It requires looking at each character, but it's fast.

Resources