周りにRubyでのメモ化方法を知らない方がおられたので、改めて自分の理解を整理するためにもRubyでのメモ化についてまとめておきます。
対象読者
・メモ化が何かわからない人
・Rubyでのメモ化方法がわからない人
・いろんな方法があるけど、どの手法を用いたら良いかわからない人
メモ化とは?
実行結果を保持し、その結果を再利用する手法を「メモ化」と呼びます。
Rubyでよく見るメモ化
Rubyでのメモ化については、下3つの書き方を実際のプロダクトコードでよく見かけます。
(他にもメモ化の方法はありますが、代表的で現場で利用できるものだけ説明します。)
# 自己代入
def hoge
@hoge ||= hoge_func
end
# instance_variable_defined?
def foo
return @foo if instance_variable_defined?(:@foo)
@foo = foo_func
end
# defined?
def bar
return @bar if defined?(:@bar)
@bar = bar_func
end
Rubyでのメモ化について解説
自己代入 ||= を利用する
Rubyではnilとfalse以外は全て真として扱われます。
メモ化する式が真を返す場合には自己代入 ||= が使えます。
インスタンス変数の @hoge は未定義の時にnilを返すので、1回目の呼び出しでは hoge_funcの戻り値が@hogeに代入され@hogeが返却されます。
2回目以降は、@hogeは真であるため@hoge自信を返却します。
自己代入が分かりづらい方は以下のコードと捉えると理解しやすいと思います。
@hoge ||= 1
# 以下と同じ↓
@hoge || (@hoge = 1)
ただし、falsyな値(false, nil)が返却される場合は、自己代入だと毎回評価してしまい意味がありません。
自己代入は、式が真の時だけ有効な手段ということを覚えておいてください。
式が偽を返す場合は、次に解説する instance_variable_defined? または defined? を利用しましょう。
instance_variable_defined? もしくは defined? を利用する
自己代入 ||= は単なる論理和で、式が1回評価されたかは判断できません。
instance_variable_defined?、もしくはdefined?を使い、明示的にインスタンス変数の存在チェックをします。
これでnilやfalseを返す式もメモ化できます。
# instance_variable_defined?
def foo
return @foo if instance_variable_defined?(:@foo)
@foo = foo_func
end
# defined?
def bar
return @bar if defined?(:@bar)
@bar = bar_func
end
instance_variable_defined?、defined? どちらを利用するかは好みに分かれます。
細かい話だと、defined?の方が最適化されているため処理速度が早いようですが、そこまで大きな差はないので、個人的には、意味が分かりやすい instance_variable_defined? を利用するのがオススメ。
まとめ
メモ化はパフォーマンス改善をする上で非常に便利な手法です。
何回も実行するけど、実行結果が変わらない処理は、積極的にメモ化を利用しましょう。
また、Rubyでのメモ化の際には、falsy値を考慮するため、instance_variable_defined? または defined? を利用が良さそうです。
是非、使い方をマスターして実際の開発に活かしてみてはいかがでしょうか。