Ruby Boolean assignment operator in a loop - ruby

Given the following code:
File.open('file1.txt', 'r') do |file|
while line = file.gets
puts "** " + line.chomp.reverse + " **"
end
end
I am confused to what is the question being asked? This is a simple piece of code I got off my tutorial, that reads a file's lines and puts it out. I do understand most of it, I believe you are assigning a variable line to the return value of file.gets, and it retrieves the value of those lines, and puts it out.
Where I am having trouble is the initial loop statement: while line = file gets
My question is that what kind of question are you asking and how does it break out of the loop?
i.e.:x=3 x ==3--> You are asking is X equal to 3, if true will return true, if false will return false.
Also, are you simultaneously assigning the return value of file.gets to the variable line, in addition to putting it in the while statement?

In Ruby everything evaluates to truthy or falsey.
There are two falsey things:
nil
false
Everything else is truthy.
The while loop checks for truthiness of line variable.
Until it is anything but either nil or false it loops.
In your example the loop will stop when file.gets returns nil, meaning, there's no next line.

What happens is that while is using the variable line as its condition. line = file.gets is assigned before while checks the condition. Additionally, while knows how to break out of the loop because at EOF file.gets returns nil which is false-y.

Related

In Ruby Why is the If/Then logic written without the If/Then structure failing?

Why does the following code fail to produce expected output, for the first two test cases if I don't add the "then" section to the the 'If?' I set the default value of the second variable "False" and I was under the impression that in Ruby a method could take an unspecified number of parameters, and the lack of a parameter when the method is called will roll back to using the default values of that parameters within the method if set.
def alphabetize(arr,rev=false)
arr.sort!
if rev == true
arr.reverse!
end
end
numbers = [1,9,2,1,10]
Test cases:
print alphabetize(numbers,false)
=begin
input: numbers,false
output: nil
expected output: 1,1,2,9,10
=end
print alphabetize(numbers)
=begin
input: numbers
output: nil
expected output 1,1,2,9,10
=end
print alphabetize(numbers,true)
=begin
input: numbers,true
output: 10,9,2,1,1
expected output: 10,9,2,1,1
=end
This code produced the expected results:
def alphabetize(arr,rev=false)
if rev == true
arr.sort!.reverse!
else
arr.sort!
end
end
numbers = [1,9,2,1,10]
You are printing the return value of the alphabetize method, which is not necessarily the value of the array.
In your first code, you have an if without a corresponding else as the last statement. In Ruby, the return value of an if statement without an else is nil, when the if condition fails.
In your second code, you have an if with an else statement. So the return value of the method, when the ifcondition fails, will be what gets executed inside the else block. In this case, arr.sort!.
It's worth mentioning the alphabetize method modifies the numbers array being passed in (indicated by the ! in the sort! and reverse! methods). If you printed numbers, instead of return value of alphabhetize, you would have the expected output as well.
def alphabetize(arr,rev=false)
arr.sort!
if rev == true
arr.reverse!
end
arr
end
Return array at the end when if false it return nil. To make sure the it returns the array value at the end of method.
Tested on rubyfiddle.com
http://rubyfiddle.com/riddles/c36bc/1

Making a sorted array of user's input

I'm learning Ruby with 'Learn to Program' by Chris Pine. On chapter 10 I should write a program where the user types as many words as he like and when he's done, he can just press Enter on an empty line and exit.
I came up with this:
puts "Type whatever you want!"
index = 0
word = ''
array = []
while word != nil
word << gets.chomp
array[index] = word
index = index + 1
end
puts ''
puts array.sort
But that doesn't work. What did I miss? Is there another way I could define word without having to repeat it?
The word will not have nil value. It will be an empty string. So you need to check for that:
while word != ""
# or even better
while !word.empty?
Also, you are adding everything to your word. You probably want to assign to it instead:
word = gets.chomp
Per author's comment:
begin
# your code here
end while !word.empty?
# OR more readable
begin
# your code here
end until word.empty?
It seems like there's a simpler solution, if I'm reading the question correctly.
You could do something like this:
user_input = gets.chomp.split(" ").sort
ex)
input: bananas clementine zebra tree house plane mine
output: ["bananas", "clementine", "house", "mine", "plane", "tree", "zebra"]
Here's a simple loop that you could do just for kicks:
arr = []
arr << $_.strip until gets =~ /^\s*$/
puts arr.sort
$_ is a special variable that evaluates to the last input read from STDIN. So basically this reads "Call gets and check if the input is just spaces. If it is then break out of the loop, otherwise append the last input with whitespace removed value onto the array and continue looping."
Or even more fun, a one liner:
puts [].tap {|arr| arr << $_.strip until gets =~ /^\s*$/}.sort
Basically same thing as above except using tap to initialize the variable.
To answer your questions:
Is there another way I could define word without having to repeat it?
Use side effects of assignment. In ruby when you assign a variable the return value of that assignment is the assigned variable, as in:
irb(main):001:0> (variable = 2) == 2
=> true
The idea would be to put the assignment in the your conditional. If I were to write something like this in a comprehensible loop, as opposed to those above, I'd write something like this:
arr = []
while !(word = gets.strip).empty?
arr << word
end
puts arr.sort
Using loop might simplify the code:
a = []
loop do
input = gets.chomp
if input.empty?
break
else
a << input
end
end
a.sort!
puts a

