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