λx.(x + a) のようなラムダ式で、xは仮引数(ラムダ式の束縛変数といいます)ですが、定義本体内に仮引数リストにない変数aが出てきますよね。aみたいな、仮引数宣言されてない変数を自由変数と呼びます。
自由変数が、いつどのように束縛(具体化)されるかは、けっこう重要な問題です。で、調べてみる。
// 自由変数の束縛タイミング
a = 3
X = {|x| x + a}
a = 8
println X(2) // 5か10か?
10
実行時に束縛されてる。ウーン、これはどうかなぁ。インタプリタでは、まー、普通の動作なのですが、実行時の環境によってクロージャの挙動が変わってしまいます。それを利用するテクニックもあるでしょうけど、分かりにくくてハマると思いますよ。
束縛が実行時(評価時)に起きるなら、定義時の環境(自由変数の束縛状況/値)はどうでもいいはずです。なのに…
// 自由変数の束縛タイミング
// aの値は未定義
X = {|x| x + a}
a = 8
println X(2)
Caught: java.lang.NullPointerException
at lambda2.run(C:\Documents and Settings\Hiyama\tmp\lambda2.groovy)
at lambda2.main(C:\Documents and Settings\Hiyama\tmp\lambda2.groovy)
なんで例外を出すのですか、君は>groovy
それはともかく、こんなこともできるわけですが:
// クロージャ内から環境の変数を変更する
a = 3
X = { a = 10 }println a
X()
println a
3
10
できるからって、むやみに使うもんじゃないね。
(続く、けど明日かな)