How to understand the '...' operator used with Boolean comparisons - ruby

select = []
0.upto 5 do |value|
select << value if (value==2)...(value==2)
end
p select # [2,3,4,5]
Can anyone tell me how to understand this code?

I learned something researching this because I've never seen the range operator used on boolean values. Apparently in this context its called a "flip flop" operator. Basically, the condition evaluates False until the first part of the conditional is True. Then it "flips" and evaluates True until the 2nd part evaluates True. In your example, the 2nd part will never evaluate to True as its already passed the valid condition of value == 2 therefore it will continue on with the range provided. You can see this flip-flopping in action if you change the 2nd condition to value == 4:
select = []
0.upto 5 do |value|
select << value if (value==2)...(value==4)
end
p select # [2,3,4]
Reference: http://nithinbekal.com/posts/ruby-flip-flop/

Related

Case code in Ruby program not working with a passed value

Have written some test code for a program, trying to pass 2 values, a file and a number. The below doesn't work at all, but if I have something like puts "test" (outside the case) it works.
def read_album(music_file, number)
puts number #(this does something)
case number.to_i
when(number > 1)
puts "done"
when(number < 2)
puts "something"
when(number == 3)
puts "none of this inside case is working"
end
end
def main()
a_file = File.new("albums.txt", "r")
print("Choose album id: ")
choice_of_album = gets().to_i
read_album(a_file, choice_of_album)
end
main()
Your cases are not doing what you think. The expressions given to when are evaluated and the result will be compared to the case value using the case equality operator ===. An expression such as number > 1 will evaluate to either true or false. It makes no sense to compare this result to an integer in Ruby.
You should compare against the constants directly.
case number
when 1
# handle 1
when 2
# handle 2
when 3
# handle 3
else
# handle unknown case; error?
end
Note that other classes may override === to provide useful behavior. The Range and Regexp classes, for example, do this.
case number
when 1..3
# handle 1, 2 and 3
end
case string
when /pattern/
# handle pattern
end
Notably, the Proc class also does this!
def greater_than(n)
proc { |x| x > n }
end
case number
when greater_than(2)
# handle number > 2
end
You need to drop the number.to_i from the case statement.
Or do something like
case number.to_i
when 1..2
puts "foo"
when 3..100
puts "bar"
else
puts "foobar"
end
end
From the Ruby docs
Case statements consist of an optional condition, which is in the position of an argument to case, and zero or more when clauses. The first when clause to match the condition (or to evaluate to Boolean truth, if the condition is null) “wins”, and its code stanza is executed. The value of the case statement is the value of the successful when clause, or nil if there is no such clause.
Your version would evaluate to somehting like
if (number > 1) === number.to_i
and since you are comparing a number with a boolean expression this will not evaluate to true. If you had an else in the case statement this would have been called.

Why does this return `nil` or the array?

It seems that the following code randomly returns the expected array for d, or nil.
def f arg
arg
end
def g *args
args
end
x,y,z = [true, 'two', false]
if a = f(x) and b = f(y) and c = f(z) then
d = g(a,b,c)
end
p d
The = operator performs assignment. It's different from the equality operator (==), which you would use to compare two values.
Here's a typical example of how these are used:
def speak(name)
if name == 'Hal'
puts "I can't do that, Dave."
end
end
myName = 'Hal'
speak(myName)
Then the output is:
I can't do that, Dave.
You're using = in the condition of your if statement, where you probably meant to use ==. In irb this generates a warning like this:
(irb):9: warning: found = in conditional, should be ==
So you're probably wondering, If I used the wrong operator, why does my code work at all?
Your code can still execute because
Assignments are expressions, meaning they return values.
All values can be evaluated as "true" or "false" (truthy or falsey).
What this means is that, in your code, a = f(x), b = f(y), and c = f(z) are each returning a value in addition to assigning a variable.
In each case, the value returned is the same as what is assigned: true, "two", and false, respectively. Therefore, your if statement is evaluating this condition:
true and "two" and false
In Ruby, all values are "truthy" except false and nil.
The string "two" is truthy, because it is neither false nor nil. However, the false is of course, "falsey," meaning that the whole expression evaluates as false, and the body of the if statement does not execute, so that g is never called, and the value of d remains unchanged.

