3

私はRubyKoansabout_message_passing.rbに取り組んでおり、次のようにmethod_missingのコードを機能させています。

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

このコードは機能しているようですが、* argsでスプラットが必要な理由とブロックで&が必要な理由がよくわかりません。

メソッドを定義している場合、*と&はそれぞれ配列引数とブロック引数を表すために使用されることを理解していますが、オブジェクトのメソッドを呼び出すためにsendメソッドで使用されるとはどういう意味ですか?

4

3 に答える 3

9

これらを一度に1つずつ取ります。何がmethod_missing起こっているのか混乱させるだけなので、これを完全に取り除いてください。それは実際にはそれとは完全に無関係です。


感嘆符*は2つのことをします。メソッド定義の引数では、複数の引数を配列に吸収します。メソッド呼び出しで使用すると、配列が個々の引数に飛び出します。両方を使用すると、任意の数の引数を別のメソッドに転送できます。

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

基本的にすべての引数を転送しているので、これは同じパターンです。


これ&はブロック引数用です。メソッド呼び出しごとにこれらの1つだけが存在する可能性があり、最後にぶら下がっているのはブロックです。これは、引数に直接含まれないという点で、特別な引数です。メソッド定義&someblockの最後の引数としてaddをキャプチャすることにより、ブロックを変数にキャプチャできます。

次に、同じ構文を使用して、メソッド呼び出しでブロックを渡すことができます。

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

def bar
  yield
end

foo { puts 'hello' } # prints hello

これにより、ハンギングブロックを呼び出さずに別のメソッドに渡すことができます。yield通常は、渡されたブロックを実行するために使用するだけなので、必ずしも必要ではありません。ただし、実行するだけでなく何かを実行したい場合は、ブロック自体への参照をキャプチャする必要があります。


したがって、これら2つのことを組み合わせると、究極のメソッドフォワーダーが得られます。任意の数の引数のすべて、および最後にぶら下がっていたブロックをキャプチャし、それらを別のメソッドに送信します。

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

最後に、これsend単なる方法です。メソッドの名前の後に任意の数の引数(配列ではない)が続くことを想定しており、オプションでハンギングブロックを処理できます。

言い換えると:

foo.send methodName, *args, &block
于 2012-12-27T00:37:51.947 に答える
1

メソッド定義のスプラットは、「一致しないすべての引数を取り、それらを配列に入れる」ことを意味します(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(一般的に)元のメソッド呼び出しからのすべての引数とブロックを渡したいので、これらの両方が必要です。

于 2012-12-27T00:31:12.637 に答える
0

私の知る限り、ブロックを直接渡すときはいつでも、構文&block_nameを使用します。

また、 Object#sendのメソッドシグネチャは、配列ではなく、無限の引数を取ります。したがって、スプラットされた値* argsを渡すことにより、コンマ区切りのargsを渡した場合と同じになります。

于 2012-12-27T00:30:29.787 に答える