Использование do block против фигурных скобок {}

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

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

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 }

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

Ответ 1

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

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

Ответ 2

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

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

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

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

method1 method2 do
  puts "hi"
end

в этом случае метод1 будет вызываться с блоком do..end, а метод2 будет передан методу 1 в качестве аргумента! что эквивалентно 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

Ответ 3

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

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

Ответ 4

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

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

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

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

Второй, менее часто встречающийся стиль известен как семантический, или " Weirich Braces", предложенный покойным великим рубистом Джимом Weirich:

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

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

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

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

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

items.map { |i| i.upcase }

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

items.each do |item|
  puts item
end

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

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

Ответ 5

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

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

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

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