it-swarm.com.ru

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

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

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

135
Blake Taylor

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

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

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

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

224
Pan Thomakos

От программирование Ruby :

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

Итак, код

f param {do_something()}

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

f param do do_something() end

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

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

49
David Brown

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

14
GSto

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

8
GlyphGryph

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

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

Я голосую за до/конец


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

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

  10.times do 
    puts ...
  end

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

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

7
DigitalRoss

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

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

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

5
Kelvin

Я поставил другой ответ, хотя уже было указано (большое различие (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

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

Так что если вы хотите использовать 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
halfbit

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

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

1
Jake Kalstad

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

1
Shankar Raju

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

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

1
Boris Stitnicky

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

Например, если вы используете фон Java, для логического метода, который вы можете использовать

def isExpired
  #some code
end 

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

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

def expired?
  #code
end

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

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

do 
  #code
end

блоки.

1
sameera207