オプショナル引数について考える

def f(x1, x2, y1=:y1, y2=:y2, *z, x3, x4)
end

みたいなやつですね(Ruby 1.9)。ここで、y1、y2がオプショナル引数です。さて、このメソッドを呼び出したとき、どのように引数はバインドされるのでしょうか。

f(1,2,3,4,5,6,7)

というメソッド呼び出しを考えてみましょう。

まず、x1、x2、x3、x4が1,2,6,7にバインドされます。ここで実引数のほうが数が少なければ、ArgumentErrorです。

次に、3,4,5とy1,y2,zが問題になります。ここは先頭から、y1が3に、y2が4に、zが[5]になります。今回は全部ありましたが、足りない場合は前から埋められていきます。3,4だけだったらzが[]になりますし、3だけならy2が:y2になります。

まあ、そういうのはどうでも良い。

def f(a=1, b=a+1, c=b+1)
end

みたいのがどうなるか、気になるところですよね?これをf()として呼ぶと、a,b,cの値は1,2,3になります。前から一個ずつ評価されていきます。逆に

def f(c=b+1, b=a+1, a=1)
end

とすると、bについてNameErrorになります。

それでは、引数のデフォルト値の中で代入がある場合はどうなるでしょう。

def f(a=(x=100), b=x)
end

表にしてみましょう。

a b x
f(1,2) 1 2 nil
f(1) 1 nil nil
f() 100 100 100

Rubyルールで、パーサがプログラムを見て行って代入が表われた変数は、代入以降の値はnilになるというのがあるので、まあ、直感的な結果かなぁ、と思います。

bとaの順を逆にしても、特におもしろいことはありませんでした。

def f(b=x, a=(x=100))
end

b=xのxは変数に見えますが、メソッド呼び出しなのですね。


バグか変な挙動があれば、「なんてinconsistentな!!これだからRubyは!!」って言おうと思ったのですが、どうやらあてが外れました。