Ruby Koans о передаче сообщений, блоке отправки и аргументах

Я работаю над Ruby Koans about_message_passing.rb и получил код, работающий для method_missing следующим образом:

def method_missing(method_name, *args, &block)
  @messages << method_name
  @object.__send__(method_name, *args, &block)
end

Этот код, кажется, работает, но я не совсем понимаю, почему знак нужен в *args, а & нужен с блоком.

Если бы я определял метод, я понимаю, что * и & используются для обозначения аргумента массива и аргумента блока соответственно, но что это означает, когда они используются с методом отправки для вызова метода для объекта?


person ZenBalance    schedule 27.12.2012    source источник


Ответы (3)


Я возьму их по одному. полностью уберите method_missing из этого, так как это только сбивает с толку происходящее. На самом деле это совершенно не связано с этим.


Знак * делает 2 вещи. В аргументах метода definition он собирает несколько аргументов в массив. При использовании в методе invocation массив разбивается на отдельные аргументы. Использование обоих позволяет вам пересылать любое количество аргументов другому методу.

def foo(*args)
  bar(*args)
end

def bar(a, b, c)
  puts a
  puts b
  puts c
end

foo(1,2,3) # prints 1, 2 and then 3

Поскольку вы в основном пересылаете все аргументы, это тот же шаблон.


& для аргумента блока. Их может быть ровно один на вызов метода, это блок, который висит на конце. Это особый аргумент, поскольку он не входит в аргументы напрямую. Вы можете записать блок в переменную, зафиксировав add &someblock в качестве последнего аргумента в определении метода.

Затем вы можете передать блок в вызове метода, используя тот же синтаксис.

def foo(&block)
  bar(&block)
end

def bar
  yield
end

foo { puts 'hello' } # prints hello

Это позволяет передать висячий блок другому методу, не вызывая его. Это не всегда требуется, потому что обычно вы просто используете yield для выполнения любого переданного блока. Но если вы хотите сделать что-то помимо простого выполнения, вам нужно зафиксировать ссылку на сам блок.


Итак, если вы объедините эти две вещи, вы получите окончательный переадресатор методов. Вы захватываете все любое количество аргументов и любой блок, который висит на конце, и отправляете их другому методу.

# forwards everything to the method `bar`
def foo(*args, &block)
  bar(*args, &block)
end

Наконец, send — это просто метод. Он ожидает имя метода, за которым следует любое количество аргументов (не массив), и может опционально обрабатывать висячий блок.

Другими словами:

foo.send methodName, *args, &block
person Alex Wayne    schedule 27.12.2012
comment
Обратите внимание, что символ splat применяется не только к определениям параметров метода/списку аргументов. Это также относится к определениям параметров блоков/спискам аргументов, назначению и. - person Jörg W Mittag; 27.12.2012

Знак в определении метода означает «взять все несопоставленные аргументы и поместить их в массив» (в ruby ​​1.8 это всегда были последние аргументы, но в 1.9 знаки могут встречаться в середине).

Использование его в вызове метода является обратным: это означает взять этот массив и использовать его содержимое в качестве аргументов.

foo(a,b) #call foo with 2 arguments: a and b
foo([a,b]) #call foo with a single array argument 
foo(*[a,b]) # call foo with 2 arguments: a and b

& аналогично: в определении метода он захватывает блок и превращает его в proc, но в вызове метода он превращает proc (или proc подобный объект - подойдет любой ответ на to_proc) в блок для этого метода

Вам нужны оба из них для method_missing, потому что (в общем) вы хотите передать все аргументы и блок из исходного вызова метода.

person Frederick Cheung    schedule 27.12.2012

Насколько мне известно, каждый раз, когда вы передаете блок напрямую, это делается с синтаксисом &block_name.

Кроме того, подпись метода для Object#send принимает бесконечные аргументы, а не массив. Таким образом, передавая значения *args с разбивкой, это то же самое, как если бы вы передали аргументы, разделенные запятыми.

person AJcodez    schedule 27.12.2012