Более элегантное решение для triangle.rb Ruby Koans
Я работаю через 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 ненавидит синтаксис. Есть ли легкое объяснение, почему это?
Ответы
Ответ 1
Существует простое объяснение, почему это:
==
в Ruby - это оператор, который выполняет определенную функцию. У операторов есть правила для определения того, в каком порядке они применяются, поэтому, например, a + 2 == 3
оценивает добавление перед проверкой равенства. Но оценивается только один оператор за раз. Не имеет смысла иметь две проверки равенства рядом друг с другом, потому что проверка равенства оценивается как true
или false
. Некоторые языки допускают это, но это все еще не работает правильно, потому что тогда вы будете оценивать true == c
, если a
и 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)
Ответ 2
Другой подход:
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
Ответ 3
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
Ответ 4
Я позаимствовал Chuck cool uniq.size и обработал его в оо-решении. Изначально я просто хотел извлечь аргумент аргумента в качестве предложения охраны, чтобы поддерживать принцип единой ответственности, но поскольку оба метода работали с одними и теми же данными, я думал, что они принадлежат друг другу в объекте.
# 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
Ответ 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
Ответ 6
Вот мое решение:
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
Ответ 7
Хм.. я не знал о 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
Ответ 8
Исходя из мира matlab, я привык массировать функции "все" и "все", и был достаточно доволен, чтобы найти их в Ruby. Итак:
def triangle(a, b, c)
eqs = [a==b, a==c, b==c]
eqs.all?? :equilateral : eqs.any?? :isosceles : :scalene
end
Не знаю, насколько это оптимально, с точки зрения удобочитаемости, времени вычисления... (ruby noob).
Ответ 9
Вот мое решение:
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