simanのブログ

ゆるふわプログラマー。競技プログラミングやってます。Ruby好き

RubyでのProcとlambdaとクロージャー

Procとlambdaを使ってクロージャを書く。

自分の中のクロージャの認識は「独立したローカル変数の操作」程度である

 

簡単なサンプルとしてはカウンティングがよくある

 

[1] pry(main)> def closure

[1] pry(main)*   count = 0

[1] pry(main)*   return lambda { return count+=1 }

[1] pry(main)* end  

=> nil

[2] pry(main)> c = closure

=> #<Proc:0x007ff73aa7e750@(pry):3 (lambda)>

[3] pry(main)> c.call

=> 1

[4] pry(main)> c.call

=> 2

[5] pry(main)> count = 100

=> 100

[6] pry(main)> c.call

=> 3

 
こんな感じ。
 
closureメソッドを呼び出すたびにメソッド内のcount変数1ずつカウント
されている。しかも、途中count変数を新しく作成し100を代入しても、
closureメソッド内のcount変数には影響が無いことがわかる

 

だが、Procでも同様のことをやろうとするとおかしなことになる 

 

[1] pry(main)> def closure

[1] pry(main)*   count = 0

[1] pry(main)*   return Proc.new { return count += 1 }

[1] pry(main)* end  

=> nil

[2] pry(main)> c = closure

=> #<Proc:0x007fef9aa7bed0@(pry):3>

[3] pry(main)> c.call

LocalJumpError: unexpected return

from (pry):3:in `block in closure'

 

こんな感じで「LocalJumpError: unexpected return」が発生する。

これはlambdaとProcにおけるreturnの扱いに違いがある。

 

lambdaではclosureメソッド内でのreturnと同じ扱いになるのだが、

Procの場合はcallを行った位置でのreturnの扱いとなる。そのため

今回はトップレベルでのreturnを行ったと同じ扱いになったため、

「LocalJumpError: unexpected return」が発生してしまったのである。

 

Procでやる場合は

 

def closure

  count = 0

  return Proc.new { count += 1 }

end

 

c = closure

p c.call

p c.call

p c.call

 -------------------------------------------------------------

1

2

3

--------------------------------------------------------------

こんな感じになるのか。Procとlambdaで大分挙動が違うから扱いには注意

しないと大変なことになりそう。

 

 

参考文献

http://doc.ruby-lang.org/ja/1.9.3/class/Proc.html