Break in While Loop when Specific Input is Entered

I am trying to halt input from a user when their input is 42.
The correct answer on the website I'm working on is:
while line = gets
break if (/42/ =~ line)
x << line
end
The code I tried that does not work is:
while line = gets.chomp
break if (line == 42)
x << line
end
Why is this the case? Am I missing some limitations to what I can use in my if statement?
The problem is that 42 is an integer, but line is a string:
1.9.3p392 :001 > "42" == 42
=> false
So it's never the case that your if statement is getting triggered, because it's comparing two different kinds of things. Matching with a Regex fixes it, though it's looking for "42" to appear anywhere in the input (e.g. "3427"). I think what you meant to say was
while line = gets.chomp
break if (line == "42")
x << lineĀ 
end
In other words, break when the input is a string with the characters 4 and 2 in it.
I suspect it's because you're comparing a number to a string. The example uses a regular expression it appears. "42" == 42 will give you false in ruby.
<< is a method(Append) on Array or String class objects. But your x not holding any referencing to such objects. Thus you are getting undefined local variable or method 'x' for main:Object (NameError).
Try this instead(by fixing local variable x to hold a practical object and converting line value to Fixnum object:
x = "hi"
while line = gets.chomp
break if (line.to_i == 42)
x << line
end
This program will help you to halt input from a user when their input is 42.
until (line = gets.chomp).to_i == 42
x << line
end
This of course bypasses the if statement you were asking about.
Your limitation for the if is based solely on the fact that you are comparing a string that will always be a string to a number, and this will never be equal. (as others have mentioned)
So we must reconsider the conditional statement. In this case, I considered it "out of place" and moved the comparison to the 'while' loop, and then inverted it to an 'until' statement, to be able to positively express the condition to end the loop on. Whenever I see a 'break' in a loop, I try to get rid of that smell, as the condition to leave a loop should really be expressed in the loop condition if possible.
I hope this helps.

what does nil mean/represent here?

Following are the simple statements in the irb shell. What does nilin the output mean ? Why does it accompany the print statement in the if block ?
irb(main):062:0> if(x==20 && y==30)
irb(main):063:1> print("if statement working !")
irb(main):064:1> else
irb(main):065:1* print("else statement working !!")
irb(main):066:1> end
if statement working !=> nil # what does nil represent here ?
In Ruby, all expressions return values, even if it's nil. Blocks and methods simply return the value of the last expression evaluated. There are many ways to use this effectively. For example, this is the reason explicit returns are often not used. Also, you can do this:
print if x == 20 && y == 30
'if statement working!'
else
'else statement working!'
end
Regarding your example: in addition to printing the string as you instructed, irb will display the value it received from the if-else blocks. Since print always returns nil, both branches will return the same value.
It means that your if-block does not return a value (which it can, actually). For instance, the following is perfectly legal and viable:
foo = if bar > 10
42
else
0
end
# now, foo is either 42 or 0

ruby push method is alternating

cool_words = []
while true
cool_words.push gets
break if gets.chomp == ''
end
puts cool_words
It is only pushing the first entry then the third and then the fifth. I think it is the way I have it breaking out of the loop because without the break method it doesn't happen.
I need it to break out of the loop when I hit enter on an empty line.
Thanks in advance!
You are calling gets twice in the loop. The first time it is being pushed into the array. The second time it is comparing against an empty string for loop breaking. But each time it is asking for a new line.
You only want to call gets one time per loop. So you can save it in a variable, and then use that variable multiple times later in the code.
cool_words = []
while true
line = gets
cool_words.push line
break if line.chomp == ''
end
puts cool_words
UPDATE: #MicahelKohl in the comments points out that you can accomplish the above task more elegantly like this:
cool_words = []
until (line = gets).to_s.chomp.empty?
cool_words << line
end
puts cool_words

Resources