Why do `while` loops never check user input, whereas `if` statements do? - ruby

An if statement such as:
if first_name || first_name.length == 0
puts "You can't leave the first name blank, try again: "
first_name = gets
end
functions correctly, because it asks for user input, and when tested by not entering any input (including spaces), it prints an error message, followed by prompting for user input, following which, However, when I attempt to use the same in a while loop:
while first_name || first_name.length == 0
puts "You can't leave the first name blank, try again: "
first_name = gets
end
the loop doesn't function correctly.
The loop does the following instead:
• Print the error message above (OK)
• Asks for user input (OK)
• As soon as user input is received, the loop executes once again (Wrong).
It isn't testing the length of the user's (now correct) input, and then allowing the rest of the application to carry out its tasks.
I also tried unless, nil?, and so on to rectify the loop's error using an if statement inside the loop, with no success. I thought the while loop would evaluate first_name again once the error was corrected, and find that its length was no longer zero, however, that turned out not to be the case.
In Java, to say something isn't equal to something else (in terms of strings, for example), I can use ! before the variable name. Can I do this in Ruby?

You can try this:
while first_name.length == 0 do
puts "You can't leave the first name blank, try again: "
first_name = gets
end
The way while cycle works is it evaluates the statement after while keyword. If it is true then it runs the block. And after that just the same. In your case statement is first_name || first_name.length == 0 which always will be true as first_name is always present. That's the reason it will loop again each time whatever you would input.

I think your condition is just wrong : the condition
first_name || first_name.length == 0
Will be true if first_name is non-nil, this will always be the case since you are setting it to a string.

After some further digging on the Internet, I gave .chomp a go, to see what it would do, as I found out that when gets is used, it takes your input, but apparently also inserts an \n escape sequence, when Enter is pressed, meaning that my loop didn't function as required.
By using gets.chomp, in conjunction with .empty? I was able to remove this escape sequence from my input, and have the loop function correctly.
Here's the correct code, which prevents an infinite loop:
first_name = gets.chomp
while first_name.empty? do
puts "You can't leave the first name blank, try again: "
first_name = gets.chomp
end

Related

Ruby Throw-Catch tutorial unclear

def promptAndGet(prompt)
print prompt
res = readline.chomp
throw :quitRequested if res == "!"
return res
end
catch :quitRequested do
name = promptAndGet("Name: ")
age = promptAndGet("Age: ")
sex = promptAndGet("Sex: ")
# ..
# process information
end
promptAndGet("Name:")
From https://www.tutorialspoint.com/ruby/ruby_exceptions.htm
When executed normally, it goes through name, age, sex, and back to name again, despite the prompt only asking for the name.
Why does this happen instead of "Name" just being asked?
That final line promptAndGet("Name") does not get immediately executed, since it's after the catch block.
The normal flow is that everything within the catch :quitRequested block gets executed immediately, in order. That's why you get all 3 prompts inside. If you answer all 3 prompts, you'll also get to the prompt on the last line.
If you answer ! to any of the three prompts, the block will terminate. So you will not get the remaining prompts inside the block.
You'll still get the prompt on the last line since it's outside of the catch.
throw is what terminates the catch block - not what initiates it.
Also, if you answer ! to that final prompt outside of the catch block, you'll get an error, because the throw was uncaught.

Ruby if/else flow

