Использование do block против фигурных скобок {} - PullRequest
110 голосов
/ 23 января 2010

Новичок в рубине, наденьте перчатки для новичка.

Есть ли какая-либо разница (неясная или практическая) между следующими двумя фрагментами?

my_array = [:uno, :dos, :tres]
my_array.each { |item| 
    puts item
}

my_array = [:uno, :dos, :tres]
my_array.each do |item| 
    puts item
end

Я понимаю, что синтаксис фигурной скобки позволит вам поместить блок в одну строку

my_array.each { |item| puts item }

но есть ли какие-либо веские причины использовать один синтаксис над другим?

Ответы [ 5 ]

98 голосов
/ 23 января 2010

Поваренная книга Ruby говорит, что синтаксис скобок имеет более высокий приоритет, чем do..end

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

1.upto 3 do |x|
  puts x
end

1.upto 3 { |x| puts x }
# SyntaxError: compile error

Второй пример работает только при использовании скобок, 1.upto(3) { |x| puts x }

69 голосов
/ 06 октября 2014

Это немного старый вопрос, но я хотел бы попытаться объяснить немного больше о {} и do .. end

как сказано выше

Синтаксис скобок имеет более высокий порядок приоритета, чем do..end

но как этот имеет значение:

method1 method2 do
  puts "hi"
end

в этом случае method1 будет вызван с блоком do..end, а method2 будет передан method1 в качестве аргумента! что эквивалентно method1(method2){ puts "hi" }

но если вы скажете

method1 method2{
  puts "hi"
}

тогда метод2 будет вызываться с блоком, а возвращенное значение будет передано методу1 в качестве аргумента. Что эквивалентно method1(method2 do puts "hi" end)

def method1(var)
    puts "inside method1"
    puts "method1 arg = #{var}"
    if block_given?
        puts "Block passed to method1"
        yield "method1 block is running"
    else
        puts "No block passed to method1"
    end
end

def method2
    puts"inside method2"
    if block_given?
        puts "Block passed to method2"
        return yield("method2 block is running")
    else
        puts "no block passed to method2"
        return "method2 returned without block"
    end
end

#### test ####

method1 method2 do 
    |x| puts x
end

method1 method2{ 
    |x| puts x
}

#### output ####

#inside method2
#no block passed to method2
#inside method1
#method1 arg = method2 returned without block
#Block passed to method1
#method1 block is running

#inside method2
#Block passed to method2
#method2 block is running
#inside method1
#method1 arg = 
#No block passed to method1
39 голосов
/ 23 января 2010

Обычно принято использовать {}, когда вы выполняете небольшую операцию, например, вызов метода или сравнение и т. Д., Так что это имеет смысл:

some_collection.each { |element| puts element }

Но если у вас слегка сложная логика, которая идет в несколько строк, используйте do .. end, например:

1.upto(10) do |x|
  add_some_num = x + rand(10)
  puts '*' * add_some_num
end

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

5 голосов
/ 03 октября 2017

Существует два общих стиля для выбора do end против { } для блоков в Ruby:

Первый и очень распространенный стиль был популяризирован Ruby on Rails и основан на простом правиле, состоящем из одного или нескольких строк:

  • Используйте фигурные скобки { } для однолинейных блоков
  • Используйте do end для многострочных блоков

Это имеет смысл, потому что do / end плохо читает в однострочном режиме, но для многострочных блоков оставлять закрывающим }, висящим на собственной строке, несовместимо со всем остальным, которое использует end в ruby, например как определения модулей, классов и методов (def и т. д.) и управляющих структур (if, while, case и т. д.)

Второй, менее часто встречающийся стиль известен как семантический, или « фигурные скобки Вейриха », предложенный покойным великим рубиистом Джимом Вейрихом:

  • Использовать do end для процедурных блоков
  • Используйте фигурные скобки { } для функциональных блоков

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

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

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

Например, здесь возвращаемое значение блока применяется к каждому элементу:

items.map { |i| i.upcase }

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

items.each do |item|
  puts item
end

Еще одним преимуществом семантического стиля является то, что вам не нужно менять фигурные скобки для завершения / завершения только потому, что в блок была добавлена ​​строка.

Как наблюдение, по совпадению функциональные блоки часто являются однострочными, а процедурные блоки (например, config) являются многострочными. Таким образом, следование стилю Weirich в конечном итоге выглядит почти так же, как стиль Rails.

1 голос
/ 26 апреля 2019

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

date = Timecop.freeze(1.year.ago) { format_date(Time.now) }
customer = Timecop.freeze(1.year.ago) { create(:customer) }

Это процедурные или функциональные?

И, на мой взгляд, подсчет строк бесполезен. Я знаю, есть ли 1 или более линий, и почему именно я должен изменить стиль только потому, что я добавил или удалил строки?

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