Ruby

【メタプログラミング】ブロックについてのあれこれ 2

引き続きブロックについての基礎知識をまとめていきたいと思います。

Procオブジェクト

ブロックを保管しておいて後で実行するためにオブジェクトが必要

inc = Proc.new { |x| x + 1 }
inc.call(3) #=> 4

dec = lambda { |x| x -1 }
dec.call(4) #=> 3

def my_method(greeting)
  "#{greeting}, #{yield}!"
end

my_proc = proc { 'Bill' }
my_method("Hello", &my_proc) #=> "Hello, Bill!"

ブロックはメソッドに渡す無名引数のようなものなので、Procとして受け取りたい場合や他のメソッドにブロックを渡したいときにはそれを指し示す名前が必要で、それには&を使う。

def math(a, b)
  yield(a, b)
end

def do_math(a, b, &operation)
  operation.class #=> Proc
  math(a, b, &operation)
end

do_math(2, 3) { |x, y| x * y } #=> 6

メソッド定義の&operationの&をつけると、メソッドに渡されたブロックを受け取って、それをProcオブジェクトに変換することになる。

ブロックが送られない場合はエラーにならずnilになる。

def do_math(a, b, &operation)
  p operation #=> nil
end

p do_math(2, 3)

lamdbaとProcのちがい

1. returnキーワードの意味のちがい

lambdaの場合は単にlambdaの中から戻るだけである。

def double(callable_object)
  callable_object.call * 2
end

l = lambda { return 10; 20 }
double(l) #=> 20

Procの場合は、Procが定義されたスコープから戻ってしまう。

def another_double
  p = Proc.new { return 10 }
  result = p.call
  return result * 2
end

another_double #=> 10

2. lamdbaのほうが引数の数に厳密

lambdaのほうが引数の数に厳密で、まちがった項数で呼び出すとArgumentErrorになる。

p = proc { |a, b| [a, b] }
l = lambda { |a, b| [a, b] }

p p.call(2) #=> [2, nil]
p p.call(1, 2, 3) #=> [1, 2]
p l.call(2)
`block in <main>': wrong number of arguments (given 1, expected 2) (ArgumentError)
p l.call(1, 2, 3)
`block in <main>': wrong number of arguments (given 3, expected 2) (ArgumentError)

lambdaのほうがメソッドに似ていて直感的なので、lambdaを使うべき。

さいごに

2回に分けてブロックについて書いてきました。ブロックを使いこなせるともっとプログラミングの幅が広がると思うので、引き続き勉強していきたいと思います。

ABOUT ME
sakai
東京在住の30歳。元々は車部品メーカーで働いていてましたが、プログラミングに興味を持ちスクールに通ってエンジニアになりました。 そこからベンチャー → メガベンチャー → 個人事業主になりました。 最近は生成 AI 関連の業務を中心にやっています。 ヒカルチャンネル(Youtube)とワンピースが大好きです!