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
三重出身の28歳。前職はメーカーで働いていて、プログラミングスクールに通って未経験からWeb業界に転職しました。Railsをメインで使っていて、AWSも少しできます。音楽を聞くこととYoutubeを見るのが好きです。最近はへきトラ劇場にハマってます