Guessing game for ruby

I'm currently trying to create a simply made guessing game, In the code there will be three set number (for now) that a person has to guess. If he/she guesses all the numbers correctly it puts, "Congrats, you win!"
Now as a beginning test i just wanted the user to guess one number correctly and the code gives back correct or incorrect.
random_guess = [1, 3, 5]
puts "Please Pick a number, 1-5"
pick_num = gets.chomp
if pick_num == random_guess = true
puts "Correct!"
else
puts "Incorrect!"
end
(I know this code is very beginner, i'm very new to ruby.) for some reason every time i run this program it puts incorrect.
Your if statement is wrong. It should be:
if random_guess.include? pick_num.to_i
Note that if you leave off the to_i the equality check will always fail because you're comparing the integer 3 against the string "3".
Just to help you a bit more. Since your end goal is to have the user guess all the numbers correctly. You can just loop on the same if statement I wrote above. And every time they guess a correct number you can remove it from the array like such:
random_guess.delete(pick_num.to_i)
Once the array is empty, the user has won.
Making random_guess random:
random_guess = []
3.times{random_guess << rand(1..5)}
random_guess
# => [5, 1, 4] # will be random in every other iteration
Checking if the number exists, you can either use index or include?:
if random_guess.index(pick_num.to_i) # alternatively random_guess.index(pick_num.to_i)
puts "Correct!"
else
puts "Incorrect!"
end
Why is your code always printing incorrect?
You are doing if pick_num == random_guess = true, which is a blunder. What is actually happening here is:
you are assigning true to random_guess. i.e. irrespective to what value (Array) random_guess holds, you are overwriting it with true.
Then you are comparing random_guess with pick_num.
So essentially you are doing this:
if pick_num == (random_guess = true) # say pick_num = "1"
# "1" == true # which is obviously false.
The correct conditional statement should be:
if pick_num == random_guess
However this will also print false every time. Reason?
pick_num is a string.
random_guess is an array that contains integer values.
You are comparing two different object types. So its always false.
Therefore the right way to solve this is checking whether the user entered value exists in the Array. For that you can use Array#index or Array#include?. Hence the statement in my proposed solution:
if random_guess.index(pick_num.to_i)
NOTE: pick_num.to_i converts pick_num (a character) into an integer. This is required as your array contains only integers and not characters.

iterate array combination method with .any method

Is there anyway to iterate through different combinations of arrays?
I'm writing a program that returns true if the largest number in an array can be the sum of any of the members of the array.
This is my code: (forgive me, i learned how to program 2 weeks ago for the first time)
def ArrayAdditionI(arr)
arr=arr.sort
largest=arr.pop
n=arr.length
for i in 1..n
if arr.combination(i).to_a.any? {|array| array.inject(:+)==largest}
return true
else
return false
end
end
end
i tested it for
a = [1,2,3,4]
and it returned false even though clearly, 3+1 = 4.
When I change the above code from arr.combination(i) to arr.combination(2) its returns true. So I'm guessing it has something to do with the combination method not being able to be looped. Any suggestions?
You have return false in the wrong place. As it is, false is returned is there is no combination of one element (i.e., one element other than the one you've removed after sorting) that sums (i.e., is equal to) largest. Rather you want to return false only if no combination of any size sums to largest. This is what you need:
def max_match?(arr)
arr=arr.sort
largest=arr.pop
n=arr.length
for i in 1..n
return true if arr.combination(i).any? {|array|
array.inject(:+)==largest}
end
false
end
arr = [1,4,7,3,5,2,13]
max_match?(arr) #=> true
arr = [1,3,4,7,59]
max_match?(arr) #=> false
A few notes:
lower case letters and optional underscores are used for names of methods. You can also put a question (?) or explanation (!) mark at the end. Here a question mark seems appropriate.
to_a is not needed. Enumerable methods (e.g., any?) can have Enumerators as receivers.
sorting is expensive and unnecessary. Instead, just get the max value and remove it from the array. (Aside: you didn't say what happens if the maximum value appears more than once. See the code below and the last sentence.)
you don't need n=arr.length. for i in 1..arr.length is enough, except...
for ... is rarely used. Rubyists tend to use each instead, or one of the many methods from the Enumerable (mix-in) module (all of which invoke each).
you don't need return false because Ruby returns the value of the last statement evaluated, which is false if the method does not return true earlier.
Here's a more Ruby-like way to do that:
def max_match?(arr)
mx = arr.max
whats_left = arr - [mx]
(1..whats_left.size).any? {|n| whats_left.combination(n).any? {|c|
c.reduce(:+) == mx }}
end
arr = [1,4,7,3,5,2,13]
max_match?(arr) #=> true
arr = [1,3,4,7,59]
max_match?(arr) #=> false
If you wish this to return true when arr contains more than one value equal to mx, insert the following after mx = arr.max:
return true if arr.count(mx) > 1

Why does a Flip-Flop operator include the second condition?

The following code is using a flip-flop operator.
(1..10).each {|x| print "#{x}," if x==3..x==5 }
Why are the results 3,4,5?
I think it should be 3,4.
As mentioned in a tutorial, this expression becomes true when x == 3, and continues to be true until x == 5. How could '5' been printed if it evaluates to false? Could anyone please clarify that for me?
The important link, from "The Ruby Programming Language" is :
4.6.9.1 Boolean flip-flops
When the .. and ... operators are used in a conditional, such as an if statement, or
in a loop, such as a while loop (see Chapter 5 for more about conditionals and loops),
they do not create Range objects. Instead, they create a special kind of Boolean
expression called a flip-flop. A flip-flop expression evaluates to true or false, just as
comparison and equality expressions do. The extraordinarily unusual thing about a
flip-flop expression, however, is that its value depends on the value of previous evalu-
ations. This means that a flip-flop expression has state associated with it; it must
remember information about previous evaluations. Because it has state, you would
expect a flip-flop to be an object of some sort. But it isn’t—it’s a Ruby expression, and
the Ruby interpreter stores the state (just a single Boolean value) it requires in its internal parsed representation of the expression.
With that background in mind, consider the flip-flop in the following code. Note that
the first .. in the code creates a Range object. The second one creates the flip-flop
expression:
(1..10).each {|x| print x if x==3..x==5 }
The flip-flop consists of two Boolean expressions joined with the .. operator, in the
context of a conditional or loop. A flip-flop expression is false unless and until the
lefthand expression evaluates to true. Once that expression has become true, the ex-
pression “flips” into a persistent true state. It remains in that state, and subsequent
evaluations return true until the righthand expression evaluates to true. When that
happens, the flip-flop “flops” back to a persistent false state. Subsequent evaluations
of the expression return false until the lefthand expression becomes true again.
In the code example, the flip-flop is evaluated repeatedly, for values of x from 1 to
10. It starts off in the false state, and evaluates to false when x is 1 and 2. When
x==3, the flip-flop flips to true and returns true. It continues to return true when x is
4 and 5. When x==5, however, the flip-flop flops back to false, and returns false for
the remaining values of x. The result is that this code prints 345.
.. or flip-flop is inherited from Perl which got it from AWK and sed in *nix. It's very powerful, but in your particular use it's fairly obscure and not a good choice for the logic you want, especially in Ruby. Instead use:
(1..10).each {|x| puts x if (3..5) === x }
Which outputs:
3
4
5
That said, it's extremely powerful when you need to extract a range of lines from a file:
File.foreach('/usr/share/dict/propernames') { |li| puts li if ($. == 5 .. $. == 7) }
Which outputs:
Agatha
Ahmed
Ahmet
Perl allows an even more-terse expression using only the line numbers of the currently read line (AKA $.) but Ruby doesn't support that.
There's also the option of using regular expressions, which behave similarly as the previous comparison:
File.foreach('/usr/share/dict/propernames') { |li| puts li if (li[/^Wa/] .. li[/^We/]) }
Which outputs:
Wade
Walt
Walter
Warren
Wayne
Wendell
Because regex work, it's possible to create a complex pattern to retrieve lines from a file based on matches. As the first, then the second pattern trigger, lines are captured. If, later in the file, another line triggers the first pattern, capturing will again occur until the second pattern matches. It's wonderfully powerful:
File.foreach('/usr/share/dict/propernames') { |li| puts li if (
li[/^Am/] .. li[/^An/] or
li[/^Wa/] .. li[/^We/]
)
}
Which outputs:
Amanda
Amarth
Amedeo
Ami
Amigo
Amir
Amos
Amy
Anatole
Wade
Walt
Walter
Warren
Wayne
Wendell
Or alternately, for our obscure-code speaking friends:
File.foreach('/usr/share/dict/propernames') { |li| puts li if (li[/^(?:Am|Wa)/] .. li[/^(?:An|We)/]) }
I find a piece of code to illustrate how the flip-flop works (just in the same book where this piece of code appears, hope it helps for those having same question like me)
$state = false # Global storage for flip-flop state
def flipflop(x) # Test value of x against flip-flop
if !$state # If saved state is false
result = (x == 3) # Result is value of lefthand operand
if result # If that result is true
$state = !(x == 5) # Then saved state is not of the righthand operand
end
result # Return result
else # Otherwise, if saved state is true
$state = !(x == 5) # Then save the inverse of the righthand operand
true # And return true without testing lefthand
end
end
Are you looking for an exclusive range? You can use three dots and the cover? method.
(1..10).each { |x| print "#{x}," if (3...5).cover?(x) }
The reason it prints 3,4,5, in your example is because it says if x is in the range from 3 to 5 print it.
To clarify the comment by #MurifoX, The flip-flop is true until x==5, and thus true specifically when x==5, but is false every time the expression is evaluated after that. Thus you are still seeing 5 being printed.
A flip-flop expression evaluates to true or false , just as comparison and equality expressions do.
The extraordinarily unusual thing about a flip-flop expression, however, is that its value depends on the value of previous evaluations. This means that a flip-flop expression has state associated with it; it must remember information about previous evaluations. Because it has state, you would
expect a flip-flop to be an object of some sort. But it isn’t—it’s a Ruby expression, and
the Ruby interpreter stores the state (just a single Boolean value) it requires in its internal parsed representation of the expression. With that background in mind, consider the flip-flop in the following code. Note that the first ".." in the code creates a Range object. The second one creates the flip-flop expression:
(1..10).each {|x| print x if x==3..x==5 }
The flip-flop consists of two Boolean expressions joined with the .. operator, in the
context of a conditional or loop. A flip-flop expression is false unless and until the
lefthand expression evaluates to true . Once that expression has become true , the expression “flips” into a persistent true state. It remains in that state, and subsequent
evaluations return true until the righthand expression evaluates to true . When that
happens, the flip-flop “flops” back to a persistent false state. Subsequent evaluations
of the expression return false until the lefthand expression becomes true again.
In the code example, the flip-flop is evaluated repeatedly, for values of x from 1 to
10. It starts off in the false state, and evaluates to false when x is 1 and 2 . When
x==3 , the flip-flop flips to true and returns true . It continues to return true when x is
4 and 5 . When x==5 , however, the flip-flop flops back to false , and returns false for
the remaining values of x . The result is that this code prints 345 .

Resources