делать..end vs фигурные скобки для блоков в Ruby

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

я твердо в лагере только с помощью фигурных скобок для коротких однострочных и делаю..конец всему остальному. Но я думал, что я обращусь к большому сообществу, чтобы получить некоторое разрешение.

Так что это, и почему? (Пример некоторым следовало код)

context do
  setup { do_some_setup() }
  should "do somthing" do
    # some more code...
  end
end

или

context {
  setup { do_some_setup() }
  should("do somthing") {
    # some more code...
  }
}

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

13 ответов


общая конвенция должна использовать do..конец для многострочных блоков и фигурные скобки для одиночных линейных блоков, но есть также разница между ними, которые можно проиллюстрировать на этом примере:

puts [1,2,3].map{ |k| k+1 }
2
3
4
=> nil
puts [1,2,3].map do |k| k+1; end
#<Enumerator:0x0000010a06d140>
=> nil

это означает, что {} имеет более высокий приоритет, чем сделать..конец, поэтому имейте это в виду, когда решаете, что вы хотите использовать.

P. S: Еще один пример, чтобы иметь в виду, пока вы разрабатываете свои предпочтения.

следующий код:

task :rake => pre_rake_task do
  something
end

на самом деле означает:

task(:rake => pre_rake_task){ something }

и этот код:

task :rake => pre_rake_task {
  something
}

на самом деле означает:

task :rake => (pre_rake_task { something })

поэтому, чтобы получить фактическое определение, которое вы хотите, с фигурными скобками, вы должны сделать:

task(:rake => pre_rake_task) {
  something
}

возможно, использование фигурных скобок для параметров-это то, что вы хотите сделать в любом случае, но если вы этого не делаете, вероятно, лучше использовать do..в этих случаях, чтобы избежать этой путаницы.


С Программирования Ruby:

фигурные скобки имеют высокий приоритет; do имеет низкий приоритет. Если вызов метода имеет параметры, которые не заключены в круглые скобки, форма скобки блока будет привязываться к последнему параметру, а не к общему вызову. Форма do будет привязана к вызову.

код

f param {do_something()}

связывает блок с param переменную, а код

f param do do_something() end

Персонализация блок функции f.

однако это не проблема, если вы заключите аргументы функции в круглые скобки.


Существует несколько точек зрения на это, это действительно вопрос личных предпочтений. Многие рубисты подход вы делаете. Однако два других стиля, которые являются общими, - это всегда использовать один или другой или использовать {} для блоков, возвращающих значения, и do ... end для блоков, которые выполняются для побочных эффектов.


есть одно важное преимущество фигурных скобок-многие редакторы имеют гораздо более простое время их сопоставления, что делает некоторые типы отладки намного проще. Между тем, ключевое слово "делать...end "довольно сложно сопоставить, тем более, что" end "s также соответствует" if " s.


Я голосую за do / end


конвенция do .. end для многострочных и { ... } для острот.

но мне нравится do .. end лучше, поэтому, когда у меня есть один вкладыш, я использую do .. end в любом случае, но отформатируйте его как обычно для do/end в трех строках. Это делает всех счастливыми.

  10.times do 
    puts ...
  end

одна проблема с { } это поэзия-режим-враждебный (потому что они плотно привязываются к последнему параметру, а не ко всему вызову метода, поэтому вы должны включить способ скобкой) и они просто, на мой взгляд, не выглядят так хорошо. Они не являются группами операторов, и они сталкиваются с константами хэша для удобства чтения.

плюс, я видел достаточно { } в C-программах. Руби, как всегда, лучше. Существует ровно один тип if блок, и вам никогда не придется возвращаться и преобразовывать оператор в составной оператор.


самое распространенное правило, которое я видел (совсем недавно в Красноречивый Рубин) составляет:

  • если это многострочный блок, используйте do / end
  • если это однострочный блок, используйте {}

пара влиятельных рубиистов предлагают использовать фигурные скобки, когда вы используете возвращаемое значение, и do / end, когда вы этого не делаете.

http://talklikeaduck.denhaven2.com/2007/10/02/ruby-blocks-do-or-brace (ВКЛ archive.org)

http://onestepback.org/index.cgi/Tech/Ruby/BraceVsDoEnd.rdoc (ВКЛ archive.org)

Это кажется хорошей практикой в целом.

Я бы немного изменил этот принцип, чтобы сказать, что вы должны избегать использования do / end на одной строке, потому что это труднее читать.

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


сводится к личной предвзятости, я предпочитаю фигурные скобки над блоком do/end, поскольку его более понятно для большего числа разработчиков из-за того, что большинство фоновых языков используют их по соглашению do / end. При этом реальный ключ должен прийти к соглашению в вашем магазине, если do/end используется разработчиками 6/10, чем все должны использовать их, если 6/10 используют фигурные скобки, то придерживайтесь этой парадигмы.

его все о создании шаблона, так что команда как whole может быстрее идентифицировать структуры кода.


между ними есть тонкая разница, но { } связывает крепче, чем do/end.


на самом деле это личное предпочтение, но, сказав это, за последние 3 года моего опыта ruby я узнал, что у ruby есть свой стиль.

одним из примеров может быть, если вы исходите из фона JAVA, для логического метода вы можете использовать

def isExpired
  #some code
end 

обратите внимание на случай верблюда и чаще всего префикс "is", чтобы идентифицировать его как логический метод.

