block、proc、lambdaの話

今までRubyの世界にblock、proc、lambdaはいつても避けられない話と思うので、今回はも一回雑談させていただきます。


# block

blockを含めたメソッドは下記のような書いていきます。

```
def method(*args, &block)
  ...
end
```

## 実験
### 引数みたいな`&block`は実際に引数ではなく、声明のような物となります。

```
[20] pry(main)> def method(&block)
[20] pry(main)* end
=> :method
[21] pry(main)> method(1)
ArgumentError: wrong number of arguments (given 1, expected 0)
from (pry):21:in `method'
[22] pry(main)> method()
=> nil
[23] pry(main)> method() { p 'hello' }
=> nil
```

上記の結果によって、`&block`は引数でないことを判明します。

### blockの定義と呼び出す

```
[25] pry(main)> def method(arg)
[25] pry(main)*   p arg
[25] pry(main)* end
:inspect
=> :method
[26] pry(main)> method(1) { p 2 }
1
=> 1
```

上記の`def method(arg)`で定義されたblockは呼ばれていないので、異常なしに`p 1`の結果を出力しました。

```
[27] pry(main)> def method1
[27] pry(main)*   yield
[27] pry(main)* end
:inspect
=> :method1
[28] pry(main)>
[29] pry(main)> def method2(&block)
[29] pry(main)*   block.call
[29] pry(main)* end
:inspect
=> :method2
```

blockを呼び出せたいのなら、`yield`や`&block`を使えましょう。

```
[30] pry(main)> method1
LocalJumpError: no block given (yield)
from (pry):33:in `method1'
[31] pry(main)> method2
NoMethodError: undefined method `call' for nil:NilClass
from (pry):36:in `method2'
```

`yield`や`&block`を使う場合に、`method1`や`method2`を実行する際はblockを追加しなければいけないです。と言う分けて、rubyには`block_given?`という判定メソッドがあります。

```
[32] pry(main)> def method1
[32] pry(main)*   yield if block_given?
[32] pry(main)* end
:inspect
=> :method1
[33] pry(main)>
[34] pry(main)> def method2(&block)
[34] pry(main)*   block.call if block_given?
[34] pry(main)* end
:inspect
=> :method2
[35] pry(main)> method1
:inspect
=> nil
[36] pry(main)> method2
:inspect
=> nil
```

上記のコードで`block_given?`の判定がfalseなので、`yield`や`block.call`を実行せずにnilを返しました。

#### 引数もチャックしない

```
[25] pry(main)> def method3(arg1, arg2)
[25] pry(main)*   yield 10
[25] pry(main)* end
=> :method3
[26] pry(main)> method3(1, 2) do |arg1|
[26] pry(main)*   p arg1
[26] pry(main)* end
10
=> 10
[27] pry(main)> method3(1, 2) do
[27] pry(main)*   p 'nothing'
[27] pry(main)* end
"nothing"
=> "nothing"
```

### blockは何だろう

blockの本体を探すために、メソッドの後ろに何らかのblockつけて見ましょう。

```
[39] pry(main)> def method3(&block)
[39] pry(main)*   p block.class
[39] pry(main)*   p block.inspect
[39] pry(main)*   block.call
[39] pry(main)* end
:inspect
=> :method3
[40] pry(main)> method3 {}
Proc
"#<Proc:0x007f82028198f0@(pry):58>"
:inspect
=> nil
```

`&block`した結果はProcのinstance objectと判明しました。

### `&`との関係

```
[41] pry(main)> int_array = [1, 2, 3]
=> [1, 2, 3]
[42] pry(main)> int_array.map(&:to_s)
=> ["1", "2", "3"]
```

`&`というば、Ruby開発者は大体上記のコードを良く見えたと思いますが、`map(&:to_s)`の書き方は簡潔ですし、格好良いな感じです。その魔力の源はまさに`&`です。

```
class Symbol
  def to_proc
    proc { |obj, args| obj.send(self, *args) }
  end
end
```

`&`は:to_sのto_procを呼び出して、Procのinstance objectを返します。そして、`&`はその「Procのinstance object」をblockとして`map`につけます。
つまり、

```
int_array.map(&:to_s)
=>
int_array.map { |obj, args| obj.send(self, *args) }
```
となります。

### 真似て書こ

```
[53] pry(main)> class AddBy
[53] pry(main)*   def initialize(num = 0)
[53] pry(main)*     @num = num
[53] pry(main)*   end
[53] pry(main)*
[53] pry(main)*   def to_proc
[53] pry(main)*     proc { |obj, args| obj.send(:+, @num, *args) }
[53] pry(main)*   end
[53] pry(main)* end
:inspect
=> :to_proc
[54] pry(main)>
[55] pry(main)> add_by_3 = AddBy.new(3)
:inspect
=> #<AddBy:0x007f82521d3a18 ...>
[56] pry(main)> [1,2,3].map(&add_by_3)
=> [4, 5, 6]
```

### block のかた

####doの型：

```
do |*args|
  ...
end
```

####{} の型：

```
{ |*args| ... }
```

####& の型

```
&:to_s
&add_by_3
...
```

但し、どの型でも独立存在することはできない。メソッドを先に発動しなければ、blockの型は使わないという認識です。そして、blockは変数として保存することもできないし、引数として他のメソッドに渡されることもできないです。そのために、procは登場します。

# proc

