Rubyのブロック、Proc、Lambdaって何？

Rubyをはじめると、ブロックとかProcとかLambdaとか、
みんな似たような雰囲気をしているから、うまく理解しづらいですよね。

復習も兼ねて、余計な説明抜きで、ブロック・Proc・Lambdaについて説明していきます。

<h1>ブロック</h1>
ブロックはクロージャです。
ですので、ブロックを使うことで、普段参照できないローカル変数を参照できるようになります。

```block.rb
class Sample
  x = 1
  y = 1

  define_method :output do
    x + y
  end
end

sample = Sample.new
puts sample.output # => 2
```

上記のコードにある、define_methodの、
doからendまでの部分（{}を利用することもできます）が<b>ブロック</b>です。

each文やmapもブロックのロジックを実行します。

```block.rb
x = 10
array = [1, 2, 3, 4]

# each
array.each do |num|
  puts x + num # => 11, 12, 13, 14
end

# map
array.map do |num|
  puts num * x # => 10, 20, 30, 40
end
```

また、ブロック内のロジックが一文だけなら、

```block.rb
# each
array.each { |num| puts x + num }

# map
array.map { |num| puts num * x }
```

このように記述すると読みやすいかもしれません。

ブロックはインスタンスメソッドに渡して、メソッド内で利用することもできます。

```block.rb
x = 1

def output
  yield * 10
end

puts output { x * 10 } # => 100
```

outputメソッドはスコープの違う変数xを参照することができませんが、
クロージャであるブロックは別です。
outputの引数としてxを含めたブロックを渡し、メソッド内のyieldでそれを展開しています。

ブロックは、メソッド定義時に明示しなくても、呼び出し時に引数としてブロックを記述することで、ブロックが渡されたと判断されます。
その後にyieldがブロックを展開するのです。
こうして、メソッドはそのスコープの外にあるxを利用して、計算を実行することができるようになりました。

上のコードの場合、outputメソッドの中身は、
(x * 10) * 10
となるわけです。

<h1>Proc</h1>
しかし、ブロックは利用の仕方に限りがあります。

ブロックは、いわば、バラバラのコードを束ねた塊に過ぎず、これ自体はオブジェクトとして扱われません。
ですので、ブロックは変数に渡すことができず、利用するときはブロックを利用するメソッド（newやdefine_methodなど）の利用時に限られてしまいます。

このようなブロックの性質を補うのが、Procです。
Procは、ブロックをオブジェクトに変換します。

```proc_statement.rb
x = 1
y = 1

block = Proc.new { |z| (x + y) * z }
puts block.call(10) # => 20
```

Proc.newにブロックを渡すと、そのブロックのオブジェクトを作成します。
上記のコードでは、変数blockにブロックのオブジェクトを格納しています。

出力するときは、callメソッドで呼び出します。
call()の中身は作成したブロックの引数です。
ブロック定義時に||の中身をセットすると、それが引数として扱われます。
上記のコードの中では、引数に10がセットされ、ブロック内の|z|が|10|に変化します。

Procをインスタンスメソッドに渡す場合は、ブロックをメソッドに渡してから、以下ようにメソッド定義時に&をつけた引数を記入します。

```proc_statement.rb
x = 1
y = 1

def output(&block)
  block.call * 10
end

puts output { x + y } # => 20
```

&はブロックをオブジェクトに変換します。つまり、上のコードの場合、ブロックをオブジェクトに変換し、それをblockという変数の中に格納しているわけです。
メソッド内には&を取り除いた引数をcallで呼び出して、ブロックを展開しています。
もちろん、すでにProcとなっているブロックを渡すこともできます。

```proc_statement.rb
x = 1
y = 1

def output(&block)
	block.call * 10
end

block = Proc.new { x + y }
puts output &block # => 20
```

また、&はオブジェクトをブロックに変換することもできます。
したがって、以下のコードも可能です。

```proc_statement.rb
x = 1
y = 1

def output
  # ブロックが引数に入ってきているので、yieldを使う。
  yield * 10
end

block = Proc.new { x + y }

# Procをブロックに変換して、メソッドに渡す。
puts output(&block) # => 20
```

<h1>Lambda</h1>
Lambdaには字面的に難解な雰囲気が漂いますが、Procの色違いのようなものです。
挙動が少しProcとは異なりますが、やることは基本同じことです。

```lambda_statement.rb
x = 1
y = 1

block = lambda { |num| (x + y) * num }
puts block.call(10) # => 20
```

また、以下は上記のコードと同じように振る舞います。

```lambda_another_statement.rb
x = 1
y = 1

block = ->(num) { (x + y) * num }
puts block.call(10) # => 20
```

```lambda_another_statement.rb
x = 1
y = 1

block = -> { (x + y) * 10 }
puts block.call # => 20
```

