RubyのProcとは何者なのか？

# はじめに
初めまして。

現在、未経験からのWebエンジニア転職を目指し、プログラミングスクールにて学習中の　　　 だい と言います。
RubyやRuby on Railsを中心とした学習を進める中でProcという用語に出会い、いまいち理解できないまま今に至っているので、この記事では振り返りも兼ねてまとめてみようと思います。

::: note warn
初学者のため、記事の内容に誤りがある可能性がございます。
誤った内容を記述している箇所がございましたら、コメント等でご教示いただけると幸いです。
:::

# Procって何？
早速、本題の「Procとは何者なのか？」に切り込んでいこうと思います。

公式のリファレンスマニュアルを見ると、ProcとはRubyがデフォルトで用意しているクラスの一つのようです。以下のように説明されています。

> ブロックをコンテキスト(ローカル変数のスコープやスタックフレーム)とともにオブジェクト化した手続きオブジェクトです。
> 
> 
> [Ruby 3.2 リファレンスマニュアル](https://docs.ruby-lang.org/ja/latest/class/Proc.html)
> 

なんだか難しいですが、以下2つに分けて考えてみようと思います。

1. ブロックをオブジェクト化したもの
2. （ブロックを）コンテキストとともにオブジェクト化したもの

それぞれについて見ていきましょう。

## 1. ブロックをオブジェクト化したもの
ブロックとは、一連の処理をまとめたカタマリを指します。`do~end`または`{}`で囲まれたアレですね。
  
  ```ruby
  # do~endで書く場合
  [1, 2, 3].each do |n|
    puts n * 2
  end
  
  # {}で書く場合
  [1, 2, 3].each { |n| puts n * 2 }
  ```

このブロック内の処理をまとめてオブジェクトとして扱おう！と作られたものが、Procオブジェクトというわけです。

Procオブジェクトは、「Procクラスから生成されるインスタンス」を指します。

Procオブジェクトに変換することで、複数の箇所で使いたいブロックの処理を変数に代入し、好きな場所で引数として呼び出すことができるようになります。

```ruby
# Procオブジェクトを用いない場合 (その都度ブロックを記述する)
[1, 2, 3].each { |n| puts n * 2 }
[4, 5, 6].each { |n| puts n * 2 }

# Procオブジェクトを用いる場合 (変数に代入して複数箇所で使える)
proc = Proc.new { |n| puts n * 2 }
[1, 2, 3].each(&proc)
[4, 5, 6].each(&proc)
```
※ Procオブジェクトの作り方、呼び出し方については後述します。

通常のブロックでは記述されたその場でしか処理を実行できないため、この点はProcの大きな特徴といえます。

## 2. （ブロックを）コンテキストとともにオブジェクト化したもの

コンテキストとともにオブジェクト化、と言われてもよくわからないので、まずは簡単な例を見てみましょう。

```ruby
message = "おはようございます"

greeter = Proc.new { puts message }

greeter.call # => "おはようございます"
```

※ Procオブジェクトの作り方、呼び出し方については後述します。。。

上記の例では、`greeter = Proc.new { puts message }`でProcオブジェクトが生成される前に、ローカル変数`message`が定義されていますね。

Procオブジェクトは、自身が生成される時点で存在する外部の変数（上記例の場合は`message`）を記憶し、その参照先の値を扱うことができます。

そのため、`greeter.call`でProcオブジェクトを呼び出すと、変数`message`に代入された文字列`"おはようございます"`が取り出されるというわけです。


ちなみに、Procオブジェクト生成後に変数の値を再代入すると、変更後の値が取り出されます。

```ruby
message = "おはようございます"

greeter = Proc.new { puts message }

message = "こんにちは"

greeter.call # => "こんにちは"
```

※ Procオブジェクトの作り方、呼び出し方については後述します。。。。。

これは、Procオブジェクトが記憶しているものが**変数に代入された値自体**ではなく、**変数から値への参照**であるためです。

【参照のイメージ】
[![Image from Gyazo](https://i.gyazo.com/1a61a7cd56c051123f75c0529ce7a099.png)](https://gyazo.com/1a61a7cd56c051123f75c0529ce7a099)

参照については、[こちらの記事](https://qiita.com/yusuke_blog1026/items/7261ccaf23214dacb37b)がわかりやすかったです。

ローカル変数のスコープ（コンテキスト）を記憶しつつブロック内の処理をオブジェクト化するため、「コンテキストとともにオブジェクト化」と説明されていたのですね。

# Procオブジェクトの使い方

ここでは、上の例でも少し登場した、Procオブジェクトの作成および実行の仕方について見ていきたいと思います。

## 実行する

作成したProcオブジェクトに対して`call`メソッドを呼び出すことで、Procオブジェクトを呼び出して処理を実行できます。

## 作成する

作成の方法は、以下のとおり4種類あります。

### 1. `Proc.new`で作る

```ruby
proc_a = Proc.new { |n| n * 4 }
puts my_proc.call(2) #=> 8
```

お馴染みの`new`メソッドの引数にブロックの処理を渡すことで、Procクラスのインスタンスを作成しています。

### 2. `proc`メソッドでブロックをProcオブジェクトに変換

```ruby
proc_b = proc { |n| n * 4 }
puts my_proc.call(2) #=> 8
```

ブロックをProcオブジェクトに変換する`proc`メソッドを用いて、Procクラスのインスタンスを作成しています。

### 3. メソッドの引数として渡す

```ruby
def proc_generate(&proc_c)
  puts proc_c.call(2)  #=> 8
end

proc_generate { |n| n * 4 }
```

`proc_generate`というメソッドの引数としてブロック（`{ |n| n * 4 }`）を受け取り、Procオブジェクトを作成しています。

メソッドの仮引数の先頭に`&`をつけることで、呼び出し元から渡されるブロックがProcオブジェクトに変換されます。

メソッド内では、生成されたProcオブジェクトに対して`call`メソッドを呼び出すことで、処理を実行しています。

### 4. `lambda`を用いて作成する

こちらについては後述します。

# `lambda`(ラムダ)って何？
Procが何者か少しずつわかってきたところで、またよくわからない用語が出てきました…

`lambda`とは、Procオブジェクトを作る別の方法を指します。
（正しい定義は、「Rubyで無名関数を作るためのひとつの方法」です。）

`lambda`を使ったProcオブジェクトの作成方法を見てみましょう。

```ruby
#lambdaを使ってProcオブジェクトを生成する
my_proc = lambda { puts 'Proc' }

# ->構文でもlambdaと同様にProcオブジェクトを生成できる
# my_proc = -> { puts 'Proc' }  

#Procオブジェクトの実行
my_proc.call

#実行結果
Proc
```

`lambda`によって作成されたProcオブジェクトは、`Proc.new`などその他の方法で作成されたオブジェクトと挙動が異なる点があります。

一例として、`lambda`によって作成されたProcオブジェクトの方が、引数の数が厳密に判定されます。

- `Proc.new`の場合
実行時に渡される引数の数が合わなくても、過不足分は無視して実行される

    ```ruby
    my_proc = Proc.new { |a, b| a + b }
    
    my_proc.call(1, 3) 
    # => 4
    
    my_proc.call(1)
    # => 1
    
    my_proc.call(1, 3, 5)
    # => 4
    ```
    


　　
- `lambda`の場合
実行時に渡す引数の数は厳密に判定され、過不足がある場合はエラーが発生する
    
    ```ruby
    my_lambda = lambda { |a, b| a + b }
    
    my_lambda.call(1, 3)
    # => 4
    
    my_lambda.call(1)
    # => wrong number of arguments (given 1, expected 2) (ArgumentError)
    
    my_lambda.call(1, 3, 5)
    # => wrong number of arguments (given 3, expected 2) (ArgumentError)
    ```
    

この辺りは、[こちらの記事](https://losenotime.jp/ruby-proc/)で詳しく解説されていたので、詳しく知りたい方はぜひ確認してみてください。

# まとめ
Procオブジェクトとは、

- ブロックをコンテキストとともにオブジェクト化したもの
- ブロック内の処理を変数に格納し、複数の箇所で呼び出せる
- 呼び出す際は`call`メソッドを使う

実際にProcオブジェクトを扱ったことがないため教科書的な説明に留まりましたが、開発の場面で遭遇した際にまた理解を深められるよう、学習を進めていこうと思います。

最後まで読んでいただき、ありがとうございました！

# 参考にした記事
[RubyのProcとは？ブロック・Proc・lamdaの違いをマスター](https://style.potepan.com/articles/29663.html)
[【Ruby】Procオブジェクトについて整理する](https://losenotime.jp/ruby-proc/)
[rubyのProcって何をしてるの](https://tenrakatsuno.com/programing-note/ruby-proc/)
[【Ruby】「オブジェクトへの参照」と「変数の中身」](https://qiita.com/yusuke_blog1026/items/7261ccaf23214dacb37b)