2011-12-18 3 views

ответ

16

Это не отменяет карты

Hash.new.method(:map).owner # => Enumerable 

Это дает две переменные, которые получают собранные в массив

class Nums 
    include Enumerable 

    def each 
    yield 1 
    yield 1, 2 
    yield 3, 4, 5 
    end 
end 

Nums.new.to_a # => [1, [1, 2], [3, 4, 5]] 
+1

Ах, конечно, 'Hash' должен определять' each' для смешивания в 'Enumerable'. Какой замечательный дизайн! –

+0

Nice отражение с методом() и владелец() кстати. Я должен это вспомнить. –

9

Учитывая, что map() определяется Enumerable, как может Hash#mapyield две переменные к ее блоку?

Это не так. Он 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 :-)

+1

Очень информативный, я даже не понял, что вы можете разрушить параметры метода. Я всегда полагал, что они убрали параметры в качестве присваивания, потому что это означало, что вы случайно можете клонировать некоторые переменные в среде, и это не будет работать одинаково между локальными варами и методами. –

+1

рубин, ты так сумасшедший –

Смежные вопросы