オプショナル引数について考える
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は!!」って言おうと思ったのですが、どうやらあてが外れました。