Procと同様に、Lambdaの後ろにブロックを繋げ、必要であれば||に引数をセットすることができます。
callで呼び出し、ブロックの中に引数がセットされているのであれば、call(10)のようにして、引数に入れるものを指定できます。

Lambdaを引数としてメソッドに渡す場合は、以下のコードを参考にしてください。
基本的にProcと同じです。

```lambda_statement.rb
# ブロックをオブジェクトに変換するだけならProcとまるっきり同じ。
# 引数の&は、ブロックをオブジェクトに変換し、変数blockに格納したと考えれば良い。
x = 1
y = 1

def output(&block)
  block.call * 10
end

puts output { x + y } # => 20
```

```lambda_statement.rb
x = 1
y = 1

def output(&block)
  block.call * 10
end

block = lambda { x + y }
puts output &block
```

```lambda_statement.rb
x = 1
y = 1

def output
  yield * 10
end

block = lambda { x + y }
puts output(&block)
```

<h1>ProcとLambdaの違い</h1>
ProcとLambdaはブロックをオブジェクトにしたもので、両者はProcクラスに分類されるという点で同等です。

```difference.rb
proc = Proc.new { |x| x * 10 } # => ProcクラスのProc

lambda = lambda { |x| x * 10 } # => ProcクラスのLambda

puts proc.class # => Proc
puts lambda.class # => Proc

puts proc.lambda? # => False
puts lambda.lambda? # => True
```

LambdaはProcの色違いのようなものだと説明しましたが、それは、Procに属する毛色の違うProcである、というわけです。
反対に、Procに属するLambdaではないProcは、どう頑張ってもLambdaではありません。Lambdaになるのは、lambda{}と->{}の記法で作成されたProcのみです。lambdaかどうかを判断するには、lambda?メソッドが役に立ちます。

しかしながら、より厳密に言えば、それぞれは異なる挙動を行います。

ProcとLambdaの相違点を見分けるのはややこしいですし、例外もさまざまあるようなので、完全に理解する必要はないと思いますが、代表的な挙動の違いとして、returnの扱い方と、引数の扱い方の違いを説明します。

まずはそれぞれのreturnに対する挙動を確認します。

```difference.rb
# Procの場合
def proc_output
  block = Proc.new { return 100 }
  called = block.call # => ブロックのスコープにあるreturnによってproc_outputの戻り値を返す
  return called * 10
end

# Lambdaの場合
def lambda_output
  block = lambda { return 100 }
  called = block.call
  return called * 10 # => メソッドのスコープにあるreturnによってlambda_outputの戻り値を返す
end

puts proc_output # => 100
puts lambda_output # => 1000
```

上記のコードから、
Procのブロック内にあるreturnは、callで評価された時に、メソッドのスコープを抜け出しますが、
Lambdaのブロック内にあるreturnは、callで評価された後、メソッドのスコープを抜け出さず、変数に格納され、メソッドのreturnによって戻り値になり、メソッドのスコープを抜け出しています。

もう１つのProcとLambdaの相違点は、引数の取り扱い方の違いにあります。

```difference.rb
# Procの場合
proc = Proc.new { |x| x * 10 }
puts proc.call(5) # => 50
puts proc.call(5, 10) # => 50

# Lambdaの場合
lambda = lambda { |x| x * 10 }
puts lambda.call(5) # => 50
puts lambda.call(5, 10) # => wrong number of arguments (given 2, expected 1)
```

Procでは、余分に引数を渡していますが、正常に動いています。
対して、Lambdaでは、余分に引数を渡すとエラーが発生します。

Procは引数を多めに渡しても、少なめに渡しても、エラーを発生させることなく動いてくれます。
余分な引数は切り捨て、足りない分はnilとして許容するわけです。

しかし、Lambdaはそれを許しません。
より厳密に引数を受け取りたがります。

おさらいすると、ProcとLambdaには以下のような相違点があります。
1. returnの挙動が異なる
  ・Procでは、returnを行うと、メソッドを抜けて、returnの値を戻す。
  ・Lambdaでは、returnを行うと、returnした値をロジックに組み込んでメソッドの最後まで計算する。
2. 引数に対する厳格さ
  ・Procでは、設定した引数の数に対して渡す引数の数は幾つでもエラーにならない。
  ・Lambdaでは、設定した引数の数に対して、厳密な数の引数を対応させて渡さなければエラーになる。

このようなエラーがあるわけですが、どちらかというとLambdaを使った方が良いみたいです。
理由はProcのように引数に対して寛容すぎると、ヒューマンエラーで間違った数の引数を渡した時に、画面上ではエラーにならないので、そのエラー自体に気づきにくくなってしまうからです。
間違った時にはしっかりとエラーを吐いてくれるLambdaを利用した方が、プログラミングする側からすると、楽だとおもいます。