Reputation: 13835
I'm going through the ruby koans, I'm on 151 and I just hit a brick wall.
Here is the koan:
# You need to write the triangle method in the file 'triangle.rb'
require 'triangle.rb'
class AboutTriangleProject2 < EdgeCase::Koan
# The first assignment did not talk about how to handle errors.
# Let's handle that part now.
def test_illegal_triangles_throw_exceptions
assert_raise(TriangleError) do triangle(0, 0, 0) end
assert_raise(TriangleError) do triangle(3, 4, -5) end
assert_raise(TriangleError) do triangle(1, 1, 3) end
assert_raise(TriangleError) do triangle(2, 4, 2) end
end
end
Then in triangle.rb we have:
def triangle(a, b, c)
# WRITE THIS CODE
if a==b && a==c
return :equilateral
end
if (a==b && a!=c) || (a==c && a!=b) || (b==c && b!=a)
return :isosceles
end
if a!=b && a!=c && b!=c
return :scalene
end
if a==0 && b==0 && c==0
raise new.TriangleError
end
end
# Error class used in part 2. No need to change this code.
class TriangleError < StandardError
end
I am beyond confused - any help at all would be much appreciated!
EDIT: To complete this koan, I need to put something in the TriangleError class - but I have no idea what
UPDATE: Here is what the koan karma thing is saying:
<TriangleError> exception expected but none was thrown.
Upvotes: 51
Views: 25449
Reputation: 1
I would like to see a reason for each TriangleError exception, so each failure case has a nice text message for the reason it failed. Also, each line or block of code is handling one workflow only for legibility. The three return cases are very clearly read and understood. I want to make it easy on myself or someone else who looks at this code later.
def triangle(a, b, c)
t = [a, b, c].sort
raise TriangleError, "side cannot be 0" if t.any?(0)
t.each do |x|
raise TriangleError, "side cannot be negative" if x < 0
end
raise TriangleError, "two sides added cannot be less than or equal to one side" if t[0] + t[1] <= t[2]
return :equilateral if (a == b) && (b == c)
return :scalene if (a != b) && (a != c) && (b != c)
return :isosceles
end
Upvotes: 0
Reputation: 603
The triangle.rb file need these update to work.
def triangle(a, b, c)
# If length is Less Then or equal to 0 the triangle not possible
# it raise error. If the length of any two sum is less or equal to other-
# Again the triangle not possible.
raise TriangleError if (a <= 0 || b <= 0 || c <= 0) || (a+b <= c || b+c <= a || c+a <= b)
# If the size of each equal its Equilateral Triangle.
return :equilateral if (a == b && a == c)
# If any two lenght is equal than it Isosceles Triangle.
return :isosceles if (a == b || b == c || a == c)
# If each lenght is different than its Scalene Triangle.
return :scalene if (a != b) && (b != c) && (a != c)
end
class TriangleError < StandardError
end
Upvotes: 0
Reputation: 536
Leon wins on fancy elegance, Benji for his knowledge of the Array API. Here's my brute elegant answer:
def triangle(a, b, c)
[a, b, c].each { | side | raise TriangleError, "Sides must be positive" unless side > 0 }
raise TriangleError, "Two sides can never be less than or equal to third side" if ((a + b) <= c) | ((a + c) <= b) | ((b + c) <= a)
return :equilateral if (a == b) && (b == c)
return :isosceles if (a == b) || (b == c) || (a == c)
return :scalene
end
Upvotes: 1
Reputation: 1
SIMPLEST SOLUTION!
def triangle(a, b, c)
#This would check for if the numbers are negative or 0.
if a <= 0 || b <= 0 || c <= 0
raise TriangleError
end
#This would check if the two sides of a triangle are greater than the remaining side.
if a + b <= c || a + c <= b || b + c <= a
raise TriangleError
end
if a == b && b = c
:equilateral
elsif a == b || a == c || b == c
:isosceles
else
:scalene
end
end
Thanks to all the beautiful people for their answer I have checked them all and I would say no need to change the TriangleError code. we need to check for invalid triangles and raise the error if the triangle is not.
Upvotes: 0
Reputation: 1339
My solution, I think it's one of the more readable ones:
def triangle(a, b, c)
a, b, c = [a, b, c].sort
if a <= 0 or c >= a + b
raise TriangleError
end
case [a, b, c].uniq.length
when 1
:equilateral
when 2
:isosceles
when 3
:scalene
end
end
Upvotes: 2
Reputation: 1
You can use this formula and validate all cases:
and you code like:
def triangle(a, b, c)
# Validate the triangle
raise TriangleError, "The triangle is not valid" unless (a + b) > c && (a + c) > b && (b + c) > a
if a === b && b === c
:equilateral
elsif a === b || a === c || b === c
:isosceles
else
:scalene
end
end
Upvotes: 0
Reputation: 11
class TriangleError < StandardError
end
def triangle(x,y,z)
if(x>=y+z||y>=x+z||z>=x+y)
raise TriangleError,"impossible triangle"
elsif(x==0&&y==0&&z==0)||(x<0||y<0||z<0)
raise TriangleError,"length cannot be zero or negative"
elsif(x==y&&x==z)
:equilateral
elsif(x==y||y==z||x==z)
:isosceles
else
:scalene
end
end
Upvotes: 1
Reputation: 99
This is my solution:-
def triangle(a, b, c)
if a <= 0 || b <= 0 || c <= 0 || a + b <= c || a + c <= b || b + c <= a
raise TriangleError
elsif (a == b) &&( a==c) && (b==c)
return :equilateral
elsif (a==b) || (b==c) || (a==c)
return :isosceles
else
return :scalene
end
end
Hope it helps.
Upvotes: 0
Reputation: 11
def triangle(a, b, c)
sides = a, b, c # Assigns variable signs (array) to all arguments.
begin
raise TriangleError if sides.inject(:+) <= 0 # Raise an error if all sides added together are less than or equal to 0. (the triangle would be invalid).
raise TriangleError if sides.any?(&:negative?) #Raise an error if there are any negative sides.
sides.each {|side| (side < (sides.inject(:+) - side) ? nil : (raise TriangleError))} # For the final check, Raise an error if any single side is greater than the other two sides added together. It can be broken down like this if side is less than (remaining sides - side we're comparing) raise an error, else, nil.
return :equilateral if sides.uniq.length == 1
return :isosceles if sides.uniq.length == 2
return :scalene if sides.uniq.length == 3
resuce TriangleError
end
end
Upvotes: 1
Reputation: 103
Not that this question needed another answer; however, I think this is the simplest and most readable solution. Thanks to all those before me.
def triangle(a, b, c)
a, b, c = [a, b, c].sort
raise TriangleError, "all sides must > 0" unless [a, b, c].min > 0
raise TriangleError, "2 smaller sides together must the > 3rd side" unless a + b > c
return :equilateral if a == b && a == c
return :isosceles if a == b || a == c || b == c
return :scalene
end
# Error class used in part 2. No need to change this code.
class TriangleError < StandardError
end
Upvotes: 1
Reputation: 542
The error checking is trying to get at the criteria for a triangle:
if (a <= 0 || b <= 0 || c <= 0)
raise TriangleError,"All lengths must be positive"
end
args=[a,b,c].sort #this lets us get at the longest side of the triangle
unless ( args[0]+args[1] > args[2] )
raise TriangleError,"Longest length may not be greater than the sum of the other lengths"
end
if (args[0]-args[1] > args[2])
raise TriangleError,"Longest length must be greater than the difference of the other two lengths"
end
There are more elegant and concise ways to do this error checking but I think it should make clear how to check for the 4 major criteria are. I actually rolled 2 criteria into a single check. E.g. no sides may be negative and no sides may be 0.
Using a splat operator for the arguments is a nice way to make sure you have an array without explicitly forming one, but I dislike it here because it means you should also add a check to make sure that there are exactly 3 arguments, which is implicit right now.
Upvotes: 0
Reputation: 1
Smallest solution I could come up with.
This will pass the tests, however, it will not complain if the array is longer than it should be. Easy to test for but didn't seem entirely needed.
def(sides*)
splat (turn the input into an array)raise TriangleError if ... or ...
Raise the error if either are truesides.sort!
sort the list in place. This is important as two statements rely on the list being sorted and this means it is only done once.sides.sort![0] <=0
get the smallest element, if this is greater than 0 all the others will be.sides.reverse.reduce(:-) >=0
biggest element minus the smaller two, if this is over 0 then the largest side was bigger than the other two.sides.uniq.size-1
get the number of unique sides (minus one)[:e,:i,:s].fetch(sides.uniq.size-1)
get the appropriate element from the array (and return).
def triangle(*sides)
raise TriangleError if sides.sort![0] <=0 or sides.reverse.reduce(:-) >=0
[:equilateral, :isosceles, :scalene].fetch(sides.uniq.size - 1)
end
Upvotes: 0
Reputation: 11
def triangle(a, b, c)
raise TriangleError if [a, b, c].min <= 0
raise TriangleError if [a, b, c].max * 2 >= [a, b, c].reduce(:+)
if a == b && b == c
:equilateral
elsif a == b || b == c || c == a
:isosceles
else
:scalene
end
end
Upvotes: 0
Reputation: 41
No need to write the TriangleError method. It says 'No need to change this code', so I won't change it at all. Stubborn as I am.
4lines shot it, nice nd clean.
def triangle(a, b, c)
if(a * b * c <= 0) || (( (a + c)<=b) || ((a + b)<=c)||((b + c)<=a) )
raise TriangleError else
return ((a == b && b == c && a == c)? :equilateral:(((a == b)||(b == c)||(a == c))? :isosceles: :scalene))
end
end
# Error class used in part 2. No need to change this code.
class TriangleError < StandardError
end
Upvotes: 0
Reputation: 889
#(1)Any zero or -ve values
if [a,b,c].any? { |side_length| side_length <= 0 }
raise TriangleError
end
#(2)Any side of a triangle must be less than the sum of the other two sides
# a < b+c, b < a+c and c < a+b a valid triangle
# a >= b+c, b >= a+c and c >= a+b an invalid triangle
total_of_side_lengths = [a,b,c].inject {|total,x| total += x}
if [a,b,c].any? { |side_length| side_length >= (total_of_side_lengths - side_length)}
raise TriangleError
end
Upvotes: 1
Reputation: 1
Rules:
size must be > 0
Total of any 2 sides, must be bigger that the 3rd
Code:
raise TriangleError if ( [a,b,c].any? {|x| (x <= 0)} ) or ( ((a+b)<=c) or ((b+c)<=a) or ((a+c)<=b))
[:equilateral, :isosceles, :scalene].fetch([a,b,c].uniq.size - 1)
Upvotes: 0
Reputation: 21262
Here's my solution... honestly I can't think of a more concise and readable one!
def triangle(a, b, c)
raise TriangleError unless a > 0 && b > 0 && c > 0
raise TriangleError if a == b && a + b <= c
raise TriangleError if a == c && a + c <= b
return :equilateral if a == b && b == c
return :isosceles if a == b || b == c || c == a
:scalene
end
Upvotes: 0
Reputation: 3426
I don't think I see this one here, yet.
I believe all the illegal triangle conditions imply that the longest side can't be more than half the total. i.e:
def triangle(a, b, c)
fail TriangleError, "Illegal triangle: [#{a}, #{b}, #{c}]" if
[a, b, c].max >= (a + b + c) / 2.0
return :equilateral if a == b and b == c
return :isosceles if a == b or b == c or a == c
return :scalene
end
Upvotes: 3
Reputation: 1661
After try to understand what I must to do with koan 151, I got it with the first posts, and get lot fun to check everyone solution :) ... here is the mine:
def triangle(a, b, c)
array = [a, b, c].sort
raise TriangleError if array.min <= 0 || array[0]+array[1] <= array[2]
array.uniq!
array.length == 1 ? :equilateral: array.length == 2 ? :isosceles : :scalene
end
Koan is a very interesting way to learn Ruby
Upvotes: 4
Reputation: 31
Here is my elegant answer, with a lot of help from the comments above
def triangle(a, b, c)
test_tri = [a,b,c]
if test_tri.min <=0
raise TriangleError
end
test_tri.sort!
if test_tri[0]+ test_tri[1] <= test_tri[2]
raise TriangleError
end
if a == b and b == c
:equilateral
elsif a != b and b != c and a != c
:scalene
else
:isosceles
end
end
Upvotes: 1
Reputation: 31
There are some absolutely brilliant people on StackOverflow...I'm reminded of that every time I visit :D Just to contribute to the conversation, here's the solution I came up with:
def triangle(a, b, c)
raise TriangleError if [a,b,c].min <= 0
x,y,z = [a,b,c].sort
raise TriangleError if x + y <= z
equal_sides = 0
equal_sides +=1 if a == b
equal_sides +=1 if a == c
equal_sides +=1 if b == c
# Note that equal_sides will never be 2. If it hits 2
# of the conditions, it will have to hit all 3 by the law
# of associativity
return [:scalene, :isosceles, nil, :equilateral][equal_sides]
end
Upvotes: 0
Reputation: 6473
def triangle(a, b, c)
[a, b, c].permutation do |sides|
raise TriangleError unless sides[0] + sides[1] > sides[2]
end
case [a,b,c].uniq.size
when 3; :scalene
when 2; :isosceles
when 1; :equilateral
end
end
Upvotes: 16
Reputation: 4705
No Need to change the TriangleError code for either challenge. You just need to check for invalid triangles and raise the error if the triangle isn't.
def triangle(a, b, c)
if a==0 && b==0 && c==0
raise TriangleError, "This isn't a triangle"
end
if a <0 or b < 0 or c <0
raise TriangleError, "Negative length - thats not right"
end
if a + b <= c or a + c <= b or b + c <= a
raise TriangleError, "One length can't be more (or the same as) than the other two added together. If it was the same, the whole thing would be a line. If more, it wouldn't reach. "
end
# WRITE THIS CODE
if a == b and b == c
return :equilateral
end
if (a==b or b == c or a == c)
return :isosceles
end
:scalene
end
Upvotes: 0
Reputation: 7851
Ended up doing this:
def triangle(a, b, c)
a, b, c = [a, b, c].sort
raise TriangleError if a <= 0 || a + b <= c
[nil, :equilateral, :isosceles, :scalene][[a, b, c].uniq.size]
end
Thanks to commenters here :)
Upvotes: 16
Reputation: 251
In fact in the following code the condition a <= 0 is redundant. a + b will always be less than c if a < 0 and we know that b < c
raise TriangleError if a <= 0 || a + b <= c
Upvotes: 3
Reputation: 11
This is what I ended up with. It is sort of a combination of a few of the above examples with my own unique take on the triangle inequality exception (it considers the degenerate case as well). Seems to work.
def triangle(a, b, c)
raise TriangleError if [a,b,c].min <= 0
raise TriangleError if [a,b,c].sort.reverse.reduce(:-) >= 0
return :equilateral if a == b && b == c
return :isosceles if a == b || a == c || b == c
return :scalene
end
Upvotes: 1
Reputation: 758
You forgot the case when a,b, or c are negative:
def triangle(a, b, c)
raise TriangleError if [a,b,c].min <= 0
x, y, z = [a,b,c].sort
raise TriangleError if x + y <= z
[:equilateral,:isosceles,:scalene].fetch([a,b,c].uniq.size - 1)
end
Upvotes: 35
Reputation: 949
Here is my version... :-)
def triangle(a, b, c)
if a <= 0 || b <= 0 || c <= 0
raise TriangleError
end
if a + b <= c || a + c <= b || b + c <= a
raise TriangleError
end
return :equilateral if a == b && b == c
return :isosceles if a == b || a == c || b == c
return :scalene if a != b && a != c && b != c
end
Upvotes: 1
Reputation: 363
You could also try to instance the exception with:
raise TriangleError.new("All sides must be greater than 0") if a * b * c <= 0
Upvotes: 2
Reputation: 909
You don't need to modify the Exception. Something like this should work;
def triangle(*args)
args.sort!
raise TriangleError if args[0] + args[1] <= args[2] || args[0] <= 0
[nil, :equilateral, :isosceles, :scalene][args.uniq.length]
end
Upvotes: 9