Good morning,
I'm starting on Ruby, wanted to create a small tool that fetches my public IP and sends it over by email. I'm stumbling on a basic problem with a string comparison and an if/else block that won't process.
Code is quite simple (see below). The problem comes at the string comparison at line 21. What I'd want is that whenever the IP address changed from what was recorded in a file, the new IP overwrites the one in the file, and other actions (not in the current code) will ensue.
It looks like that sometimes the comparison is not executed, thus the statements following the if do not execute.
I'd like to understand why is that. Line 21 has been changed at times for if (!oldIP == ip) to if (oldIP != ip), same result. I also suspect the return value to be ignored (dead code path ?) sometimes.
Here's the code
#!/usr/bin/env ruby
require "net/http"
puts "\e[H\e[2J"
def FetchIPAddress()
oldIP = ""
if File::exists?('/tmp/wanipaddress.txt')
iFile = File.open('/tmp/wanipaddress.txt')
oldIP = iFile.read()
iFile.close()
end
oFile = File.open('/tmp/wanipaddress.txt', "w+")
ip = Net::HTTP.get(URI("https://api.ipify.org"))
puts "old = " + oldIP
puts "new = " + ip
if (!oldIP == ip)
puts "changed"
oFile.puts ip
oFile.close()
else
ip = "unchanged"
puts "unchanged"
end
return ip
end
Really, I do see some erratic behaviour here; I suspect it's just me being a newbie with Ruby.
Your file likely contains a line break.
Try this
if old_ip.chomp != ip.chomp
...
end
chomp removes a trailing linebreak.
Best use p to print values for debugging, this will escape whitespace and thus make trailing linebreaks visible. You should never use puts for debugging.
And here is why !a == b will never ever work.
!a == b is the same as a.!().==(b) and executed as follows
first the ! method is called on object a, which returns false for a string
then ==(b) method is called on the resulting boolean
and since a boolean is never equal to a string the comparison will always fail
The problem with this line (if (!oldIP == ip)) is pretty simple - what you want it to do is check whether the oldIP is different from the new IP.
What you do instead is take oldIP, check whether it's true or false, then negate it (that's what the ! does), then compare it to ip. Since oldIP is a String, and thus always true, which gets negated to false, and ip is (I'm guessing) a String, it will always be true, your line essentially reads if (false == true).
To solve this problem, you could use the != comparison operator, like if oldIP != ip, or, if you like the negation, if !(oldIP == ip).

Does ruby's case statement fall through?

I am writing a hangman game in ruby and I wanted to use a case statement to determine which body part to place corresponding to a number of incorrect guesses. I made this game using a board class I use for other games like chess and connect-4 because I have a method which serializes the board class allowing me to save and load the game without any extra code. For the game to be saved, I needed some way of determining the number of incorrect guesses for the hangman without adding extra variables to the board class. To solve this I used an instance variable on the board class called history, which can be used to push moves from the game to the boards history. When the board gets serialized, the history is saved as well, which can be read by the game and used to determine incorrect guesses.
In the hangman game, I have a method called read history (which I use for all the games since it solves the serialization issue described above). The read_history method is responsible for reading the past guesses, display them, and determine the number of incorrect guesses. This number is then passed to a hang method which determines which body parts of the hangman to add.
def hang(incorrect)
case incorrect
when 0
#hangman = [" ", " ", " "]
break
when 7
#hangman[2][2] = '\\'
when 6
#hangman[2][0] = '/'
when 5
#hangman[2][1] = '*'
when 4
#hangman[1][2] = '\\'
when 3
#hangman[1][0] = '/'
when 2
#hangman[1][1] = '|'
when 1
#hangman[0][1] = 'o'
end
end
If I were writing this in java, and a value of 5 were passed to the above method, it would read the statement until it hit "when 5" or in java terms "case 5:". It would notice that there is not a break in the statement and will move down the list executing the code in "case 4:" and repeating until a break is found. If 0 were passed however it would execute the code, see the break, and would not execute and other statements.
I am wondering if Ruby is capable of using case statements the way java does in the way that they fall through to the next statement. For my particular problem I am aware that I can use a 0.upto(incorrect) loop and run the cases that way, but I would like to know the similarities and differences in the case statement used in ruby as opposed to the switch-case used in java
No, Ruby's case statement does not fall through like Java. Only one section is actually run (or the else). You can, however, list multiple values in a single match, e.g. like this site shows.
print "Enter your grade: "
grade = gets.chomp
case grade
when "A", "B"
puts 'You pretty smart!'
when "C", "D"
puts 'You pretty dumb!!'
else
puts "You can't even use a computer!"
end
It's functionally equivalent to a giant if-else. Code Academy's page on it recommends using commas to offer multiple options. But you can still won't be able to execute more than one branch of logic.
It does not fall through.
Ruby just doesn't have the same behavior as Java for this type of statement.
If you want to simulate the fall through behavior, you can do something like this:
def hang(incorrect)
#hangman = [" ", " ", " "]
#hangman[2][2] = '\\' if incorrect > 6
#hangman[2][0] = '/' if incorrect > 5
#hangman[2][1] = '*' if incorrect > 4
#hangman[1][2] = '\\' if incorrect > 3
#hangman[1][0] = '/' if incorrect > 2
#hangman[1][1] = '|' if incorrect > 1
#hangman[0][1] = 'o' if incorrect > 0
#hangman
end

struggling with my pseudocode

I'm about to build a program written in pseudocode. I've already done most of the work , but I'm stuck on the code and I don't know what to do exactly. im a begginer and not everything is clear to me ...in one of the tasks i have to do , i have to make the program ask for the players name , which will be stored as a string then the program has to check if it exceeds the limit between 2/20 characters and inform the user if the input is wrong . i have researched and tried to figure out how i might be able to fix my code but i have a really short amount of time left and coudn't find anything regarding my problem :/ . this is the code ive done for this specific task. i know its wrong but i just dont know how to fix it . any help with be much appreciated . Thanks in advance :)
pseudocode:
// Getting user's name
valid = false
loop until valid is equal to true
Output" please enter your name "
Input playName
If (playName is => 1)AND(=<20)then
Valid = true
Otherwise
output "name exceeds the character limit"
I'm not sure what the syntax of your pseudo code is but :
assuming tabulation has meaning, you may have forgot to indent some lines to include them in the loop
'valid' is first declared with a lower case first letter so you may continue referencing it same way in line "Valid = true" -> "valid = true"
In the 'If' you want to test the lenght of the String and not compare the string to an int so maybe call a function length(String) that would return the length of the string or access a string.length attribute (as you wish in pseudo code)
You want the playName to be superior or equal to 2 "length(playName) >= 2" and inferior or equal to 20 "length(playname) <= 20"
The commonly used keyword meaning Otherwise is 'Else' as in
IF (Condition) THEN (code) ELSE (code)
I may modify you code like this :
// Getting user's name
valid = false
loop until valid is equal to true
Output" please enter your name "
Input playName
If (length(playName) >= 2) AND (length(playName) <= 20)
Then
valid = true
Else
output "name exceeds the character limit"

Ruby: String Comparison Issues

I'm currently learning Ruby, and am enjoying most everything except a small string comparason issue.
answer = gets()
if (answer == "M")
print("Please enter how many numbers you'd like to multiply: ")
elsif (answer. == "A")
print("Please enter how many numbers you'd like to sum: ")
else
print("Invalid answer.")
print("\n")
return 0
end
What I'm doing is I'm using gets() to test whether the user wants to multiply their input or add it (I've tested both functions; they work), which I later get with some more input functions and float translations (which also work).
What happens is that I enter A and I get "Invalid answer."The same happens with M.
What is happening here? (I've also used .eql? (sp), that returns bubcus as well)
gets returns the entire string entered, including the newline, so when they type "M" and press enter the string you get back is "M\n". To get rid of the trailing newline, use String#chomp, i.e replace your first line with answer = gets.chomp.
The issue is that Ruby is including the carriage return in the value.
Change your first line to:
answer = gets().strip
And your script will run as expected.
Also, you should use puts instead of two print statements as puts auto adds the newline character.
your answer is getting returned with a carriage return appended. So input "A" is never equal to "A", but "A(return)"
You can see this if you change your reject line to print("Invalid answer.[#{answer}]"). You could also change your comparison to if (answer.chomp == ..)
I've never used gets put I think if you hit enter your variable answer will probably contain the '\n' try calling .chomp to remove it.
Add a newline when you check your answer...
answer == "M\n"
answer == "A\n"
Or chomp your string first: answer = gets.chomp

Resources