プログラムを書いていると、状況によっては、「もらった引数を使って別な関数・メソッドに処理を丸投げする」必要が出てくる場合があります。ここでは、そのような例を示してみます。
JavaScript…配列のようで配列でないもの
JavaScriptの関数では、宣言した仮引数とは別に、arguments
という、すべての引数を詰め込んだ「配列のようなオブジェクト」1が渡されているので、これを使って別な関数をapply
することで、すべての引数をそのまま渡せます(apply
の引数は「配列のようなオブジェクト」で大丈夫です)。
なお、apply
の第1引数は呼び出し先でthis
になるオブジェクトです。ただの関数なら気にしなくて構いませんが、メソッドを呼ぶ際はきちんと設定しましょう。
//foo()からbar()をそのまま呼ぶ
function foo(hoge, piyo){
bar.apply(this, arguments);
}
#Ruby…必殺、splat
Rubyの場合、メソッドの唯一の引数を*args
のように*
を前置して宣言しておくと、それがすべての引数の入った配列になります。逆に、引数を渡す段階で*
を前置させると、その配列の中身を引数に並べてくれます。
def foo(*args)
bar(*args)
end
構文解析上の問題について
上の例ではbar(*args)
とかっこが付いています。かっこを外した場合、bar
、*
、args
という3つの要素が並ぶことになりますが、それはbar
とargs
の掛け算、と取れなくもない状況です。ここでは、bar *args
のように、*
の前だけ空白を入れた場合は、メソッド呼び出しをsplatしているものと解釈され、それ以外のbar*args
やbar * args
は掛け算として解釈されます2。1つ目の引数をsplatする場合には、かっこを付けたほうがいいかもしれません(かっこありならbar(* args)
でも問題ありません)。
ブロックは…?
まとめて*args
で受け取っても、ブロックは取得できません。&block
としてブロックを引数として受け取って、別なメソッドを呼ぶ際にも&block
としてブロックを渡す形にしましょう。
def foo(*args, &block)
bar(*args, &block)
end
なお、&block
としたメソッドにブロックが渡されなかった場合、この変数はnil
となりますが、&nil
とすると「ブロックを渡さない」という意味になるので、block_given?
によって条件分岐する必要もなく、ブロックがあってもなくてもそのまま対応可能です。