Есть ли сокращение для Ruby Blocks?

Меня раздражает следующая многословная запись на Ruby:

polys.each { |poly| poly.edges.each {|edge| draw edge.start, edge.end } }

(polys — это массив полигонов, ребра — это метод Polygon, возвращающий массив ребер)

В идеале я хотел бы сократить это до чего-то вроде этого:

polys.each.edges.each { draw _.start, _.end }

Более конкретно я хотел бы знать:

  • Как мы могли написать хак method_missing как с первым каждым? (При вызове с отсутствующим методом перечислитель может вызвать этот метод для каждого элемента, возвращенного из перечисления)
  • Есть ли способ избавиться от |x|, используя любой символ или имя по умолчанию?

person Christopher Oezbek    schedule 08.08.2016    source источник
comment
Это действительно draw edge.start edge.end? Не draw edge.start, edge.end?   -  person mu is too short    schedule 09.08.2016
comment
То, что вы хотите сделать, проблематично в Ruby (или любом языке сценариев), поскольку Ruby ничего не знает о получателе до времени выполнения. Сравните [[1,2],[3,4]].each { |e| puts e } с [[1,2],[3,4]].each { |_,e| puts e } Как здесь можно использовать значение по умолчанию для переменных блока?   -  person Cary Swoveland    schedule 09.08.2016
comment
Много лет назад, когда я был программистом на C, многословное письмо вызывает у меня улыбку. Хотя это не короче, вам может быть понятнее разделить то, что у вас есть, на методы очевидным образом: def draw_edge(edge); draw edge.start, edge.end; end; def draw_poly(poly); poly.each { |edge| draw_edge(edge) }; end; def draw_polys(polys) { |poly| draw_poly(poly) }.   -  person Cary Swoveland    schedule 09.08.2016
comment
Запрашивать драгоценный камень, который обеспечивает функциональность x, не по теме, поэтому вы должны удалить это из вопроса. Вопросы, в которых нас просят порекомендовать или найти книгу, инструмент, программную библиотеку, учебное пособие или другой сторонний ресурс, не относятся к теме Stack Overflow, поскольку они, как правило, привлекают самоуверенные ответы и спам. Вместо этого опишите проблему и что уже сделано для ее решения.   -  person the Tin Man    schedule 09.08.2016
comment
Спасибо за замечания (запятые исправлены). @CarySwoveland: я прошу языковую функцию, которая позволяет использовать имя, такое как x, если нет |...| используется. Подумайте: как бы это сделал Perl?   -  person Christopher Oezbek    schedule 09.08.2016
comment
Извините, я не знаю Perl.   -  person Cary Swoveland    schedule 13.08.2016


Ответы (1)


Нет. Самое близкое, что вы можете сделать, это:

polys.flat_map(&:edges).each { |_| draw _.start, _.end }

flat_map преобразует массив в другой массив и сводит его к одномерному массиву. Если внутри блока вызывается один метод без параметров, вы можете использовать ярлык &:edges.

При этом я, вероятно, буду держать его ближе к вашему первоначальному предложению, так как оно более читабельно:

polys.each do |poly| 
  poly.edges.each {|edge| draw edge.start, edge.end }
end

Помните, вы пишете код один раз, но он много читается, поэтому удобочитаемость важнее краткости.

person Martin    schedule 08.08.2016
comment
polys.flat_map(&:edges).each {|edge| draw(edge.start, edge.end) } лучше, нет необходимости использовать карту и сглаживание как отдельные методы. - person philomory; 09.08.2016
comment
Кроме того, если они определяют метод draw_edge, они могут просто написать polys.flat_map(&:edges).each(&method(:draw_edge)) - person philomory; 09.08.2016