Учитывая, что map()
определяется Enumerable
, как может Hash#map
yield
две переменные к ее блоку?
Это не так. Он s a single Объект к его блоку, который представляет собой двухэлементный массив, состоящий из ключа и значения.
Это просто уничтожение того, обязывать:
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 выполняет деструктурирование bind. (Это имеет место здесь.)
- Если у вас
yield
больше объектов, чем блок принимает аргументы, дополнительные объекты игнорируются.
- Если в блоке больше аргументов, чем
yield
, дополнительные аргументы привязаны к 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
и вызывается метод settek a.b=
. Сумасшедший, да? Вот почему он был удален в 1.9.
Если вы хотите поразить своих коллег, прекратить определение ваших установщиков, как это:
attr_writer :foo
и вместо того, чтобы определить их как это:
define_method(:foo=) {|@foo|}
Просто убедитесь, что кто-то заканчивает поддержание it :-)
Ах, конечно, 'Hash' должен определять' each' для смешивания в 'Enumerable'. Какой замечательный дизайн! –
Nice отражение с методом() и владелец() кстати. Я должен это вспомнить. –