do..end vs фигурные скобки для блоков в Ruby - PullRequest
135 голосов
/ 08 апреля 2011

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

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

Так что же это и почему? (Пример некоторого кода musta)

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 ]

224 голосов
/ 08 апреля 2011

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

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

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

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..end в этих случаях, чтобы избежать этой путаницы.

50 голосов
/ 08 апреля 2011

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

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

Таким образом, код

f param {do_something()}

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

f param do do_something() end

Связывает блок с функцией f.

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

14 голосов
/ 08 апреля 2011

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

8 голосов
/ 10 ноября 2011

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

7 голосов
/ 08 апреля 2011

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


Условное обозначение do .. end для многострочного и { ... } для однострочного.

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

  10.times do 
    puts ...
  end

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

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

7 голосов
/ 08 апреля 2011

Самое распространенное правило, которое я видел (совсем недавно в Eloquent Ruby ):

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

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

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 в одной строке, потому что его сложнее читать.

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

1 голос
/ 29 июля 2014

Мой личный стиль - подчеркивать удобочитаемость над жесткими правилами выбора { ... } против 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
}

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

1 голос
/ 11 июля 2014

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

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 не существует ...

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

так что каждый ответ вроде

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

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

Так что если вы хотите использовать 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
1 голос
/ 08 апреля 2011

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

Один из примеров: если вы пришли изФон JAVA, для логического метода, который вы можете использовать

def isExpired
  #some code
end 

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

Но в мире ruby,тот же метод будет

def expired?
  #code
end

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

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

do 
  #code
end

блоков.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...