Rubyのブロック・Proc・Lambda:違いと使い分けを完全マスター

Rubyのブロック・Proc・Lambda:違いと使い分けを完全マスター

Rubyのプログラムで頻繁に利用されるブロック、Proc、Lambdaは、それぞれ異なる特徴を持つコードの再利用手段です。本記事では、それぞれの違いや用途、使い分けのポイントについて徹底的に掘り下げます。

ブロックとは何か

ブロックはメソッドに渡せる一時的なコードの塊で、do…endまたは{}を使って記述します。

def greeting
  yield if block_given?
end

greeting { puts "Hello, World!" }

Procとは何か

Procはブロックをオブジェクトとして扱えるようにしたものです。Proc.newまたはKernel#procを使って作成します。

my_proc = Proc.new { puts "This is a Proc" }
my_proc.call

Lambdaとは何か

LambdaはProcの一種で、より厳格な引数チェックを持ちます。Kernel#lambdaまたは->構文で作成します。

my_lambda = ->(name) { puts "Hello, #{name}" }
my_lambda.call("Alice")

ブロックとProcの違い

ブロックは一時的なもので、複数の引数や変数を保持できません。Procはオブジェクトとして変数に代入可能です。

def test_block
  yield
end

test_block { puts "This is a block" }

my_proc = Proc.new { puts "This is a Proc" }
my_proc.call

ProcとLambdaの違い

Lambdaは引数チェックが厳格で、returnは呼び出し元には影響しません。Procは引数チェックが緩く、returnは呼び出し元に影響を与えます。

# 引数チェックの違い
lambda_func = ->(a, b) { puts a + b }
proc_func = Proc.new { |a, b| puts a + b }

lambda_func.call(1, 2) # OK
proc_func.call(1)      # OK, bはnil

# returnの違い
def lambda_test
  lambda_func = -> { return "Lambda return" }
  lambda_func.call
  "Method return"
end

def proc_test
  proc_func = Proc.new { return "Proc return" }
  proc_func.call
  "Method return"
end

puts lambda_test # => "Method return"
puts proc_test   # => "Proc return"

ブロックをメソッドに渡す方法

ブロックはメソッドに渡す際、yieldで実行するか、&を使って明示的にProcに変換します。

def execute_block(&block)
  block.call
end

execute_block { puts "Block executed" }

Procを使った柔軟な処理

Procは引数に渡したり、複数のメソッドで共有したりするのに便利です。

def repeat(proc)
  3.times { proc.call }
end

my_proc = Proc.new { puts "Repeated" }
repeat(my_proc)

Lambdaを使った関数型プログラミング

Lambdaは特に関数型プログラミングスタイルで有用です。厳密な引数チェックが期待される場面で使います。

add = ->(a, b) { a + b }
puts add.call(3, 5) # => 8

クロージャとしてのProcとLambda

ProcとLambdaはクロージャとして振る舞い、定義されたスコープの変数を保持します。

def create_closure
  counter = 0
  Proc.new { counter += 1 }
end

closure = create_closure
puts closure.call # => 1
puts closure.call # => 2

ブロックを使用するデザインパターン

ブロックはテンプレートメソッドパターンやリソース管理パターンでよく使われます。

def with_file(filename)
  file = File.open(filename, "w")
  yield(file)
ensure
  file.close
end

with_file("test.txt") { |file| file.puts("Hello, File!") }

Procを活用したコールバック処理

Procはイベント駆動型プログラミングやコールバック処理に便利です。

callbacks = []
callbacks << Proc.new { puts "Callback 1" }
callbacks << Proc.new { puts "Callback 2" }

callbacks.each(&:call)

Lambdaの適用例:簡潔な関数の定義

Lambdaは短い匿名関数の定義に最適です。Enumerableモジュールとの組み合わせで効果を発揮します。

numbers = [1, 2, 3, 4, 5]
squares = numbers.map(&->(n) { n * n })
puts squares # => [1, 4, 9, 16, 25]

まとめ

Rubyのブロック、Proc、Lambdaは、それぞれ異なる用途と特性を持つコードの再利用手段です。特徴を理解し、適切な場面で使い分けることで、効率的で可読性の高いコードを書くことができます。