Elliot
Elliot

Reputation: 13835

Ruby Koan 151 raising exceptions

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

Answers (30)

Clay Givens
Clay Givens

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

Raksha Saini
Raksha Saini

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

Isaac Rabinovitch
Isaac Rabinovitch

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

Basil Al-Isa
Basil Al-Isa

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

Tom Busby
Tom Busby

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

Luis Cort&#233;s
Luis Cort&#233;s

Reputation: 1

You can use this formula and validate all cases:

  • a + b > c
  • a + c > b
  • b + c > a

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

javeria
javeria

Reputation: 11

your previous triangle method should appear here

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

Himanshu Tiwari
Himanshu Tiwari

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

Michael G
Michael G

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

macsteps
macsteps

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

wmsmith
wmsmith

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

DarkArk
DarkArk

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.

  1. def(sides*) splat (turn the input into an array)
  2. raise TriangleError if ... or ... Raise the error if either are true
  3. sides.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.
  4. sides.sort![0] <=0 get the smallest element, if this is greater than 0 all the others will be.
  5. 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.
  6. sides.uniq.size-1 get the number of unique sides (minus one)
  7. [: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

BenMliang
BenMliang

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

cold_farmer
cold_farmer

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

ryan
ryan

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

jules345
jules345

Reputation: 1

Rules:

  1. size must be > 0

  2. 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

Simone
Simone

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

Jon Carter
Jon Carter

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

Vielinko
Vielinko

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

Chaos
Chaos

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

agustinMichael
agustinMichael

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

Josh
Josh

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

Will
Will

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

Leon Fedotov
Leon Fedotov

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

Serg
Serg

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

user1138
user1138

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

Sergey
Sergey

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

rahul286
rahul286

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

Juanjo
Juanjo

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

tomblomfield
tomblomfield

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

Related Questions