Ruby

そのRubyプログラムをきっと速くできない小ネタ

2017年9月なので少々前の話になるのですが、Rails のコミットログを見ていたらこんな変更を見つけました。

https://github.com/rails/rails/commit/32e1e70995a0c3f985dd2b5e9ce2295bc7b906ef#diff-83ce56e0f856f2c8d7ceb7fab4eb4c2bR74

難しいことではなく、単に ||= を使っていたところを defined? でチェックし代入するように変更しています。

何が違うか

値が nil だった場合、変更前の書き方の場合は、「偽かチェックして、右辺を改めて算出する」のに対して、変更後はそれらが要りません。

処理 偽なら代入 defined? が偽なら代入
定義しているか ? チェックする
偽か チェックする -
代入する値 計算する -

しかもこれが、このメソッドを通るたびに発生します。

自分は、真偽チェックと defined? と (Ruby の言語レベルの話として) どの程度コストが違うのか、というところまでは理解してないのですが、少なくとも defined? だけにすればやることが減るのは確かだと思います。

どんなケースに応用できるか

フレームワークレベルでこれを直せばなるほど意味があるように思いますが、それ以外でもどこかに役立てたいものです。

同様の修正が有効なのは、(追記しました)

  • 右辺で複雑な計算をしている
  • 結果として nil もしくは false が返って来うる
  • 何度も実行されるが、右辺の返り値は変わらない

上記の場合でしょうか。

自分の書いているプログラムには思い当たらないです。残念。

追記: 特に早くしたい目的ではないのですが、代入するものの導出が複数行に渡り、 begin - end で書いているようなケースでは、見た目の複雑さが大きくは変わらないので積極的に使っていこうと思いました。

before
def client
  @client ||= begin
                # ... 何かすごい処理
                Client.new
              end
end
after
def client
  unless defined?(@client)
    # ... 何かすごい処理
    @client = Client.new
  end
  @client
end