クロージャ
クロージャはブロックの内部に変数を保持できる、ということはいろんなところで書いてあります。
Rubyのブロックはそれ自身をオブジェクト化することができ、そうすることによってメモリ上に独立して存在できるようになる。
lambda { n += 1 } # => #<Proc:0x0001f57c@-:27>ブロックをオブジェクト化したものはProcクラスのインスタンスであり、callメソッドを呼ぶことによってブロック内の手続きを呼び出すことができる。そしてこの場合でもブロックがその外側で定義されたローカル変数を参照できるという性質は保たれる。このようなブロックの性質はクロージャと呼ばれる
n = 0 main = lambda { n += 1 } main.call # => 1 main.call # => 2 main.call # => 3
このカウントアップの例がよく挙げられますが、だからどうなんだろう、と思ってしまったり。
そして、内部保持しているといっても外からの擾乱を受ける。
n = 1 main.call # => 2
lambda(Proc)宣言以前に存在する変数は取り込まれる
クロージャというか手続きlambda/Proc(以下はlambdaで統一)は、その宣言/生成の時点での環境の変数をそのlambdaの中に取り込みます。
pry(main)> a = 0 => 0 pry(main)> b = 1 => 1 pry(main)> l = lambda{ puts a,b } => #<Proc:0x007fc28224bd18> pry(main)> l.call 0 1 => nil pry(main)> a = 2 => 2 pry(main)> l.call 2 1 => nil
aもbもlの中に取り込まれています(囲い込み)。そしてaを変更したらlに反映されます(外部が見える)。
lambda(Proc)宣言以降に出現する変数は取り込めない
で。
lambdaを生成する時点で環境に存在していない変数はどうなるのか。
pry(main)> ll = lambda{ puts c,d } => #<Proc:0x007fc282128bc0@(pry):43 (lambda)> pry(main)> c = 3 => 3 pry(main)> d = 4 => 4 pry(main)> ll.call NameError: undefined local variable or method `c' for main:Object from (pry):43:in `block in __pry__'
やはりlambdaに取り込まれていません。
「授業開始時点で着席していない変数には単位あげません」
的な(なんだそりゃ)。
でも。先にlambdaを宣言しておいて、それ以降に環境に生成された変数を取り込むことは当然ながら可能ですよね。ブロック変数に入れれば良いわけです。
pry(main)> lll = lambda{ |y, z| puts y, z } => #<Proc:0x007fc28111d2a8> pry(main)> w = 10 => 10 pry(main)> x = 11 => 11 pry(main)> lll.call(w, x) 10 11 => nil
lambda宣言時に存在しなかった変数をローカル変数に使うとブロック内ローカル変数になる
lambda宣言時に環境に存在しなかった変数をlambdaで使っても、環境側には見えません。lambda内のローカル変数になっている。
pry(main)> a = 40 pry(main)> b = 2 pry(main)> l = lambda{ puts a,b; s = a + b; puts s } => #<Proc:0x007fc2810ff410@(pry):65 (lambda)> pry(main)> l.call 40 2 42 => nil pry(main)> s NameError: undefined local variable or method `s' for main:Object from (pry):67:in `__pry__'
もちろん宣言後にlambda内のローカル変数と同名の変数を環境で使っても、当然ながら反映されず。
pry(main)> s = 100 pry(main)> l.call 40 2 42 => nil pry(main)> s => 100