Ruby Koans的triangle.rb更优雅的解决方案

时间:2011-01-20 01:41:36

标签: ruby

我一直在研究Ruby Koans,并将其发送到about_triangle_project.rb,在其中您需要编写方法三角形的代码。

这些项目的代码可在此处找到:

https://github.com/edgecase/ruby_koans/blob/master/koans/about_triangle_project.rb

https://github.com/edgecase/ruby_koans/blob/master/koans/triangle.rb

在triangle.rb中,我创建了以下方法:

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

我从阅读克里斯·派恩的“学会计划”中知道,总有不止一种方法可以做。虽然上面的代码有效,但我不禁想到有一种更优雅的方式。是否有人愿意提出他们如何使这种方法更有效和紧凑的想法?

我很好奇的另一件事是,为了确定等边三角形,我无法创造(a == b == c)的条件。它是等边三角形的证明,但Ruby讨厌语法。对于为什么会有这么简单的解释吗?

9 个答案:

答案 0 :(得分:55)

有一个简单的解释为什么:

Ruby中的

==是一个执行特定功能的运算符。运营商有规则来确定他们应用的顺序 - 例如,a + 2 == 3在等式检查之前评估添加。但是一次只评估一个操作员。将两个相等性检查彼此相邻是没有意义的,因为相等性检查的结果为truefalse。有些语言允许这样做,但它仍然无法正常工作,因为如果true == ca相等,那么您将评估b,即使a = = = b == c用数学术语表示。

至于更优雅的解决方案:

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

或者,甚至更简洁(但不太可读):

[:equilateral, :isosceles, :scalene].fetch([a,b,c].uniq.size - 1)

答案 1 :(得分:11)

另一种方法:

def triangle(a, b, c)
  a, b, c = [a, b, c].sort
  raise TriangleError if a <= 0 or a + b <= c
  return :equilateral if a == c
  return :isosceles if a == b or b == c
  return :scalene
end

答案 2 :(得分:6)

def triangle(a, b, c)
  if a == b && a == c                # transitivity => only 2 checks are necessary
    :equilateral
  elsif a == b || a == c || b == c   # == operator has the highest priority
    :isosceles
  else
    :scalene                         # no need for return keyword
  end
end

答案 3 :(得分:5)

我借用了Chuck的酷炫uniq.size技术并将其用于oo解决方案。最初我只想提取参数验证作为保护条款以维护单一责任原则,但由于两种方法都在相同的数据上运行,我认为它们属于一个对象。

# for compatibility with the tests
def triangle(a, b, c)
  t = Triangle.new(a, b, c)
  return t.type
end

class Triangle
  def initialize(a, b, c)
    @sides = [a, b, c].sort
    guard_against_invalid_lengths
  end

  def type
    case @sides.uniq.size
    when 1 then :equilateral
    when 2 then :isosceles
    else :scalene
    end
  end

  private
  def guard_against_invalid_lengths
    if @sides.any? { |x| x <= 0 }
      raise TriangleError, "Sides must be greater than 0"
    end

    if @sides[0] + @sides[1] <= @sides[2]
      raise TriangleError, "Not valid triangle lengths"    
    end
  end
end

答案 4 :(得分:5)

class TriangleError < StandardError
end

def triangle(a, b, c)
  sides = [a,b,c].sort

  raise TriangleError if sides.first <= 0 || sides[2] >= sides[1] + sides[0]
  return :equilateral if sides.uniq.length  == 1
  return :isosceles if sides.uniq.length  == 2
  :scalene
end

答案 5 :(得分:3)

这是我的解决方案:

def triangle(a, b, c)
  sides = [a, b, c].sort
  raise TriangleError, "Invalid side #{sides[0]}" unless sides[0] > 0
  raise TriangleError, "Impossible triangle" if sides[0] + sides[1] <= sides[2]
  return [:scalene, :isosceles, :equilateral][ 3 - sides.uniq.size ]
end

答案 6 :(得分:2)

嗯..我不知道uniq - 所以来自smalltalk(很久以前)我用过:

require 'set'
def triangle(a, b, c)
  case [a, b, c].to_set.count
    when 1 then :equilateral
    when 2 then :isosceles
    else :scalene
  end
end

答案 7 :(得分:2)

来自matlab世界,我习惯于数组函数&#39;任何&#39;并且&#39;所有&#39;,并且很高兴能够在Ruby中找到它们。所以:

def triangle(a, b, c)
  eqs = [a==b, a==c, b==c]
  eqs.all?? :equilateral : eqs.any?? :isosceles : :scalene
end

根据可读性,计算时间......(红宝石noob),不知道这是否是最佳的。

答案 8 :(得分:1)

这是我的解决方案:

def triangle(a, b, c)
  return :equilateral if a == b and b == c
  return :isosceles if ( a == b or b == c or a == c )
  return :scalene
end