но в мире ruby тот же метод был бы

def expired?
  #code
end

Так что я лично думаю, лучше пойти с "ruby way" (но я знаю, что для понимания требуется некоторое время (это заняло у меня около 1 года :D)).

наконец, я бы пошел с

do 
  #code
end

блоки.


я поставил другой ответ, хотя большой разница уже была отмечена (prcedence / binding), и это может вызвать проблемы с поиском (Железный Дровосек и другие указали на это). Я думаю, что мой пример показывает проблему с не очень обычным фрагментом кода, даже опытные программы не читают, как sunday times:

module I18n
    extend Module.new {
        old_translate=I18n.method(:translate)
        define_method(:translate) do |*args|
            InplaceTrans.translate(old_translate, *args)
        end
        alias :t :translate
    }
end

module InplaceTrans
    extend Module.new {
        def translate(old_translate, *args)
            Translator.new.translate(old_translate, *args)
        end
    }
end

затем я сделал некоторые украшения кода ...

#this code is wrong!
#just made it 'better looking'
module I18n
    extend Module.new do
        old_translate=I18n.method(:translate)
        define_method(:translate) do |*args|
            InplaceTrans.translate(old_translate, *args)
        end
        alias :t :translate
    end
end

если вы измените {} здесь do/end вы получите ошибка, что метод translate не существует ...

почему это происходит, указано здесь более одного приоритета. Но куда ставить брекеты? (@The Tin Man: я всегда использую брекеты, как и вы, но здесь ... под наблюдением)

поэтому каждый ответ, как

If it's a multi-line block, use do/end
If it's a single line block, use {}

это просто неправильно если используется без " но следите за скобками / приоритет!"

еще раз:

extend Module.new {} evolves to extend(Module.new {})

и

extend Module.new do/end evolves to extend(Module.new) do/end

(что когда-либо результат extend делает с блоком ...)

поэтому, если вы хотите использовать do / end, используйте это:

#this code is ok!
#just made it 'better looking'?
module I18n
    extend(Module.new do 
        old_translate=I18n.method(:translate)
        define_method(:translate) do |*args|
            InplaceTrans.translate(old_translate, *args)
        end
        alias :t :translate
    end)
end

мой личный стиль-подчеркнуть читаемость над жесткими правилами {...} vs do...end выбор, когда этот выбор возможен. Мое представление о читаемости таково:

[ 1, 2, 3 ].map { |e| e + 1 }      # preferred
[ 1, 2, 3 ].map do |e| e + 1 end   # acceptable

[ 1, 2, 3 ].each_with_object [] do |e, o| o << e + 1 end # preferred, reads like a sentence
[ 1, 2, 3 ].each_with_object( [] ) { |e, o| o << e + 1 } # parens make it less readable

Foo = Module.new do     # preferred for a multiline block, other things being equal
  include Comparable
end

Foo = Module.new {      # less preferred
  include Comparable
}

Foo = Module.new { include Comparable }      # preferred for a oneliner
Foo = module.new do include Comparable end   # imo less readable for a oneliner

[ [ 1 ], [ 1, 2 ] ].map { |e| e.map do |e| e + 1 end }  # slightly better
[ [ 1 ], [ 1, 2 ] ].map { |e| e.map { |e| e + 1 } }     # slightly worse

в более сложном синтаксисе, таком как многострочные вложенные блоки, я пытаюсь перемежать {...} и do...end разделители для большинства естественных результатов, например.

Foo = Module.new { 
  if true then
    Bar = Module.new {                          # I intersperse {} and keyword delimiters
      def quux
        "quux".tap do |string|                  # I choose not to intersperse here, because
          puts "(#{string.size} characters)"    # for multiline tap, do ... end in this
        end                                     # case still loks more readable to me.
      end
    }
  end
}

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


есть третий вариант: напишите препроцессор, чтобы вывести "конец" на своей собственной строке, из отступа. Глубокие мыслители, предпочитающие сжатый код, оказываются правы.

еще лучше, hack ruby, так что это флаг.

конечно," pick your fights " простейшее решение состоит в том, чтобы принять соглашение о стиле, что последовательность концов все появляются на одной строке, и научить свой синтаксис окраски, чтобы отключить их. Для удобства редактирования можно использовать скрипты редактора для расширения / сворачивания эта последовательность.

20% до 25% моих строк кода ruby являются " end " в своей собственной строке, все тривиально выведены моими соглашениями отступов. Ruby-это шепелявый язык, который достиг наибольшего успеха. Когда люди оспаривают это, спрашивая, где все ужасные скобки, я показываю им рубиновую функцию, заканчивающуюся семью строками лишнего "конца".

Я делал код в течение многих лет, используя препроцессор lisp для вывода большинства скобок: панель ' | ' открыла группу, которая автоматически конец строки и знак доллара " $ " служили пустым заполнителем, где в противном случае не было бы символов, помогающих вывести группировки. Это, конечно, территория религиозной войны. Lisp / scheme без скобок является самым поэтичным из всех языков. Тем не менее, проще просто успокоить скобки, используя синтаксическую раскраску.

Я все еще кодирую с препроцессором для Haskell, чтобы добавить heredocs и по умолчанию все флеш-строки как комментарии, все отступы как код. Я нелюбовь к комментариям персонажей, независимо от языка.