ZeroDivisionError in Ruby Program - ruby

I am trying to teach myself Ruby using "Computer Science Programming Basics in Ruby" and other sources. I am stuck on a question and this book does not provide solutions.
The exercise is to write a program that given two points on a 2d graph outputs a message describing the line (horizontal or vertical) or it's slope (positive or negative). This is what I have so far.
# Get the first point from a user
puts "Please enter Point A's X value."
x_1 = gets.to_i
puts "Please enter Point A's Y value."
y_1 = gets.to_i
# Get the second point from a user
puts "Please enter Point B's X value."
x_2 = gets.to_i
puts "Please enter Point B's Y value."
y_2 = gets.to_i
slope = ((y_1-y_2) / (x_1-x_2))
#Check to see if the line is vertical or horizontal and the slope is +ve or -ve
case
when (slope == 0) then
puts "The line is horizontal."
when (slope > 0) then
puts "The slope is positive."
when (slope < 0) then
puts "The slope is negative."
when (x_1-x_2 == 0) then
puts "The line is vertical."
end
How would I make a value that is divided by zero return puts "The line is vertical!" without getting the ZeroDivisionError ?

Replace all to_i with to_f. Then you can test for a vertical line with slope.abs == Float::INFINITY.
For completeness, include the test slope.nan? as the first test to output Those are not two distinct points! This will cover the case when they enter in the same point twice.

x == 0 ? puts "The line is vertical!" : y/x

You can also rescue divide by zero operations in ruby
begin
1/0
rescue ZeroDivisionError => kaboom
p kaboom
end

One way to do this is to follow your equation with a rescue, such as
2/0 # this throws an error
2/0 rescue "This line is vertical" # this prints the rescue
2/2 rescue "This line is vertical" # this prints 1

Related

Code won't function properly without the Integer method for Ruby to properly identify whether my input is either greater or less than when compared

I am brand new to programming. I am going through the Learn Ruby course on Codecademy. It's taking me through a exercise on experimenting with else/if statements. I'm attempting to run code where you input two integers (n and y) and print a statement based on whether n > y, n < y, or n == y.
As an example, when I input n = 5 and y = 15, it would print out the statement "I'm getting printed because n is greater than y) despite that not being true. Some set of numbers would print the correct statement, some set of numbers (as the one above) always printed the incorrect statement. After about 30 minutes of trying to figure out why it wouldn't work, I attempted adding the Integer method to my code and it works as intended. I'm just trying to understand why it wouldn't work properly prior to that.
Here is my code before:
print "Enter an integer for x:"
n = gets.chomp
print "Enter an integer for y:"
y = gets.chomp
if n < y
print "I'm getting printed because #{n} is less than #{y}"
elsif n > y
print "I'm getting printed because #{n} is greater than #{y}"
else
print "I'm getting printed because #{n} is equal to #{y}"
end
Here is my code after adding the Integer method:
print "Enter an integer for n:"
n = Integer(gets.chomp)
print "Enter an integer for y:"
y = Integer(gets.chomp)
if n < y
print "I'm getting printed because #{n} is less than #{y}"
elsif n > y
print "I'm getting printed because #{n} is greater than #{y}"
else
print "I'm getting printed because #{n} is equal to #{y}"
end
After going back in the lessons, I noticed an example Codecademy provided where they use the Integer method, but they do not go into detail about it. Regardless, I still added it to my code in the same fashion they used it in their example, and now it works properly. Like I said above, I just want to understand why it wouldn't work before and where it was going wrong in my code before I go any further in my lessons.
gets returns the string entered by the user, including the newline character created by pressing enter. gets.chomp removes the newline character at the end of the string, but still leaves the input a string.
And strings are sorted and compared alphabetically. "aa" < "b" correct when comparing strings and in the same sense "11" < "2" is correct for strings containing numbers.
But Integer(gets.chomp) translates the user input from a string containing a number into an integer (an actual number). And therefore the comparison works as expected afterward.
Documentation of the methods in play:
Kernel#gets
String#chomp
Kernel#Integer

