I'm trying to make users input a positive integer(>=1). If the user inputs a negative integer or 0, the program gonna notice and say you didn't put a valid integer, please try again. my program works so far until I try to input a string. My program break after I input a string. Does anybody know how to fix this?
I tried elsif !barsofsoap.match (/\A[-+]?[0-9]*.?[0-9]+\Z/)
puts "You didn't enter an integer, please put a positive integer", but it didn't work
loop do
puts "How many soaps are you looking to purchase? (Please put an integer)"
x = gets.chomp
barsofsoap = Integer(x) rescue false
if barsofsoap >= 1
break
else
puts "You didn't put an integer, please put an integer and try again."
end
end
I hope the program will not break if the user inputs a string.
The reason why your current code fails is because you use false as rescue value. You can't compare false >= 1. Sticking close to your original code you might wrap the comparison into a begin/end block.
loop do
puts "How many soaps are you looking to purchase? (Please put an integer)"
x = gets.chomp
begin
barsofsoap = Integer(x)
if barsofsoap >= 1
break
end
rescue ArgumentError => _error
puts "You didn't put an integer, please put an integer and try again."
end
end
Or simplifying the code:
loop do
puts "How many soaps are you looking to purchase? (Please put an integer)"
break if (barsofsoap = gets.to_i).positive?
puts "Invalid input. Provide a positive integer."
end
The above code doesn't detect if the input is a string or an integer. By replacing Integer(x) with x.to_i it would simply return 0 if invalid. Since you want the user to provide an positive integer it still requests them to provide a new integer.
nil.to_i #=> 0
''.to_i #=> 0
'asdf'.to_i #=> 0
'12.34'.to_i #=> 12
Like shown above the string '12.34' will produce the value 12. Depending on your requirements you might not want to use this value and instead mark it as invalid input. In this case you could use a regex to check the provided input.
loop do
puts "How many soaps are you looking to purchase? (Please put an integer)"
input = gets
break barsofsoap = input.to_i if input.match?(/\A[1-9]\d*\Z/)
puts "Invalid input. Provide a positive integer."
end
The regex /\A[1-9]\d*\Z/ will match the following:
\A Matches start of string.
[1-9] Matches one of the digits 1-9.
\d* Matches zero or more digits 0-9.
\Z Matches end of string. If the string ends with a newline, it matches just before the newline.
You try this: num.positive?
see here: https://ruby-doc.org/core-2.6/Numeric.html#method-i-positive-3F
Also you can try convert the text to an integer and validate by doing this:
num.is_a?(Integer)
https://ruby-doc.org/core-2.6/Object.html#method-i-is_a-3F
Or even better since input is always text, you can use a regex to make sure only numbers are entered: "12345" =~ /\A\d+\Z/
How about try this:
loop do
puts "How many soaps are you looking to purchase? (Please put an integer)"
barsofsoap = gets.chomp
if barsofsoap.match(/^[\d]+$/).nil?
puts "You didn't put an integer, please put an integer and try again."
elsif barsofsoap.to_i.positive?
break
else
puts "You didn't put an integer, please put an integer and try again."
end
end
Other option, using n.to_i.to_s == n to check the input is an Integer and moving the loop into a method:
def get_input_integer
loop do
puts "Enter a positive Integer"
n = gets.chomp
if n.to_i.to_s == n
return n.to_i if n.to_i.positive?
puts "It's not positive"
else
puts "It's not an integer"
end
end
end
x = get_input_integer
Related
I have to write a program which asks the user to enter a number.
The program keeps on asking the user for a number until the user types 'Stop'
at which point the sum of the numbers that the user has entered should be printed.
I've tried many,many things and none of my ideas work.
This is what I have - but I can that it isn't correct. What am I doing wrong?
I've only used while loops and arrays
total_user_input = []
# As long as the user inputs a number, the program will keep putting Give me a number
# and then adding that number to the total_user_input array.
puts "Give me a number: "
while user_input = gets.chomp.to_i
#add the input to the array total_user_input
total_user_input.push(user_input.to_i)
puts "Give me a number: "
# If the user however types stop, then the loop is broken and we jump down to the
# sum bit - where all of the numbers in the total_user_input array are added together
# and printed. End of program!
if user_input == "stop"
break
end
sum = 0
total_user_input.each { |num|
sum += num
}
puts sum
end
The output isn't as it should be.
As others have identified the problems with your code let me suggest how you might reorganize it. Ruby provides many ways to execute loops but you many find it desirable to primarily relay on the method Kernel#loop and the keyword break. (As you will learn in time, loop is particularly convenient when used with enumerators.)
def sum_numbers
tot = 0
loop do
print 'Gimme a number: '
s = gets.chomp
break if s == 'Stop'
tot += s.to_i
end
tot
end
The keyword break can optionally take an argument (though why that is not mentioned in the doc I cannot say), in which case it (if a literal) or its value (if a variable or method) is returned by loop. Here one would generally see
break tot if s == 'Stop'
without the final line, tot. As the loop returns tot and that is the last calculation performed by the method, the method will return the final value of tot.
You could have instead written
return tot if user_input == 'Stop'
but I think most coders believe best practice dictates that one should not return from a method from within a loop (or from within nested loops) unless there is a good reason for doing so.
Some small points:
I used print rather than puts to that the user's entry will be shown on the same line as the prompt.
I used s (for "string") rather than user_input because it reduces the chance of spelling mistakes (e.g., user_imput), speeds reading, and (possibly a foible of mine), looks neater. True, s is not descriptive, but one only has to remember its meaning for three consecutive lines of code. Others may disagree.
You could write, break if s.downcase == 'stop' if you want, say, 'stop' or 'STOP' to have the same effect as 'Stop'.
'23O3'.to_i #=> 23 (that's an an oh, not a zero), so in real life you'd want to confirm that either 'Stop' or the string representation of a number had been typed.
This is how I would do this preferring to use loop do end syntax with a break when it should. Also added a bit more text so user knows what's happening.
total_user_input = []
puts 'Give me a number or "stop" to end: '
loop do
user_input = gets.chomp
total_user_input << user_input.to_i
puts "Give me a number: "
break if user_input.downcase == "stop"
end
puts "Total entered: #{total_user_input.inject(&:+)}" unless total_user_input.empty?
puts 'goodbye!'
Note these few things:
get.chomp.to_i will convert every input to integer. ("stop" or any non integer string will be 0)
Arrangement of the flow is quite messy.
total_user_input = []
puts "Give me a number: "
while user_input = gets.chomp.strip
total_user_input.push(user_input.to_i)
sum = 0
total_user_input.each { |num|
sum += num
}
puts sum
if user_input == "stop"
break
end
end
Hope you understand this.
I am trying to solve the "24" game. The point of the game is to generate 4 random integers from 1-9 and ask the player to use addition, subtraction, multiplication, or division to get the number 24. My code runs fine until a player enters a number, and then I get "Command not found". Can someone please take a look at this:
def evaluate (input,solved_v)
input = eval (input.to_f)
#convert to a float and then evaluates it; it computes
if input == 24
solved_v = true
puts "great job! you did it!"
else
puts "please try again"
end
end
def test_entry (input)
if input.scan(%r{[^\d\s()+*/-]}).empty?
#this scan detects letters and special characters because only numbers work
true
else
false
end
end
puts
puts "try to use +, -, / or * to"
puts "get 24 from the integers provided"
puts
series = (1..4).collect{ rand(1..9)}
#generates 4 random numbers between 1 and 9
for i in series
puts i
end
puts "Please guess"
solved = false
unless solved = true
user_input = gets.chomp
if test_entry(user_input) == true
evaluate(user_input)
else
puts "invalid characters entered"
puts "please try again"
puts
end
end
There are numerous problems with your program.
Don't put spaces between your method names and parentheses.
eval takes a string argument, not a float.
Ruby passes arguments by value, so solved_v isn't going to get
returned. Make it the return value of your evaluate method. I'd also
suggest renaming your methods to express their boolean intent. See below...
Don't check boolean expressions for equality to true or false, just use them.
def correct?(input)
if eval(input) == 24
puts "great job! you did it!"
true
else
puts "please try again"
false
end
end
def good_entry?(input)
input.scan(%r{[^\d\s()+*/-]}).empty?
end
and they get used as follows
while true
user_input = gets.chomp
if good_entry?(user_input)
break if correct?(user_input)
else
...
end
end
Finally, note that you're not actually checking that the input provided by the user uses only the supplied random numbers.
The below ruby code is not working as expected. It doesn't seem to recognise the 1..3 range and is only reading the else condition. If I gave a number instead of a range, it works though. Not sure where I'm going wrong.
print "Enter your cost: "
cost = gets.chomp
case cost
when 1..3
puts "inexpensive"
when 3..5
puts "affordable"
else puts "no comments"
end
You're trying to match a string against an integer range. That's not going to work. Make an integer.
cost = gets.chomp.to_i
The input you get from gets is always a string, so it will never match a number range. To convert it to an integer, do this:
cost = gets.to_i
You can directly put it in case statement like so
case gets.to_i
I'm trying to validate that the input a user gives my program via gets is an integer. is_a?(Integer) does not work, as far as i can tell, because gets gets a string from the user, so it will always return false even if the user enters an valid integer (in string form). One would think I could simply use to_i on the input and be done with it, but that raises another issue - "75akjfas".to_i results in 75. So if I relied on to_i to solve my problems, anything starting with numbers will work.
How do I cleanly validate that the value is an integer only, and not a combination of numbers and letters? Or do I need to resort to regex for this?
print "Enter an integer (or try to sneak by something other): "
puts Integer(gets) rescue puts "Hey, that's not an integer!"
How about s.to_i.to_s == s? I'd prefer regex however.
Using regex you could do it like this:
class String
def integer?
!!(self =~ /^[-+]?[0-9]+$/)
end
end
You could use Integer() but you need to be careful with hex, binary, or octal input. http://ruby-doc.org/core-2.3.1/Kernel.html#method-i-Integer
def valid_integer?(string)
begin
!!Integer(string)
rescue ArgumentError
false
end
end
Check this code example for how to use the checked string input by the user
puts "Enter a number: "
user_input = gets.chomp
check = (user_input.to_i.to_s == user_input)
while (!check ) do
puts("Wrong Input, Pls, Enter a number: " )
user_input = gets.chomp
check = (user_input.to_i.to_s == user_input)
end
if user_input.to_i < 0
puts "Number is negative"
elsif user_input.to_i > 0
puts "Number is positve"
else
puts "Number is Zero"
end
Ruby can do it without esoteric solutions:
Integer is a integer
1970.is_a?Integer
true
String is not a integer
"1970".is_a?Integer
false
String to integer is a integer
"1970".to_i.is_a?Integer
true
I'm messing around in Ruby some more. I have a file containing a class with two methods and the following code:
if __FILE__ == $0
seq = NumericSequence.new
puts "\n1. Fibonacci Sequence"
puts "\n2. Pascal\'s Triangle"
puts "\nEnter your selection: "
choice = gets
puts "\nExcellent choice."
choice = case
when 1
puts "\n\nHow many fibonacci numbers would you like? "
limit = gets.to_i
seq.fibo(limit) { |x| puts "Fibonacci number: #{x}\n" }
when 2
puts "\n\nHow many rows of Pascal's Triangle would you like?"
n = gets.to_i
(0..n).each {|num| seq.pascal_triangle_row(num) \
{|row| puts "#{row} "}; puts "\n"}
end
end
How come if I run the code and supply option 2, it still runs the first case?
Your case syntax is wrong. Should be like this:
case choice
when '1'
some code
when '2'
some other code
end
Take a look here.
You also need to compare your variable against strings, as gets reads and returns user input as a string.
Your bug is this: choice = case should be case choice.
You're providing a case statement with no "default" object, so the first clause, when 1, always returns true.
Effectively, you've written: choice = if 1 then ... elsif 2 then ... end
And, as Mladen mentioned, compare strings to strings or convert to int: choice = gets.to_i