Учитывая, что map()
определяется как Enumerable
, как может Hash#map
yield
две переменные в своем блоке?
Это не так.Это yield
sa один объект в своем блоке, который представляет собой массив из двух элементов, состоящий из ключа и значения.
Это просто деструктурирующая привязка:
def without_destructuring(a, b) end
without_destructuring([1, 2])
# ArgumentError: wrong number of arguments (1 for 2)
def with_destructuring((a, b)) end # Note the extra parentheses
with_destructuring([1, 2])
def with_nested_destructuring((a, (b, c))) p a; p b; p c end
with_nested_destructuring([1, [2, 3]])
# 1
# 2
# 3
# Note the similarity to
a, (b, c) = [1, [2, 3]]
Теоретически, вам нужно было бы позвонить map
следующим образом:
hsh.map {|(k, v)| ... }
И, фактически, для inject
вам на самом деле нужно , чтобы сделать это:
hsh.inject {|acc, (k, v)| ... }
Однако Ruby более мягок в проверке аргументов для блоков, чем для методов.В частности:
- Если вы
yield
более одного объекта, но блок принимает только один аргумент, все объекты собираются в массив. - Если вы
yield
один объект, но блок принимает несколько аргументов, Ruby выполняет деструктурирование связывания.(Это имеет место здесь.) - Если вы
yield
больше объектов, чем блок принимает аргументы, дополнительные объекты игнорируются. - Если у вас блок принимает больше аргументов, чем вы
yield
ing, дополнительные аргументы связаны с nil
.
По сути, та же семантика, что и для параллельного присваивания.
Фактически, до Ruby 1.9, аргументы блока фактически имел семантику присваивания.Это позволило вам делать сумасшедшие вещи, подобные этим:
class << (a = Object.new); attr_accessor :b end
def wtf; yield 1, 2 end
wtf {|@a, a.b| } # WTF? The block body is empty!
p @a
# 1
p a.b
# 2
Этот сумасшедший материал работает (в версии 1.8 и старше), потому что передача аргументов блока обрабатывается так же, как присваивание.IOW, хотя вышеуказанный блок пуст и не выполняет ничего , тот факт, что аргументы блока передаются так, как если бы они были назначены, означает, что установлен @a
и установлен метод a.b=
называется.Сумасшедший, а?Вот почему он был удален в 1.9.
Если вы хотите напугать своих коллег, прекратите определять ваши сеттеры следующим образом:
attr_writer :foo
и вместо этого определите их следующим образом:
define_method(:foo=) {|@foo|}
Просто убедитесь, что кто-то другой заканчивает тем, что поддерживает его: -)