Procについて

procとは

ブロックをオブジェクト化するためのクラス。

  • プロックについて ブロックは下記の形で書かれたものです。
    ブロックはオブジェクトではない!
# do ~ end の形
10.times do |f| puts f end

# { ~ }の形
10.times { |f| puts f }

procオブジェクトの基本構文

# Proc.newにブロックを渡す。
hello_proc = Proc.new { 'Hello!' }

# Kernel#.proc も同じ挙動になる
hello_proc = proc { 'Hello!' }

# 実行する時はcallメソッドを使用する。
hello_proc.call # => Hello!
  • 引数を利用する
add_proc = proc { |a, b| a + b }

add_proc.call(10, 20) # => 30

デフォルト値、可変長引数、キーワード引数も使用できる!

add_proc = proc { |a = 0, b = 0| a + b }

add_proc.call  # => 0
add_proc.call(10) # => 10
add_proc.call(10, 20) # => 30

Kernel.#lambda と Proc.new, Kernel.#procの違い

lambdaを使用して生成する方がよりメソッドに近い働きをします。
lambdaは引数の数が違っているとエラーになります。

# Proc.newは多重代入のようなふるまいをする。
x = Proc.new { |a, b, c| p a,b,c }
# エラーにはならない
x.call(10, 20) 
# => 10 20 nil

x = lambda { |a, b, c| p a,b,c }
# エラーになる
x.call(10, 20) # => wrong number of arguments

&とto_procメソッドについて

Procのオブジェクトをブロックとして渡したい場合は、引数の前に&を付ける。

reverse_proc = Proc.new { |s| puts s.reverse }

# 引数にProcオブジェクトを渡す場合は&が必要。
%w[Ruby Java Perl].map(&reverse_proc)

&は右辺のオブジェクトに対してto_procメソッドを呼び出す。
但し、レシーバーが元からPrcoオブジェクトの場合は自分自身が返ってくるだけ。

シンボルとto_procメソッド

シンボルに対してto_procを呼び出した場合、実行時の引数の数によって実行される処理の内容が微妙に変わる。

split_proc = :split.to_proc
split_proc  # => #<Proc:0x00007f9669940aa0(&:split)>

引数が1つの場合

一つ目の引数がレシーバーになり、そのレシーバーに対して元のシンボルと同じ名前のメソッドを呼び出す。

# 'a-b-c-d e'.split と等価
split_proc.call('a-b-c-d e') # => ["a-b-c-d", "e"]

引数が2つ以上の場合

2つ目以降はメソッドの引数となる。

#  'a-b-c-d e'.split('-')と等価
split_proc.call('a-b-c-d e', '-') # => ["a", "b", "c", "d e"]

参考

class Proc (Ruby 3.0.0 リファレンスマニュアル)