LoginSignup
17
10

More than 5 years have passed since last update.

Ruby で Proc を即時実行する代わりに begin 式で代入する

Last updated at Posted at 2016-12-26

以前は、例えば、下のようなコードを書くことが度々ありました。

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

参考

17
10
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
10