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

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.

Related

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.

Why Can't This Code Find Powers? (Ruby)

App Academy's practice test says their chosen way of finding if an input is a power of 2 is to keep dividing it by 2 on a loop and check whether the end result is 1 or 0 (after having tested for the numbers 1 and 0 as inputs), which makes sense, but why won't this way work?
def try
gets(num)
counter = 0
go = 2 ** counter
if num % go == 0
return true
else
counter = counter + 1
end
return false
end
I can't figure out why this won't work, unless the counter isn't working.
There are a number of problems with your code.
First of all, there is no loop and your counter will reset to zero each time if you intend to use the method in a loop, because of counter = 0.
counter = 0; go = 2 ** counter basically means go = 2 ** 0 which is 1. Therefore num % 1 will always be 0
You actually need to divide the number and change it in the process. 12 % 4 will return 0 but you don't know by that if 12 is a power of 2.
IO#gets returns a string and takes a separator as an argument, so you need to use num = gets.to_i to actually get a number in the variable num. You are giving num to gets as an argument, this does not do what you want.
Try:
# Check if num is a power of 2
#
# #param num [Integer] number to check
# #return [Boolean] true if power of 2, false otherwise
def power_of_2(num)
while num > 1 # runs as long as num is larger than 1
return false if (num % 2) == 1 # if number is odd it's not a power of 2
num /= 2 # divides num by 2 on each run
end
true # if num reached 1 without returning false, it's a power of 2
end
I add some checks for your code. Note, that gets(num) returns a String. Your code is fine, but not for Ruby. Ruby hates type-cross-transform like Perl does.
def try(num = 0)
# here we assure that num is number
unless (num.is_a?(Integer))
puts "oh!"
return false
end
counter = 0
go = 2 ** counter
if num % go == 0
return true
else
counter = counter + 1
end
return false
end
The general problem is "how string could use '%' operator with number?"
Try some code in the interpretator (irb):
"5" % 2
or
"5" % 0

Problems with Modulo operator Ruby: Why am I getting "undefined method `%' for 1..100:Range"?

For some reason I'm getting the error undefined method '%' for 1..100:Range when I run the following code:
[1..100].each do |x|
if x % 3 == 0 && x % 5 == 0
puts "CracklePop"
elsif x % 3 == 0
puts "Crackle"
elsif x % 5 == 0
puts "Pop"
else
puts x
end
end
Any idea what's going on? Any help is much appreciated.
That's the wrong syntax for ranges.
You've made an array with 1 element, and that element is itself the range 1..100. What you've written is equivalent to [(1.100)]. You're iterating over the outer array one time, and setting x to (1..100)
You want (1..100).each, which invokes each on the range, not on an array containing the range.
By doing [1..100] you are not looping from 1 to 100 but on 1..100, which is a Range object, what you really want to do is:-
(1..100).step do |x|
if x % 3 == 0 && x % 5 == 0
puts "CracklePop"
elsif x % 3 == 0
puts "Crackle"
elsif x % 5 == 0
puts "Pop"
else
puts x
end
end
Basically, Range represents an interval, you can iterate over Range as explained here, create an array from Range as explained here and more details on range can be found here.
Just as it says. 1..100 does not have a method %. The expression (1..100) % 3 is undefined.

Ruby: 'case true' when does it block loop?

There is piece of code
A = "am"
F = "fm"
def fmam(n)
return if n == 0
loopy(n - 1)
case true
when n % 15 == 0
puts B + L
when n % 5 == 0
puts L
when n % 3 == 0
puts B
else
puts n
end
end
fmam(20)
in this code what does case true do it this code?
case has two forms. The form you're using compares the "target" after the case keyword ( true in this case) with each comparison (the part after each when keyword) using the === operator. You end up with a series of boolean expressions and execute the code for the first one that evaluates to true. As such, it's redundant and a bit confusing. It would be better to remove the true and use the second form of case:
case
when n % 15 == 0
puts B + L
when n % 5 == 0
puts L
when n % 3 == 0
puts B
else
puts n
end
This does the same thing but is clearer.
tutorialspoint :- says
case expression
[when expression [, expression ...] [then]
code ]...
[else
code ]
end
Compares the expression specified by case and that specified by when using the === operator and executes the code of the when clause that matches.
saying that look below:
A = "am"
F = "fm"
L = "dd"
B = 'aa'
def fmam(n)
return if n == 0
case true
when n % 15 == 0
puts B + L
when n % 5 == 0 # this evaluates to true first, which then matched with true value mentioned in the case statement.
puts L
when n % 3 == 0
puts B
else
puts n
end
end
fmam(20) #=> dd
Now look at the below code:
A = "am"
F = "fm"
L = "dd"
B = 'aa'
def fmam(n)
return if n == 0
case false
when n % 25 == 0 # this evaluates to false first, which then matched with false value mentioned in the case statement.
puts B + L
when n % 5 == 0
puts L
when n % 3 == 0
puts B
else
puts n
end
end
fmam(30) #=> aadd
You could refactor the case statement to
msg = case 0
when n % 15
B + L
when n % 5
L
when n % 3
B
else
n
end
puts msg

Ruby logical "and" operator

When playing with IRB I came across this:
a = -1
b = 1
(a and b) > 0
returns true, however
(false and true) == true
returns false.
Why does the first statement return true? In the 'pickaxe' I read "Both and and && evaluate to true only if both operands are true. They evaluate the second operand only if the first is true[...]"
This implies -- to me -- that the first statement should return false
Why should it be false? Both operands a and b are not false, and even not nil..
IIRC, in Ruby, every value different than false/nil is considered true. Even zero. Hence 0 and 0 is true. -1 and 1 surely too!
EDIT: aah, I just grasped what you meant. You mean that the first expression -1 and 1 should return -1? No, that's what OR does!
-1 and 1 => 1
-1 or 1 => -1
AND evaluates ALL operand for 'true' result, and reduces checks only if one item is 'false'.
OR evaluates ALL operand for 'false' result, and reduces checks only if one item is 'true'
Both -1 and 1 are "truthy" values from ruby's point of view. That's why
-1 and 1 # => 1
false and true => false
that the first statement should return false
I don't see where you got this from. Aren't you confusing it with OR operator?
a = -1
b = 1
a and b # => 1
(a and b) > 0 # => true
a && b # => 1
(a && b) > 0 # => true
a || b # => -1
(a || b) > 0 # => false
in ruby, the only time an object is false is if it is false or nil.
>> !!nil # false
>> !!false # false
>> !!-1 # true
in your syntax, it is impossible to get what you want to say when you try
>> (1 and -1) > 0
since and should be used to return true or false but what I found interesting is the value returned
>> (1 and -1) # -1
>> (-1 and 1) # 1
so having (b and a) > 0 would return false which is interesting
(1 and -1) will return -1 since the and operator needs to satisfy all conditions. there's something wrong with me today.

Resources