Cant figure out where I am making mistake - ruby

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.

Related

Simple program but so very stuck- Loops in Ruby

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.

Code should return highest of two numbers entered by the user

This is the code.
puts "Give me a number"
one = gets.chomp
puts "Give me another number"
two = gets.chomp
if one > two
puts "This is the bigger number #{one}"
else
puts "This is the bigger number #{two}"
end
I don't know where my fault is.
you didn't change them to integer, gets.chomp gives you string.
puts "Give me a number"
one = gets.chomp.to_i
puts "Give me another number"
two = gets.chomp.to_i
First, you are gathering input from an external user of your "application", you then have to decide how you want to manage wrong input and conversion.
In any case, gets will return a String
You are going for numbers, but are they Integer, Float ?
Since String is Comparable, your original code will run but will probably not yield the expected returns.
Let's say you are only interested in Integers.
You will have three conversion functions to consider :
Kernel#Integer (which is included in the top-level binding)
String#to_i.
String#to_i will never return an error.
'abcd'.to_i #=> 0
'1.2'.to_i #=> 1
nil.to_i #=> 0
Kernel#Integer will return an error if the String is not a valid Integer. (which I generally find more appropriate for user inputs).
Integer('0') #=> 0
Integer('abcd') #=> ArgumentError
Integer(nil) #=> TypeError
Finally, you will have to compare your inputs. I personnally would use Array#max since it conveys optimal readability (at the expense of performance still, but you do not seem to be in a performance critical system).
My final code would look like this (without error handling):
inputs = []
2.times do
puts 'Give me a number'
inputs << Integer(gets.chomp) # or use gets.chomp.to_i
end
puts "This is the bigger number : #{inputs.max}"
More refactoring can be done but I don't think it would serve for beginners in Ruby.

Input string does not match a numerical range

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

How can I create a program to accept ten numbers from a user?

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.

Misbehaving Case Statement

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

Resources