## 変数として
```
[16] pry(main)> p = Proc.new(&:to_s)
=> #<Proc:0x00007f90c7cbb1c8(&:to_s)>
[17] pry(main)> p = proc(&:to_s)
=> #<Proc:0x00007f90c7cbb1c8(&:to_s)>
[18] pry(main)> p = proc {|obj| obj.to_s}
=> #<Proc:0x00007f90c7d0ae30@(pry):20>
[19] pry(main)> p = proc do |obj|
[19] pry(main)*   obj.to_s
[19] pry(main)* end
=> #<Proc:0x00007f90c7d9b110@(pry):21>
```

## 引数として

```
[13] pry(main)> p = proc { puts 'I am a proc' }
=> #<Proc:0x00007f90c7b4ebc8@(pry):13>
[14] pry(main)> def method_with_proc(p)
[14] pry(main)*   p.call
[14] pry(main)* end
=> :method_with_proc
[15] pry(main)> method_with_proc(p)
I am a proc
=> nil
```

## 実験

### blockと同じく引数もチャックせず

```
[28] pry(main)> p = proc do |arg1, arg2|
[28] pry(main)*   p arg1
[28] pry(main)*   p arg2
[28] pry(main)* end
=> #<Proc:0x00007f90be320db0@(pry):49>
[29] pry(main)> p.call
nil
nil
=> nil
[30] pry(main)> p.call('name')
"name"
nil
=> nil
[31] pry(main)> p.call('name', 'is', 'colin')
"name"
"is"
=> "is"
```

### blockとの関係

procとblockが別物でけど、procはblockをto_proc経由して実体化した物として理解して問題ないと思います。
そして、procのto_procはその自身を返す

```
[32] pry(main)> p = proc {}
=> #<Proc:0x00007f90c9867c00@(pry):56>
[33] pry(main)> p.equal? p.to_proc
=> true
```

# lambda

procと全く同じものかな？

```
[43] pry(main)> p = proc {}
=> #<Proc:0x00007f90c9aed5d8@(pry):67>
[44] pry(main)> l = lambda {}
=> #<Proc:0x00007f90c9b26130@(pry):68 (lambda)>
[45] pry(main)> puts p.class
Proc
=> nil
[46] pry(main)> puts l.class
Proc
=> nil
```

実は、

```
[47] pry(main)> puts p.inspect
#<Proc:0x00007f90c9aed5d8@(pry):67>
=> nil
[48] pry(main)> puts l.inspect
#<Proc:0x00007f90c9b26130@(pry):68 (lambda)>
=> nil
```

末尾の`(lambda)`はどういう事ですか？？？

## 実験

```
[54] pry(main)> def method_proc
[54] pry(main)*   p = proc { return 'I am a proc' }
[54] pry(main)*   p.call
[54] pry(main)* end
=> :method_proc
[55] pry(main)>
[56] pry(main)> def method_lambda
[56] pry(main)*   l = lambda { return 'I am a lambda' }
[56] pry(main)*   l.call
[56] pry(main)* end
=> :method_lambda
[57] pry(main)> method_proc
=> "I am a proc"
[58] pry(main)> method_lambda
=> "I am a lambda"
```

結局同じじゃないですか？
少々改修して、

```
[59] pry(main)> def method_proc
[59] pry(main)*   p = proc { return 'I am a proc' }
[59] pry(main)*   p.call
[59] pry(main)*
[59] pry(main)*   'I am a method'
[59] pry(main)* end
=> :method_proc
[60] pry(main)>
[61] pry(main)> def method_lambda
[61] pry(main)*   l = lambda { return 'I am a lambda' }
[61] pry(main)*   l.call
[61] pry(main)*
[61] pry(main)*   'I am a method'
[61] pry(main)* end
=> :method_lambda
[62] pry(main)> method_proc
=> "I am a proc"
[63] pry(main)> method_lambda
=> "I am a method"
```

`return`を使ったら、大体procとlambdaの区別が出てくる。

### lambdaは引数をチャックする

```
[64] pry(main)> l = lambda do |arg1, arg2|
[64] pry(main)*   p arg1
[64] pry(main)*   p arg2
[64] pry(main)* end
=> #<Proc:0x00007f90c7e62ee0@(pry):107 (lambda)>
[65] pry(main)> l.call('name')
ArgumentError: wrong number of arguments (given 1, expected 2)
from (pry):107:in `block in __pry__'
[66] pry(main)> l.call('name', 'is')
"name"
"is"
=> "is"
[67] pry(main)> l.call('name', 'is', 'colin')
ArgumentError: wrong number of arguments (given 3, expected 2)
from (pry):107:in `block in __pry__'
```

lambdaはメソッドみたいなものと認識れても問題ないと思います。

# 運用経験

そう考えたら、実際運用でblockとlambdaがあれば、十分ではないですか？procは蛇足な設計かな？
自分自身の経験によって、procはpowerfulと思います。
例えば、railsのcontrollerで、

```
def some_action(...)
  ...
  return render :some_view, error: message1 if condition1
  ...
  return render :some_view, error: message2 if condition2
  ...
  return render :some_view, error: message3 if condition3
  ...
end
```

改修した後は、

```
def some_action(...)
  some_proc = proc do |condition, message|
    return render :some_view, error: message if condition
  end
  ...
  some_proc.call(condition1, message1)
  ...
  some_proc.call(condition2, message2)
  ...
  some_proc.call(condition3, message3)
  ...
end
```

# まとめ

block、proc、lambdaについて纏めました。
理解不足により、認識が間違っている点や不明点等あればご指摘下さい。