Does ruby's case statement fall through? - ruby

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

Related

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).

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

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

Error in my ruby code [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I can't get this code to work. Can anyone help me by pointing out the error in this code so that I can understand where I made mistakes?
STDOUT.print 'Do you wish to input another length in meters? '
more = STDIN.getString
more = STDIN.getint( );
more = more.toUpper( )
while(more[1] = 'Y')
STDOUT.puts 'Enter length in meters: '
gets(meter)
f = meter * 3.28084
feet = f.toInt
inches = (12.0 * (feet - f)).to_i
print 'The length is '
if feet = 1
STDIN.print feet + 'foot ';
else
STDOUT.print feet + 'feet '
if inches = 1
STDOUT.print inches + ' inch.\n'
else if (inches < 1)
STDOUT inches + ' inches.\n'
else
STDOUT.print '.\n'
STDOUT.print 'Do you wish to input another length in meters: '
more = STDIN.getint
end
Where do I start?
In Ruby, you need to terminate your blocks with end. You did it for while, but not for if.
Ruby uses elsif; you can't write else if.
Ruby does not have toInt; it's called to_i, as you use in the very next line
gets(meter) is an error; you need to say meter = gets
STDIN does not have getString, it has gets. It also doesn't have getint, you need to write gets.to_i.
toUpper does not exist, use upcase, as in more = more.upcase. You can also use the more readable and more efficient more.upcase!.
In if and while, you have assignment =, where you presumably want to have comparison ==.
more[1] is the second character of more; the first being more[0].
more = ... is being called twice in a row. That means the first value you input will be discarded without effect.
STDIN.print is an obvious mistake for STDOUT.print.
You can use puts "..." instead of print "...\n".
STDIN and STDOUT are redundant when you are using gets, print and others; STDIN.gets is identical to gets, STDOUT.print is identical to print.
STDOUT inches + ' inches.\n' is an obvious mistake, since STDOUT is not a function.
'.\n' contains three characters: a period, a backslash and a letter. The double-quoted ".\n" contains two: a period and a newline.
Ruby does not typically use ;, and it does not usually use empty parentheses for calling 0-parameter functions. These are just stylistic errors, and won't impact runtime.
There may or may not be more.

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"

How to write a Ruby switch statement (case...when) with regex and backreferences?

I know that I can write a Ruby case statement to check a match against a regular expressions.
However, I'd like to use the match data in my return statement. Something like this semi-pseudocode:
foo = "10/10/2011"
case foo
when /^([0-9][0-9])/
print "the month is #{match[1]}"
else
print "something else"
end
How can I achieve that?
Thanks!
Just a note: I understand that I wouldn't ever use a switch statement for a simple case as above, but that is only one example. In reality, what I am trying to achieve is the matching of many potential regular expressions for a date that can be written in various ways, and then parsing it with Ruby's Date class accordingly.
The references to the latest regex matching groups are always stored in pseudo variables $1 to $9:
case foo
when /^([0-9][0-9])/
print "the month is #{$1}"
else
print "something else"
end
You can also use the $LAST_MATCH_INFO pseudo variable to get at the whole MatchData object. This can be useful when using named captures:
case foo
when /^(?<number>[0-9][0-9])/
print "the month is #{$LAST_MATCH_INFO['number']}"
else
print "something else"
end
Here's an alternative approach that gets you the same result but doesn't use a switch. If you put your regular expressions in an array, you could do something like this:
res = [ /pat1/, /pat2/, ... ]
m = nil
res.find { |re| m = foo.match(re) }
# Do what you will with `m` now.
Declaring m outside the block allows it to still be available after find is done with the block and find will stop as soon as the block returns a true value so you get the same shortcutting behavior that a switch gives you. This gives you the full MatchData if you need it (perhaps you want to use named capture groups in your regexes) and nicely separates your regexes from your search logic (which may or may not yield clearer code), you could even load your regexes from a config file or choose which set of them you wanted at run time.

Resources