"Bug" in if statement parameters Learn Ruby The Hard Way - ruby

On line 7 of http://learnrubythehardway.org/book/ex35.html, there is a comment stating there is a bug.
When this program runs and you choose a number which contains, neither a "0" nor a "1" it will say it's not a number. How do I get it to detect all numbers?
Here is the code:
def gold_room
puts "This room is full of gold. How much do you take?"
print "> "
choice = $stdin.gets.chomp
# this line has a bug, so fix it
if choice.include?("0") || choice.include?("1")
how_much = choice.to_i
else
dead("Man, learn to type a number.")
end
if how_much < 50
puts "Nice, you're not greedy, you win!"
exit(0)
else
dead("You greedy bastard!")
end
end

You can use regular expression here. \d means any digit, + means one or more times, =~ is a pattern match operator, so:
if choice =~ /\d+/
how_much = choice.to_i
else
dead("Man, learn to type a number.")
end

Related

My Ruby calculator program acts like I didn't enter a valid operator

I have started learning Ruby today and so far I am loving the easy to read syntax! However, I have made a calculator app to learn the language and am struggling to get it to work. Below is the code for the app.
def add(first_number, second_number)
first_number + second_number
end
def subtract(first_number, second_number)
first_number - second_number
end
def multiply(first_number, second_number)
first_number * second_number
end
def divide(first_number, second_number)
first_number / second_number
end
def mod(first_number, second_number)
first_number % second_number
end
def main()
puts "First number: "
first_number = gets
puts "Operator (+, -, *, /, %): "
operator = gets
puts "Second number: "
second_number = gets
if operator == "+"
result = add(first_number, second_number)
elsif operator == "-"
result = subtract(first_number, second_number)
elsif operator == "*"
result = multiply(first_number, second_number)
elsif operator == "/"
result = divide(first_number, second_number)
elsif operator == "%"
result = mod(first_number, second_number)
else
result = "Invalid operator. Please try again."
end
puts "Result: " + result
end
main()
It keeps going to the else and printing invalid operator and I am unsure why. Am I making a stupid mistake, or am I going about this program the wrong way? Thank you in advance and sorry for such a simple question, I have only started learning Ruby today :)
The answer is quite simple: gets isn't doing what you think. It's preserving the trailing newline.
irb(main):003:0> gets
hello
=> "hello\n"
You need to get rid of the trailing newline.
irb(main):004:0> gets.chomp
hello
=> "hello"
You can see the impact of this on comparisons:
irb(main):013:0> gets.chomp == "hello"
hello
=> true
irb(main):014:0> gets == "hello"
hello
=> false
If you want to get rid of all leading or trailing whitespace, you may want to use #strip.
irb(main):015:0> gets.strip
+
=> "+"
You'll also need to convert your inputs for operands to numbers.
first_number = gets.to_f
When converting this way, there is no need to chomp off the trailing newline, though you could: gets.chomp.to_f.
This program is way longer and more complex then it needs to be. In Ruby operators are just implemented as methods so you can use Object#send to perform all of these simple mathematical operations:
# This is Ruby - not C/Java/Rust etc - there is no need for a "main" method.
puts "First number: "
first_number = gets.chomp.to_f
# Get the user input in a loop until the user provides a valid operator
operator = loop do
puts "Operator (+, -, *, /, %): "
operator = gets.chomp
if ["+", "-", "*", "/", "%"].include?(operator)
break operator
else
puts "Invalid operator. Please try again."
end
end
puts "Second number: "
second_number = gets.chomp.to_f
puts "Result: " + first_number.send(operator, second_number)
If you want to be able to take more advanced input and map it to methods in this way using a lookup table (a hash) as suggested by Tadman is a good idea.

How do you get the output in Ruby to sort in even and odd numbers?

I got this assignment in Codecademy. I want to print the even numbers.
print "Print any number:"
inpt = gets.chomp
def odd_or_even(num)
if num % 2 == 0
print "even"
else
print "odd"
end
end
I do not get any output. Is the problem in the method, or how I've written the equation? I've tried changing both.
You defined the method odd_or_even but never actually call it.
You have two options:
Take the more script-like approach and work with the input without the use of methods:
print 'Print any number: '
input = gets.to_i
# ^ convert the input (which is always a string) to an integer
if input % 2 == 0
puts 'even'
# ^ is the same as `print` but adds a newline character at the end
# (unless the string ends with a newline character)
else
puts 'odd'
end
If you want to use a method you'll have to define it and call it as well:
def odd_or_even(num)
if num % 2 == 0
puts 'even'
else
puts 'odd'
end
end
print 'Print any number: '
input = gets.to_i
odd_or_even(input)
# ^ method is called here
Ruby also has a lot of build-in helpers. You can achieve the same result with the following two lines:
print 'Print any number: '
puts gets.to_i.odd? ? 'odd' : 'even'
references:
#gets get user input
String#to_i convert input to an integer
Integer#odd? check if the integer is odd
Ternary if: use inline of if expression ? true : false
I think you have to check your input data.
Otherwise it may be a situation:
'abc'.to_i.even? #=> true
It's because non-digital String#to_i returns 0.
As you see it's not good.
def odd_or_even(number)
number.odd? ? 'odd' : 'even'
end
puts 'Print any number:'
input = gets.chomp
if input =~ /\D/
puts 'It is not a number'
else
puts "It's #{odd_or_even(input.to_i)}"
end
Firstly we validate data using regex. If it's not a number we will not check is it odd or even.
Note:
Very often we use =~ operator to match String and Regexp.
/\D/ means any non-digit character.
i = 20
loop do
i -= 1
next if i % 2 == 1
print "#{i}"
break if i <= 0
end
I see two issues with what you've posted - first gets.chomp is going to return a string and you really want an integer, so you'd want to do gets.to_i (or Integer(gets) if you want to guard against non-numeric inputs). The second is that print doesn't include a line break, so that could be the source of your "no output" issue - maybe try switching to puts instead?

