Более элегантное решение для треугольника Руби Коанса. - PullRequest
15 голосов
/ 20 января 2011

Я работал через 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).Это доказательство для равностороннего треугольника, но Руби ненавидит синтаксис.Есть ли простое объяснение, почему это так?

Ответы [ 9 ]

55 голосов
/ 20 января 2011

Существует простое объяснение, почему это так:

== в 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)
11 голосов
/ 13 мая 2011

Другой подход:

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
6 голосов
/ 20 января 2011
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
5 голосов
/ 15 октября 2014
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 голосов
/ 28 февраля 2011

Я позаимствовал классную технику Чака uniq.size и превратил ее в ооо решение.Первоначально я просто хотел извлечь валидацию аргумента в качестве защитного предложения для поддержания принципа единой ответственности, но поскольку оба метода работали с одними и теми же данными, я думал, что они принадлежат друг другу в объекте.

3 голосов
/ 01 февраля 2012

Вот мое решение:

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
2 голосов
/ 17 декабря 2015

Исходя из мира Matlab, я привык к массиву функций 'any' и 'all' и был достаточно счастлив найти их в Ruby.Итак:

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

Не знаю, оптимально ли это, хотя, с точки зрения читабельности, времени вычислений ... (ruby noob).

2 голосов
/ 27 мая 2011

Хм .. Я не знал о uniq - поэтому, исходя из разговоров в чате (давным-давно), я использовал:

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
1 голос
/ 06 августа 2014

Вот мое решение:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...