Ruby

Ruby Koans

KoansI decided to tackle a little Ruby language learning this weekend. So yesterday, I downloaded the Ruby Koans and went to work. The Koans were created by EdgeCase to help people learn Ruby. Basically, they are a set of unit tests that exercise aspects of Ruby. To begin, all of the tests fail. You have to write code or jump back and forth to the Interactive Ruby Shell (IRB) and get the results of the statements to make the asserts pass.

I went through and did 105 unit tests yesterday, each of them having several parts to them. Having messed around a little with Rails, I was familiar with using IRB and Rake and a little of the syntax. In my opinion, the way this is set up and builds upon itself is really helpful and is a great way to teach Ruby to n00bs. I really salute Jim Weirich and Joe O’Brien for their work in getting these together.

One of the exercises is to create a triangle method that takes three parameters (the sizes of the sides). You then have to decide what kind of triangle it is. After those tests pass, your next assignment is to prevent 0 length or negative sides as well as making sure your triangle validates.

I wrote all the code and passed all the tests except for the triangle validation test. I was having a serious brain cramp and could not remember how you ensured that you’ve gotten the correct sides for any triangle (and not just a right triangle), so I looked for someone else’s example of this code. I found this code:

# Triangle Project Code.

# Triangle analyzes the lengths of the sides of a triangle
# (represented by a, b and c) and returns the type of triangle.
#
# It returns:
#   :equilateral  if all sides are equal
#   :isosceles    if exactly 2 sides are equal
#   :scalene      if no sides are equal
#
# The tests for this method can be found in
#   about_triangle_project.rb
# and
#   about_triangle_project_2.rb
#
def triangle(a, b, c)
  if a<=0 or b<=0 or c<=0 
    fail TriangleError, "Sides must all have length greater than 0."
  end

  myArray = [a,b,c]
  maxSide = myArray.max
  total = 0
  myArray.each { |myEle| total += myEle }
  if total - maxSide <= maxSide
    fail TriangleError, "Sum of shortest two sides must be greater than length of longest side."
  end

  if a==b and a==c
     :equilateral 
  elsif a!=b and b!=c and a!=c
    :scalene
  else
     :isosceles
  end
end

# Error class used in part 2.  No need to change this code.
class TriangleError < StandardError
end

Okay, I see. The smallest two sides added together need to be greater than the third side. However, I thought that the author of this code greatly over-complicated this process. We are always dealing with a fixed number of sides and the trick is to put them in order and then add away. I figured Ruby had a simple array sort (and they do), so I would place the sides into an array, sort it, and then add the 0 index and 1 index spot and make sure that it was greater than the 2 index spot. Case Closed and (in my opinion) a bit easier to read. Forgiving of course, that I didn’t pass messages in my exceptions, I personally like my code better (of course 😉 ). I also appreciate that Ruby doesn’t mind me putting ( and ) around my if conditions. I just feel safer for them to be contained 😉

# Triangle Project Code.

# Triangle analyzes the lengths of the sides of a triangle
# (represented by a, b and c) and returns the type of triangle.
#
# It returns:
#   :equilateral  if all sides are equal
#   :isosceles    if exactly 2 sides are equal
#   :scalene      if no sides are equal
#
# The tests for this method can be found in
#   about_triangle_project.rb
# and
#   about_triangle_project_2.rb
#
def triangle(a, b, c)
  # WRITE THIS CODE
  if (a <= 0 or b <= 0 or c<=0)
    raise TriangleError
  end
  
  sides = [a, b, c].sort
  
  if (sides[0] + sides[1] <= sides[2])
    raise TriangleError
  end
  
  if (a==b and a==c)
    value = :equilateral
  else
    if (a==b or a==c or b==c)
      value = :isosceles
    else
      value = :scalene
    end    
  end  
end

# Error class used in part 2.  No need to change this code.
class TriangleError < StandardError
end

As I do these Koans, I’ve been making note of things I’d like to include in future blog entries about things I’ve enjoyed about the language and will be posting them in the near term.

13 comments Ruby Koans

Nathan Kelley says:

I took Leon Gersing’s Koans talk at #devLink and worked on the all weekend. I especially enjoyed the Triangle koan. Here is how i did it. I intentionally refactored my if blocks into more “english style” statements. (Trying to not write C# in ruby)