How do I check if user input is an integer in Ruby?

I am trying to loop until user inputs an integer. When user inputs a letter, the following code should print "Think of a number":
print "Think of a number "
while user_input = gets.to_i
if user_input.is_a? Integer
puts "your number is #{user_input}"
break
else
print "Think of a number "
end
end
I succeeded with my code when user inputs an integer. However when user inputs a string, the to_i method returns 0, and does not execute the else statement because it is a number.
The main issue with your code is String#to_i method is omnivorous.
"0".to_i #⇒ 0
"0.1".to_i #⇒ 0
"foo".to_i #⇒ 0
That said, user_input in your code is always integer.
What you probably want is to accept digits only (and maybe a leading minus for negatives.) The only concise way to accept a subset of characters is a regular expression.
# chomp to strip out trailing carriage return
user_input = gets.chomp
if user_input =~ /\A-?\d+\z/
...
The regular expression above means nothing save for digits with optional leading minus.
Or, even better (credits to #Stefan)
if gets =~ /\A-?\d+\Z/
If you only want to accept postive digits, you can use a range:
user_input = gets.chomp
if ('0'..'9').cover? user_input
let check below one used Integer(gets.chomp) rescue ''
print "Think of a number "
while user_input = Integer(gets.chomp) rescue ''
if user_input.is_a? Integer
puts "your number is #{user_input}"
break
else
print "Think of a number "
end
end
I came across a similar problem. I ended up doing this:
if user_input.strip == user_input.to_i.to_s
# More code here!
end
Testing for float would be:
if user_input.strip == user_input.to_f.to_s
# More code here!
end
Solved my issue. See if it helps.

Ruby detects variable is string, but ends the script without breaking loop [duplicate]

I need to check whether my variable is an Integer or a String.
The code below will just break the loop, without warning me for an illegal character. Can anyone help me to find the mistake?
x = 0
while x == 0
name = gets.chomp.capitalize
if name.empty?
puts "No input. Try again"
elsif name.is_a? Integer
puts "Illegal character: Integer "
else
x = 1
end
end
Because gets returns a string you need to find out if the string represents a number (and only a number).
First, translate your string to an integer with to_i. Please note that to_i returns 0 for strings that do not include numbers. In a second step check if translating this integer back into a string matches the original string
string.to_i.to_s == string
Note that this is just a simple example, it wouldn't work for example with the string 00.
Another way might be checking if the string only contains numbers. That could be done by using a regexp:
string.match(/\A\d+\z/)
You can do something like this:
loop do
puts "Enter name"
name = gets.chomp
if name.empty?
puts "No input, try again"
elsif name.scan(/\d+/).any?
puts "Illegal character: Integer"
else
raise StopIteration
end
end
case-expression
Or use a case-expression to tidy things up.
loop do
puts "Enter name"
case gets.chomp
when ''
puts "No input, try again"
when /\d/
puts "Illegal character: Integer"
else
raise StopIteration
end
end
See String#scan, Array#any? and StopIteration for further details

how to meet a condition before going on to the next step?

how do I loop if a condition is not met?
print "Please enter first number "
first_number = gets.chomp
if first_number =~ /[a-zA-Z]/
puts "not a number"
end
As per the code posted above, if you enter a letter, you'll get the statement of it not being a number.
How do I repeat it, if a user enters a letter?
As of now, it goes to the next one which is this:
print "Please enter second number "
second_number = gets.chomp
if second_number =~ /[a-zA-Z]/
puts "not a number"
end
I don't want it to it to go to the next one, until the user has entered a number in the first one.
You can use while and until as a modifier to a block. This will run the block first and then check a conditional and run the block again until it passes, which is the behavior you want.
begin
puts 'Please enter first number'
first_number = gets.chomp
end until first_number =~ /\d+/
I suggest you consider doing it like this:
num = nil # initialize to anything
loop do
puts 'Enter a number'
num = gets.chomp.strip
case num
when /^\d+$/
break
when /^[a-z]+$/i
print "You entered one or more letters and no digits."
else
print "You made some other illegal entry."
end
puts " Try again"
end
puts "You enterered the number #{num}"
Some notes:
num must be initialized (to anything) before the loop in order for it to be visible after the loop's end statement.
the case statement, since it uses === for evaluating when expressions, allows you to enter a regex for each case.
^ and $ in the regexes are anchors, so that, for example, "34b" =~ /^\d+$/ => nil (what I assume you want), rather than "34b" =~ /\d+/ => 0.
the i in /[a-z]+$/i allows matching letters to be uppercase or lowercase.
the user may enter one or more digits, or one or more letters, but there are many other possibilities as well ("3$,a"), so I added another "catch-all" possibility in the case statement.

Resources