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
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'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
Issue:
def average_of_three(num1, num2, num3)
puts "Enter first number"
num1= gets.to_i
puts "Enter second number"
num2 = gets.to_i
puts "Enter third number"
num3 = gets.to_i
avg=0
avg = (num1 +num2 + num3 )/3
puts "your average is#{avg}"
end
Solution:
I was missing the following statement :
average_of_three(10, 20, 30)
This code should work if you call that method, but the method doesn't need arguments. Those variables are declared locally anyway.
Right now you'd do:
average_of_three(0, 0, 0)
Where the initial values don't matter as you ignore them anyway. They could be :zero or "Who cares!" just the same.
This isn't very Ruby code though. When writing Ruby think first in terms of data structures.
For example, to get three values:
def get_n(n = 3)
n.times.map do |i|
print "Enter number #{i+1}: "
gets.to_i
end
end
This asks a series of questions and stores the result in an array, that's what map does.
Now you can average those:
def average(values)
return unless (values.any?)
values.sum / values.length
end
This has a guard clause where it won't execute unless there's at least one value. Then it calls sum, which not surprisingly adds up all the values. Divide by the length and you're done.
So you'd tie this all together with:
values = get_n
puts "Average is: #{average(values)}"
Note: In Ruby when you divide an integer by an integer you always get an integer. This means the values get rounded. If you'd prefer to have fractional results you can switch the input converter from .to_i to .to_f. That change will cause floats to flow through the rest of the code.
So I've written this piece of code to play around with numbers and the until loop:
number = rand(10)
puts number
puts "Guess the number"
guess = gets.chomp
until guess == number
puts "Guess again!"
guess = gets.chomp
end
puts "You've guessed it right! The number is #{guess}
But for some reason it it always stuck in the until loop and I am not sure why. I puts the random number to know that i guess right and try out the code. I'm completely new to Ruby, So I guess it's a very obvious thing I am missing, but I just dont see it.
From my point of view, whenever I prompt for the guess again, that guess that validated by the until loop with until guess == number.
Who can help me clear this up?
The reason is simple, and that is you're failing to cast the user input into an integer.
i.e. if I write
number = gets.chomp
and I type 1 for the number, then the number variable will equal the string "1", not the integer 1.
To fix this, just use guess = gets.chomp.to_i
Instead of guess = gets.chomp(which will return a string of the user input), use guess = gets.to_i (which will convert user input into integer)
to_i method will convert the element into integer and will drop /n character since it's not part of the integer. Don't need to add .chomp method.
I am creating a program that accepts ten numbers from a user. Then, it displays the total of the ten numbers, their average, and the smallest and largest numbers. Finally, it is supposed to display the word 'Jackpot!' for every number entered that is of the same or greater value than 100, and 'Tough Luck.' for every number less than 100.
My code does not seem to be working and will not run in Ruby.
puts 'Please enter 10 numbers one at a time.'
num1=gets.chomp
num2=gets.chomp
num3=gets.chomp
num4=gets.chomp
num5=gets.chomp
num6=gets.chomp
num7=gets.chomp
num8=gets.chomp
num9=gets.chomp
num10=gets.chomp
value_list=[num1, num2, num3, num4, num5, num6, num7, num8, num9, num10]
subtotal=0
for x in 0..9
subtotal=subtotal+value_list[x]
puts 'Total: ' + subtotal
average=subtotal/10.to_f
average=sprintf("%.2f",average)
puts 'Average: ' + average
puts 'Smallest value: ' + sprintf("%.2f",value_list.min.to_s)
puts 'Largest value: ' + sprintf("%.2f",value_list.min.to_s)
if num1..num10 >=100
puts 'Jackpot!'
else
puts 'Tough Luck.'
sleep 120
A few things:
You need to end your blocks: for ... end, if ... else ... end, etc. This is the main reason it won't run, i.e. the parser doesn't recognize it as valid syntax.
gets.chomp returns a string, but you are looking for integers. Try gets.chomp.to_i to force integer types.
'Total: ' + subtotal: you would be adding an integer to a string, which is not allowed. Ruby string interpolation goes like this (double quotes required): "Total: #{subtotal}". This will transform the subtotal variable into a string and insert it into the other string on the fly.
I'm not sure what you're trying to do here: if num1..num10 >= 100. Is this if any member of the range is greater than 100, or if the sum is greater than 100?
There's also a lot of optimization you can do here. One good opportunity is when you have a lot of repeated code, as in num1 = gets.chomp.to_i; num2 = gets ... etc.
Here's a version of your program that (I think) does what you want it to. If you're serious about learning Ruby, go look up the documentation for some of these methods (<<, inject, .times etc):
puts 'Please enter 10 numbers one at a time.'
value_list = []
10.times do
value_list << gets.chomp.to_i
end
subtotal = value_list.inject(:+)
puts "Total: #{subtotal}"
puts "Average: #{(subtotal/10.0).round(2)}"
puts "Smallest value: #{value_list.min}"
puts "Largest value: #{value_list.max}"
if subtotal >= 100
puts 'Jackpot!'
else
puts 'Tough Luck.'
end
Hope this helps.
Your code need some end statements, and you need to convert the input to numbers before calculation.
You also need to close the for at the end and the if before the 'Jackpot!'.
You can convert all of the strings input using map:
value_list = value_list.map(&:to_i)
Where you calculate the subtotal you need to covert its data to a string, or just interpolate the data:
puts "Total: #{subtotal}"
After this you need to redo if num1..num10 >=100. It compares 100 to a Range which is wrong.