以前は、例えば、下のようなコードを書くことが度々ありました。
lambdaパターン
@logger ||= -> {
logger = MyLogger.new
logger.level = Logger::DEBUG
logger
}.call
これは Proc オブジェクトを作って、その場で実行しています。
他の言語でいうところの「無名即時関数」のような使い方です。
が、引数を取らないなら、 begin .. end
でいいですよ、と教えてもらいました。
begin-endパターン
@logger ||= begin
logger = MyLogger.new
logger.level = Logger::DEBUG
logger
end
ベンチマーク
折角なので、ベンチマークを取ってみました。
bench-block-assignment.rb
#!/usr/bin/env ruby
require 'benchmark'
n = 1_000_000
Benchmark.bm(7) do |x|
tn = x.report('lambda:') do
n.times do
foo = -> {
:foo
}.call
end
end
tm = x.report('begin:') do
n.times do
foo = begin
:foo
end
end
end
end
実行結果は下の通り。
概ね10倍弱ほど実行効率が違います。
% ruby bench-block-assignment.rb
user system total real
lambda: 0.390000 0.000000 0.390000 ( 0.397704)
begin: 0.050000 0.000000 0.050000 ( 0.049975)
詳しくは追ってませんが、lambda を使うケースでは、Procを作るコストや関数呼び出しのオーバーヘッドが掛かるのかな、と想像しています。
(追記) 別解: tap を使う
今回のコード例だと、tap を使った方が好ましそう、とコメントで教えてもらいました。
tapパターン
@logger ||= MyLogger.new.tap do |logger|
logger.level = Logger::DEBUG
end