def triangle(a, b, c)
raise TriangleError unless TriangleSideLengthsAreValid?(a,b,c)
return :equilateral if [a,b,c].uniq.length==1
return :isosceles if [a,b,c].uniq.length==2
return :scalene if [a,b,c].uniq.length==3
end

def TriangleSideLengthsAreValid?(a,b,c)
ax,bx,cx=[a,b,c].sort
return (ax+bx)>cx
end

I don’t think using .uniq on the array was cheating.

Pete says:

Nathan,

That’s really awesome. You are definitely right, your code is much more the “Ruby way” than mine. I don’t think that uniq is cheating, that is a very clever solution to the problem in my opinion.

Thanks for dropping by and moreso for sharing that code with me!

For a moment there I thought I was looking at my code as the downloaded example, but then I remembered that I didn’t check any answers into github.

I saw Nathan’s solution while at Devlink. I highly approve of its brevity 😀

Pete says:

Greg,

Thanks for commenting. I wish I could have been at Devlink, but I didn’t waste the entire weekend, anyway! Happy belated congratulations on becoming an American citizen, btw.

[…] post is an extension of my last post on the Ruby Koans. Today, I tackled the next Ruby Koan on the block, which was to score dice throws […]

I am also doing the ruby koans. I was so proud of my solution, that I needed to share… sorry if I am gloating. Here is what I came up with:

def triangle(a, b, c)
sides = [a,b,c].sort
raise TriangleError if sides.select{|x| x 0
raise TriangleError if sides[0] + sides[1] <= sides[2]
[:equilateral, :isosceles, :scalene][sides.uniq.size – 1]
end

Kay says:

I just did the triangle scenario and my code is as follows:

def triangle(a,b,c)
if (a<=0 or b<=0 or c<=0)
begin
raise TriangleError
end
elsif (a==b and a==c)
return :equilateral
elsif
(a==b or a==c or b==c)
return :isosceles
else
:scalene
end
end

Alexxey says:

def triangle(a, b, c)
a,b,c=[a, b, c].sort
raise TriangleError if a=a+b

case [a, b, c].uniq.length
when 1
:equilateral
when 2
:isosceles
else
:scalene
end
end

Shorter code is definitely not better, but combining my own TriangleError and Brian Genisio’s Array you can write it in 2 lines:

def triangle(a, b, c)
raise TriangleError, “Triangle error” if (a <= 0 || b <= 0 || c = (a+b+c))
[:equilateral, :isosceles, :scalene][[a,b,c].uniq.size – 1]
end

Who performs better 😉

Jared Hodgkins says:

This is my modifications to Brian’s post since I was having issues with his TriangleError

def triangle(a, b, c)
sides = [a,b,c].sort
raise TriangleError unless sides.each{|x| x > 0}
raise TriangleError if sides[0] + sides[1] <= sides[2]
[:equilateral, :isosceles, :scalene][sides.uniq.size – 1]
end

Chad says:

Here was my solution to part 1 & 2 of the Triangle Koan, triangle.rb:

def triangle(a, b, c)
abc = [a, b, c]
side1, side2, side3 = abc.sort #sorts them from smallest to biggest side
raise TriangleError if side1 + side2 <= side3 # checks for triangle inequality (take the two smallest sides and add them, combined they need to be larger than the biggest side)
abc.each {|x| raise TriangleError if x <= 0 } #checks to see if any sides are smaller or equal to zero

if (a == b && a ==c)
:equilateral
elsif (a == b || b == c || a == c)
:isosceles
else
:scalene
end
end

Shannon says:

a, b, c = [a, b, c].sort
raise TriangleError unless a > 0 && a + b > c

if a != b && b != c && c != a then
:scalene
elsif a == b && b == c then
:equilateral
else
:isosceles
end

Chris Pelatari says:


def triangle(a, b, c)
raise TriangleError unless valid_triangle?(a,b,c)
[:equilateral, :isosceles, :scalene][[a,b,c].uniq.size - 1]
end

def valid_triangle?(a,b,c)
ax,bx,cx = [a,b,c].sort
ax + bx > cx
end

Comments are closed.