Параметры для блока определяются методом определения. Определение для reduce/inject
перегружено ( docs ) и определено в C, но если вы хотите определить его, вы можете сделать это следующим образом (обратите внимание, это не охватывает все перегруженные случаи для фактическое reduce
определение):
module Enumerable
def my_reduce(memo=nil, &blk)
# if a starting memo is not given, it defaults to the first element
# in the list and that element is skipped for iteration
elements = memo ? self : self[1..-1]
memo ||= self[0]
elements.each { |element| memo = blk.call(memo, element) }
memo
end
end
Это определение метода определяет, какие значения использовать для memo
и element
, и вызывает переменную blk
(блок, переданный методу) с ними в определенном порядке.
Обратите внимание, что блоки не похожи на обычные методы, потому что они не проверяют количество аргументов. Например: (обратите внимание, в этом примере показано использование yield
, который является другим способом передачи параметра блока)
def foo
yield 1
end
# The b and c variables here will be nil
foo { |a, b, c| [a,b,c].compact.sum } # => 1
Вы также можете использовать деконструкцию для определения переменных во время запуска блока, например, если вы хотите reduce
над хэшем, вы можете сделать что-то вроде этого:
# this just copies the hash
{a: 1}.reduce({}) { |memo, (key, val)| memo[key] = val; memo }
Как это работает, вызов reduce
для хеша неявно вызывает to_a
, который преобразует его в список кортежей (например, {a: 1}.to_a = [[:a, 1]]
). reduce
передает каждый кортеж как второй аргумент в блок. В месте, где вызывается блок, кортеж деконструируется на отдельные переменные ключа и значения.