[Ruby入門] 16. Proc と lambda（処理ブロックと空間をオブジェクト化する）

&gt;&gt; 連載の目次は [こちら！](http://qiita.com/prgseek/items/237d02be844534063f1d)

今回は、処理ブロックやそのスコープを、Procでオブジェクト化する方法を学んでみたい。
かなりディープなお話だと思うので、入門編としては概要をつかむ程度になると思うが、ひと通りの扱い方や特徴については、ここで理解しておきたい。


### ■ 参考URL

http://melborne.github.io/2014/04/28/proc-is-the-path-to-understand-ruby/

http://www.atmarkit.co.jp/ait/articles/1409/29/news035_3.html#031

http://qiita.com/ryo-ma/items/24c46592b45775e8644d



### ■ Proc と lambda の概要

メソッドをオブジェクト化したのが、「Method」オブジェクト（これは [08. メソッドを定義する](http://qiita.com/prgseek/items/f91f71349164c3376acc#-メソッドをオブジェクトとして扱う) を参照）で、
ブロックをオブジェクト化したのが「Proc」オブジェクト。

ブロックがProcとしてオブジェクト化された時、そのブロック内の処理だけではなく、そのブロックが書かれた空間（スコープと同義？）も一緒に封じ込められる。
これは、いわゆる「クロージャ」をRubyで実現するものであり、Procの肝となる概念だが、複雑なので後述の例文で実際に動かして理解する。

lambda は、独自の記法でProcを生成する手段。
ただし、lambda記法で生成されたProcは、Procのnewやprocメソッドで生成されたProcオブジェクトとは微妙に振る舞いに違いがあるので注意が必要（後述）




### ■ 基本的な使い方

```ruby
# ################################################
# Proc の生成
# ################################################

# Procのnewで生成
p1 = Proc.new { |str| puts str }
p p1  #<Proc:0x007fd1360cdfa0@/Users/miro/Documents/RubyProjects/MyRuby/mytest2.rb:2>

# proc メソッドで生成
p2 = proc { |str| puts str }
p p2  #<Proc:0x007fd1360cde88@/Users/miro/Documents/RubyProjects/MyRuby/mytest2.rb:5>

# lambda メソッドで生成
p3 = lambda { |str| puts str }
p p3  #<Proc:0x007fd1360cddc0@/Users/miro/Documents/RubyProjects/MyRuby/mytest2.rb:8 (lambda)>

# lambda メソッドの省略形
p4 = ->(str) { puts str }
p p4  #<Proc:0x007fd1360cdd20@/Users/miro/Documents/RubyProjects/MyRuby/mytest2.rb:9 (lambda)>

# lambda メソッドの省略形の引数なしパターン
p5 = -> { puts "I like it!" }
p p5  #<Proc:0x007fd1360cdc80@/Users/miro/Documents/RubyProjects/MyRuby/mytest2.rb:10 (lambda)>



# ################################################
# Proc の実行
# ################################################

# call で実行
# call の引数が、そのままProcの実行ブロックのパラメータとして渡される。↑上記の |str| の部分。 
p1.call("wa-i")
p2.call("lalala-")
p3.call("hello")
p4.call("hey")
p5.call

# ※ちなみに、メソッドの回でも書いたが、上記の「call」メソッドは省略記法があるので注意。
p1["waiwai"]
p2.("gayagaya")



# ################################################
# メソッドに Proc を渡す
# ################################################

# メソッドの引数としてProcを渡して実行する
def exec_proc(arg, proc)
  proc.call(arg)
end
p = ->(n) { puts n * n }
exec_proc(4, p)  # 16


# メソッド側で、普通のブロックで受け取った引数をProcに変換して実行する
def exec_proc(arg, &callable)
  callable.call(arg)
end
exec_proc(3) { |n| puts n * n }  # 9
# 仮引数に&を付けると、引数を to_proc でProcに変換したうえで、callが実行される
# ちなみに、メソッドの回で解説したMethodオブジェクトも to_proc でProcに変換可能


# Procに「&」を付けて渡して、yieldで実行する
def exec_proc(arg)
  yield(arg)
end
p = ->(n) { puts n * n }
exec_proc(5, &p)  # 25
# 上記は、下記と同じこと
exec_proc(5) { |n| puts n * n }  # 25

```


### ■ Proc の生成方法による微妙な違い

Proc は、Proc.new で生成した場合と、lambdaで生成した場合で、微妙に挙動が異なる。
その違いをここで整理しておく。

#### ● return の挙動の違い


以下の通り、
Proc.new で生成したProc内でreturnすると、親空間(？)にあたるメソッドも抜けてしまう。
いっぽう、lambdaで生成したProc内でreturnしても、親空間(？)にあたるメソッドの処理は続行される。


```ruby
def method1
  p = Proc.new { return "Proc return!" }
  p.call
  return "method1 return!"
end
 
def method2
  p = -> { return "lambda return!"  }
  p.call
  return "method2 return!"
end
 
puts method1  # Proc return!
puts method2  # ethod2 return!
```


#### ● break の挙動の違い

breakも、returnと同様、
Proc.new で生成したProcからbreakすると、親空間(？)にあたるループも抜けてしまう。
いっぽう、lambdaで生成したProcからbreakしても、親空間(？)にあたるループは続行される。


```ruby
p = Proc.new { puts "Proc break!"; break; }
3.times do |i|
  puts "loop1: #{i}"
  p.call
end
  # loop1: 0
  # Proc break!
 
p = -> { puts "lambda break!"; break; }
3.times do |i|
  puts "loop2: #{i}"
  p.call
end
  # loop2: 0
  # lambda break!
  # loop2: 1
  # lambda break!
  # loop2: 2
  # lambda break!
```


#### ● 引数が足りない時の挙動の違い


以下の通り、
Proc.new で生成したProcに渡された引数が足りなかった場合、渡されなかった引数はnilになる
いっぽう、lambdaで生成したProcに渡された引数が足りなかった場合、例外が発生する


```ruby
proc = Proc.new { |arg1, arg2| p arg1, arg2 }
proc.call("wa-i")
  # "wa-i"
  # nil

proc = -> (arg1, arg2) { p arg1, arg2 }
proc.call("wa-i")
  # Uncaught exception: wrong number of arguments (given 1, expected 2)
```


### ■ クロージャとしてのProcの挙動を確認する


前述の通り、ブロックが、Procとしてオブジェクト化された時、そのブロック内の処理だけではなく、そのブロックが書かれた空間（スコープと同義？）も一緒に封じ込められる。これが、他言語における、いわゆる「クロージャ」に該当する。
抽象的なので、実際のコードで試してみる。


```ruby

# メソッドという `空間` を用意する。
# ある `空間` で生成された Proc には、
#   自分が生成された`空間`(この例ではメソッドのスコープ)までが、まるごと封じ込められている
def get_proc

  # nという変数が存在する空間がある
  n = 0;

  # その空間でProcを生成して返す
  ->(inc) { n += inc; puts n }
end

# Procを生成してp1に代入
p1 = get_proc
3.times { p1.call(5) }
  # 5
  # 10
  # 15
  # p1というProcには、インクリメント処理だけでなく、その空間(この例ではget_procというメソッドのスコープ)が封じ込められている。
  # そのため、上記のように同一のProcオブジェクトから複数回インクリメント処理を呼ぶと、
  #    同じ空間に存在するnという変数は共有されて増えていくことが確認できる

# Procを生成してp2に代入
p2 = get_proc
3.times { p2.call(4) }
  # 4
  # 8
  # 12
  # こちらは新規にProcを生成しているので、先程の３回インクリメントとは別空間となっていることが分かる。
```


### ■ Proc をcase文の条件分岐に利用する

Procクラスでは、「===」がcallメソッドのエイリアスとして定義されている。
なので、何かしらの値とProcの戻り値を比較して、条件に該当するかどうかチェックすることができる。

これを利用して、case文のwhen節にProcを指定することができる。

```ruby

def check_fun(chk)

  is_event = -> (name) { ["GW", "夏休み", "クリスマス"].include?(name) }

  case chk
  when is_event then puts "楽しい！ ヾ(＠＾▽＾＠)ﾉ"
  else puts "つまんない (´・_・｀)"
  end

end

check_fun("夏休み")
# 楽しい！ ヾ(＠＾▽＾＠)ﾉ
```