2005/01/20

Rubyにおけるクロージャーの例だけど、M氏に指摘されて、より「環境を保持してる」っぽく書き換えた。当初のものでは Proc のインスタンスを生で print していたのを make_power_gen という関数で覆っただけだが、出力を見れば、この関数ジェネレータが「環境を保持している」という点がはっきりする。

def make_power_gen
prev = 1
Proc.new{
prev = prev * 2
}
end

power_gen_1 = make_power_gen
print "power_gen_1: ", power_gen_1.call, ", ", power_gen_1.call, "\n"

power_gen_2 = make_power_gen
print "power_gen_2: ", power_gen_2.call, ", ", power_gen_2.call, ", ", power_gen_2.call, "\n"

print "power_gen_1: ", power_gen_1.call, ", ", power_gen_1.call, "\n"

実行結果

power_gen_1: 2, 4
power_gen_2: 2, 4, 8
power_gen_1: 8, 16


出力例では、make_power_gen を2回呼び出して関数を2つ(power_gen_1とpower_gen_2)作り出し、それぞれ何回か実行している。ポイントは、power_gen_1 における変数 prev の値が、power_gen_2 の実行によって影響を受けていないこと。つまり、関数ジェネレータ make_power_gen がきちんと機能している。クロージャーでなく、変数 prev が関数内でローカルなスコープを持っていたら、こんなとき power_gen_1, … ,power_gen_n をすべて個別に定義するしかない。

No comments :