Odds of rolling two dice with equal outcomes seem more common than I pictured

After running this program several times i noticed that my y value is somewhere between 60-80 every single time.
I thought because 70^2 is 4900 that i would end up with my y value ending up around 1 each run through, but actually its about a 1 in 70 chance of the dice equaling each other.
So why is it that rolling 2 70 sided dice and having the results equal each other is not a 1/4900 chance, but rather a 1/70 chance? Heres the program...
x=0
y=0
while x < 4900
random = rand(70)
random2 = rand(70)
puts " "
puts random
puts random2
if random == random2
puts "the numbers matched"
y+=1
end
x+=1
if x == 4900
puts " "
puts y
end
end
There are 4900 possible outcomes (1,1), (1,2), (1,3) .. ,(70, 70)
There are 70 outcomes that are suitable for your condition - (1,1), (2,2) .. (70,70)
So, the probability is needed_outcomes/all_outcomes = 70/4900 = 1/70 ~= 0.0142858
In test program number of tests is not connected to number of outcomes. Larger number of tests tends to show more accurate results (through in this case we don't program at all, but there is ruby tag in the question).
So, we can try this:
x=0
total_matches = 0.0
N = 1000000
while x < N
random = rand(1..70)
random2 = rand(1..70)
total_matches += 1 if random == random2
x += 1
end
puts total_matches/N
It gives something around 0.0142.
If you wish to estimate the probability of two thrown 70-sided dice showing the same value (which we know to be (1.0/70).round(6) #=> 0.014286) by simulating throws, you can assume one die always shows the same given value and repeatedly throw the second die only, counting the number of times it shows the assumed value of the first die, and then divide the count by the number of throws. (See my comment on the question.)
Suppose each die has sides labelled 0, 1,...,69 and we assume the first die always shows a 0. We can then simulate as follows:
def simulate(nbr_throws)
nbr_throws.times.sum { rand(70) == 0 ? 1 : 0 }.fdiv(nbr_throws).round(6)
end
simulate( 100) #=> 0.01
simulate( 1_000) #=> 0.016
simulate( 10_000) #=> 0.0151
simulate( 100_000) #=> 0.01358
simulate( 1_000_000) #=> 0.014305
simulate( 10_000_000) #=> 0.014282
simulate(100_000_000) #=> 0.014284
See Kernel#rand and Integer#fdiv. More generally, if each die had n sides, change rand(70) to rand(n).

Identifying Triangle with if/else

Question is a user gives 3 sides and identifies triangles, like equilateral, isosceles and scalene. Here is my coding, I don't know why gives any sides that always show up "invalid". I think it's logic wrong, but I can't figure out.
puts "please input the length of 3 sides:"
a = gets.chomp.to_i
b = gets.chomp.to_i
c = gets.chomp.to_i
if a + b <= c
puts "invalid"
elsif a <= 0 || b <= 0 || c <= 0
puts "invalid"
else
if a == b && b == c
puts"equilateral triangle"
elsif a == b
puts"isosceles triangle"
else
puts"scalene triangle"
end
end
The fact that your code always prints "invalid" makes me think that input is passed in on one line instead of being on separate lines. For example, when the input is:
50 50 50
instead of getting 50 in all three variables you would get 50 in a and 0 in b, c. This is because gets takes in an entire line instead of taking one value.
In such an event, this is what you need:
a, b, c = gets.split.map{ |value| value.to_i }
A better more effective way to do this is to store the values of the triangle sides into a hash first, the value of of each triangle side will be the keys, and the value of each key can be the repeats. This will work with strings too.
Here is an Example.
# First you get an array, you can use gets.chomp as string and split to array, whichever way you choose, but in the end we end up with an array, and we pass the array to the method.
def triangle_type(arr)
# Create new empty hash
repeated_sides = Hash.new(0)
# First make sure the array is only a length of three. (this is optional)
if arr.length == 3
# Iterate through each value in the array and store it to to a hash to find duplicates
arr.each do |x|
repeated_sides[x] += 1
end
# sort the hash by it's values in descending order, for logic to work later.
repeated_sides = repeated_sides.sort_by {|k,v| v}.reverse.to_h
# uncomment this below to see the duplicate sides hash
#puts "#{repeated_sides}"
# Iterate through the sorted hash, apply logic starting from highest and first value the iterator will find.
repeated_sides.each do |k,v|
return v == 3 ? 'Equilateral Triangle' : v == 2 ? 'Isosceles Triangle' : 'Scalene Triangle'
end
end
# Return Not a triangle if the condition fails
return 'Not a triangle'
end
# Test with integers
puts triangle_type([4,1,2,5]) # output: Not a triangle
puts triangle_type([3,3,3]) # output: Equilateral Triangle
puts triangle_type([4,3,3]) # output: Isosceles Triangle
puts triangle_type([4,2,3]) # output: Scalene Triangle
# Test with strings
puts triangle_type(['4','1','2','5']) # output: Not a triangle
puts triangle_type(['3','3','3']) # output: Equilateral Triangle
puts triangle_type(['4','3','3']) # output: Isosceles Triangle
puts triangle_type(['4','2','3']) # output: Scalene Triangle
puts triangle_type(['a','a','a']) # output: Equilateral Triangle
puts triangle_type(['a','c','c']) # output: Isosceles Triangle
puts triangle_type(['a','b','c']) # output: Scalene Triangle
Skipping user inputs, since I can not reproduce the error (even if Unihedron found a fix) there is still a problem with the logic.
When the input is a = 1000, b = 1, c = 1, the result is "scalene triangle", but it should return "invalid". Below a fix I suggest.
Let's store the input in an array (already converted into integer or float):
sides = [a, b, c]
First you need to check that all sides are positive:
sides.all? { |x| x > 0 }
Then, check that the sum of two sides is greater than the other:
sides.combination(2).map{ |x| x.sum }.zip(sides.reverse).all? { |xy, z| xy > z }
Finally (I'm missing something?), to pick the triangle denomination you can use an hash accessing it by sides.uniq result:
triangle_kinds = {1 => 'equilateral', 2 => 'isosceles', 3 => 'scalene'}
triangle_kinds[sides.uniq.size]
Used the following methods over array (enumerable):
https://ruby-doc.org/core-2.5.1/Enumerable.html#method-i-all-3F
https://ruby-doc.org/core-2.5.1/Array.html#method-i-combination
https://ruby-doc.org/core-2.5.1/Array.html#method-i-map
https://ruby-doc.org/core-2.5.1/Array.html#method-i-zip
https://ruby-doc.org/core-2.5.1/Array.html#method-i-reverse
https://ruby-doc.org/core-2.5.1/Array.html#method-i-sum
https://ruby-doc.org/core-2.5.1/Array.html#method-i-uniq

How to check if input is an integer?- Ruby [duplicate]

This question already has answers here:
How to test if a string is basically an integer in quotes using Ruby
(19 answers)
Closed 6 years ago.
I have this code where I am entering input for sides of a triangle. Depending on the values, it will print it the triangle is equilateral, isoceles, or scalene. It's executing for number values, but how do I specify that the input should only be integers? For example, if I type in "w" , it should say invalid or error, but in this case, it executes. How do I solve this?
Basically, I am looking for a way to write that if a string were to be inputted, it should show up as an error (then I would write a print statement saying it is invalid). So could I put that into another if statement? (before the ones mentioned below)
Example Code:
puts "Enter the triangle length"
x = gets.chomp
puts "Enter the triangle width"
y = gets.chomp
puts "Enter the triangle height"
z = gets.chomp
if x == y and y == z
puts "This triangle is equilateral"
else if
x==y or y == z or x==z
puts "This triangle is isoceles"
else if
x!=y and y!=z and x!=z
puts "this triangle is scalene"
end
end
end
If you are dealing with integers, you can check this with ruby.
Note, this is not as robust as regex, but it covers most cases.
if (input != '0') && (input.to_i.to_s != input.strip)
# input is not a number
else
# input is a number
end
strip is there if you want to accept input with leading or trailing whitespace, otherwise you can leave it off.
While all the other answers are probably more or less robust, I would go with another one. Since you have a triangle sides lengths, they are to be greater than zero, right? Then one might use the side effect of String#to_i method: for everything that is not converting to integer it returns zero. Therefore:
x = gets.chomp.to_i
y = gets.chomp.to_i
z = gets.chomp.to_i
raise "Wrong input" unless x > 0 && y > 0 && z > 0
# ...
You can do something like this:
x = x.to_i
puts "Please enter an integer" if x == 0
Why?
Because:
"ABC".to_i # returns 0
You may be better off calling strip instead of chomp
gets.strip.to_i
An example:
## ruby user_age.rb
# input variables
name = ""
years = 0
MONTHS_PER_YEAR = 12 # a constant
# output variable
months = 0
# processing
print "What is your name? "
name = gets.strip
print "How many years old are you? "
years = gets.strip.to_i
puts "please enter an integer" if years == 0
months = years * MONTHS_PER_YEAR
puts "#{name}, at #{years} years old, "\
"you are #{months} months old."
There are several ways of doing it. If you allow for a leading sign,
x =~ /^[+-]?\d+$/
would be a possibility.
You will also have to think whether or not you allow surrounding or embedding spaces (for instance, a space between the sign and the first digit).
I assume that any string value that, when converted to a float, equals an integer is to be accepted and the integer value is to be returned. Moreover, I assume integers can be entered with the "xen" (or "xEn") notation, where x is an integer or float and n is an integer.
def integer(str)
x = Float(str) rescue nil
return nil if x==nil || x%1 > 0
x.to_i
end
integer("3") #=> 3
integer("-3") #=> -3
integer("3.0") #=> 3
integer("3.1") #=> nil
integer("9lives") #=> nil
integer("3e2") #=> 300
integer("-3.1e4") #=> -31000
integer("-30e-1") #=> -3
integer("3.0e-1") #=> nil
You could use Integer() to check if a string contains an integer:
Integer('1234')
#=> 1234
Integer('foo')
#=> ArgumentError: invalid value for Integer()
This could be combined with a retry:
begin
number = Integer(gets) rescue retry
end

Comma assignment (why right go first?) i.e. n,m = 1, 2?

In the process of figuring out blocks and yields I came across these comma-separated assignments:
def fibit
n,m =1,1
loop do |variable|
yield n
n,m = m,n+m # this line
puts "n is #{n} m is #{m}"
end
end
fibit do |num|
puts "Next : #{num}"
break if num > 100
end
Why does the m get assigned first in this scenario?
Does the last one always go first? If so why?
This was also seen as only e has the value of 1 meaning e went first?
e,r=1
puts r
puts "-------"
puts e
Also, does anyone have an idea why the code-block versions just executes, where if I write the same code with no code block I actually need to call the method for it to run?
def fibit
n,m =1,1
loop do |variable|
puts "Next : #{n}"
break if n > 100
n,m = m,n+m
end
end
fibit
If I didn't have the last line it wouldn't run. Where in the first one I don't actually call the fibit method? Or is the block kicking it off?
m does not get assigned first. When using multiple assignments, all right hand side calculations are done before any assignment to the left hand side.
That's how this code works:
a = 1
b = 3
a, b = b, a
a
# => 3
b
# => 1
This would not be possible if the assignment was done serially, since you would get that both would be either equal 1 or 3.
To further prove my point, simply swap the assignment of n and m in your code, and you'll find that the result is the same:
def fibit
n,m =1,1
loop do |variable|
yield n
m,n = n+m,m # this line
puts "n is #{n} m is #{m}"
end
end
fibit do |num|
puts "Next : #{num}"
break if num > 100
end

Resources