Все четыре из них допустимы, но все они имеют разную семантику — что правильно, зависит от того, чего вы пытаетесь достичь.
Примеры
Рассмотрим следующий метод, который дает несколько значений.
def frob
yield 1, 2, 3
end
Вариант A: параметры блока
Дайте мне первые два полученных значения, если они есть, остальные меня не интересуют.
frob { |v, w| [v, w].inspect}
# => "[1, 2]"
Вариант B: параметр блока + локальная переменная блока
Дайте мне первое значение, остальные меня не интересуют; и дайте мне дополнительную неинициализированную переменную.
frob { |v; w| [v, w].inspect}
# => "[1, nil]"
Вариант C: параметры блока, некоторые со значениями по умолчанию
Получите первые два значения, и если второе значение не инициализировано, установите для этой переменной значение 1:
frob { |v, w = 1| [v, w].inspect }
# => "[1, 2]" <-- all values are present, default value ignored
Получите первые пять значений, и если пятое значение не инициализировано, установите для этой переменной значение 99:
frob { |v, w, x, y, z = 99| [v, w, x, y, z].inspect }
# => "[1, 2, 3, nil, 99]"
Вариант D: параметры позиционирования и блока ключевых слов
Получите мне первое значение, и если метод выдает параметр ключевого слова w
, получите и его; если нет, установите его на 1.
frob { |v, w: 1| [v, w].inspect }
# => "[1, 1]"
Это разработано для случая, когда метод выдает параметры блока:
def frobble
yield 1, 2, 3, w: 4
end
frobble { |v, w: 1| [v, w].inspect }
# => "[1, 4]"
В Ruby ‹ 2.7 блок с параметром ключевого слова также деструктурирует хеш, хотя Ruby 2.7 даст вам предупреждение об устаревании, как если бы вы передали хэш методу, который принимает аргументы ключевого слова:
def frobnitz
h = {w: 99}
yield 1, 2, 3, h
end
# Ruby 2.7
frobnitz { |v, w: 1| [v, w].inspect }
# warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
# => "[1, 99]"
Ruby 3.0 не выдает предупреждения об устаревании, но также игнорирует хэш:
# Ruby 3.0
frobnitz { |v, w: 1| [v, w].inspect }
# => [1, 1]
Однако предоставление явного аргумента ключевого слова по-прежнему работает, как и ожидалось, в версии 3.0:
# Ruby 3.0
frobble { |v, w: 1| [v, w].inspect }
# => "[1, 4]"
Обратите внимание, что форма аргумента ключевого слова завершится ошибкой, если метод выдаст неожиданные ключевые слова:
def frobnicate
yield 1, 2, 3, w: 99, z: -99
end
frobnicate { |v, w: 1| [v, w].inspect }
# => ArgumentError (unknown keyword: :z)
Деструктуризация массива
Различия становятся очевидными еще при рассмотрении метода, возвращающего массив:
def gork
yield [1, 2, 3]
end
Передача блока с одним аргументом даст вам весь массив:
gork { |v| v.inspect }
# => "[1, 2, 3]"
Однако передача блока с несколькими аргументами даст вам элементы массива, даже если вы передадите слишком мало или слишком много аргументов:
gork { |v, w| [v, w].inspect }
# "[1, 2]"
gork { |v, w, x, y| [v, w, x, y].inspect }
# => "[1, 2, 3, nil]"
Здесь снова может пригодиться синтаксис ;
для блочных переменных:
gork { |v; w| [v, w].inspect }
# => "[[1, 2, 3], nil]"
Однако обратите внимание, что даже аргумент ключевого слова все равно приведет к деструктуризации массива:
gork { |v, w: 99| [v, w].inspect }
# => "[1, 99]"
gork { |v, w: 99; x| [v, w, x].inspect }
# => "[1, 99, nil]"
Затенение внешней переменной
Обычно, если вы используете имя внешней переменной внутри блока, вы используете эту переменную:
w = 1; frob { |v| w = 99}; w
# => 99
Вы можете избежать этого с помощью любого из вариантов выше; любой из них будет затенять внешнюю переменную, скрывая внешнюю переменную от блока и гарантируя, что любые эффекты, которые блок оказывает на нее, являются локальными.
Вариант A: параметры блока:
w = 1; frob { |v, w| puts [v, w].inspect; w = 99}; w
# [1, 2]
# => 1
Вариант B: параметр блока + локальная переменная блока
w = 1; frob { |v; w| puts [v, w].inspect; w = 99}; w
# [1, nil]
# => 1
Вариант C: параметры блока, некоторые со значениями по умолчанию
w = 1; frob { |v, w = 33| puts [v, w].inspect; w = 99}; w
# [1, 2]
# => 1
Вариант D: параметры позиционирования и блока ключевых слов
w = 1; frob { |v, w: 33| puts [v, w].inspect; w = 99}; w
# [1, 33]
# => 1
Однако другие поведенческие различия все еще сохраняются.
Значения по умолчанию
Вы не можете установить значение по умолчанию для локальных переменных блока.
frob { |v; w = 1| [v, w].inspect }
# syntax error, unexpected '=', expecting '|'
Вы также не можете использовать аргумент ключевого слова в качестве параметра блока.
frob { |v; w: 1| [v, w].inspect }
# syntax error, unexpected ':', expecting '|'
Если вы знаете, что вызываемый вами метод не возвращает параметр блока, вы можете объявить поддельный параметр блока со значением по умолчанию и использовать его для получения предварительно инициализированного блока. локальная переменная. Повторяется из первого примера выбора D выше:
frob { |v, w: 1| [v, w].inspect }
# => "[1, 1]"
person
David Moles
schedule
28.01.2021
;
для назначения значений по умолчанию, переменные после;
не являются параметрами блока, вы просто объявляете, что блок будет их использовать, и Ruby не должен перезаписывать существующие локальные переменные с помощью то же имя, когда вы делаете. - person matt   schedule 05.06.2014;
block-local только в том случае, если я хочу скрыть переменную с тем же именем во внешней области. - person oddjobsman   schedule 05.06.2014[[1,2], [3]].map { |f,s=4| [f,s] } => [[1, 2], [3, 4]]
. Я неправильно понял, что вы говорите? - person Cary Swoveland   schedule 05.06.2014a = [a0, [a1, a2], a3]
в блок, но будете использовать только элементa2
. Устранение неоднозначности позволяет вам записать это как|a0,(a1,a2),a3|
, а использование заполнителей позволяет упростить его до|_,(_,a2,_)|
, которое можно сократить до|_,(_,a2)|
. Точно так же устранение неоднозначности позволяет вам писатьhash.each_with_object([]) { |(k,v),arr|...}
. - Кэри Суовленд 41 минуту назад - person Cary Swoveland   schedule 05.06.2014|f,s=4| [f,s]
) допустимо (и полезно). Предоставление значения по умолчанию для local block не очень полезно (например,|f;s=4| ...
) и выдаст ошибку. Использование;
— это не просто альтернатива использованию,
— все, что стоит перед точкой с запятой, является параметром, а все, что после — локальной переменной блока. - person matt   schedule 05.06.2014