第129回 クロージャは作られたときの状態を保持するわけではなく参照する
第55回 クロージャ - bingo_nakanishiの他言語出身者のためのPerl入門
第56回 クロージャの実践的使いどころ - bingo_nakanishiの他言語出身者のためのPerl入門
第58回 クロージャをもっとていねいに - bingo_nakanishiの他言語出身者のためのPerl入門
私は今まで、「クロージャは作られたときの状態を保持する」という言い方をしてきたが、
これは厳密には間違いだったようだ。
状態を保持しているように見える例
use strict; sub h{ my $c = shift; sub { $c++ }; }; my $h = h(5); print $h->(), "\n"; print $h->(), "\n"; print $h->(), "\n";
結果:
5 6 7
この例だと、確かに作られたときの状態を持っており、その状態に変化を加えているように見える。
厳密に言うと「作られたときの変数を参照している」
しかし、次の例を見てみると、
use strict; sub h{ my $c = shift; my $fun = sub { $c++ }; # ここで作った $c = 10; # $cを変更 return $fun; # 作った関数を返すのはココ } my $h = h(5); print $h->(), "\n"; print $h->(), "\n"; print $h->(), "\n";
結果:
10 11 12
このように、$cは作られたときではなく、$c = 10;が実行されたものを見ている。
つまりこれは、関数が作れたときの状態を保持するのではなく、作られたところの外側の変数をただ参照していることを示している。
同じ変数をクロージャとして持つ関数を2つ返してみる
use strict; sub h{ my $c = shift; [ sub{ $c++; print $c, "\n" }, sub{ $c++; print $c, "\n" } ]; } my($fun1, $fun2) = @{h(5)}; $fun1->(); $fun1->(); $fun2->(); $fun1->();
結果:
6 7 8 9
このようにお互いに同じ変数をクロージャとして持つ場合は、
参照であるので、お互いの関数は同じ$cを更新している。
Rubyでも念のために見てみる
class Hoge def foo c = 1 [1,2,3].each do |i| c=c+1 # cを更新 puts c end puts puts c # cが更新されているのがわかる end end Hoge.new.foo
結果:
2 3 4 4
確かに外側のcが更新されている。