引き続きブロックについての基礎知識をまとめていきたいと思います。
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回に分けてブロックについて書いてきました。ブロックを使いこなせるともっとプログラミングの幅が広がると思うので、引き続き勉強していきたいと思います。