メモ化を知った

この投稿はRuby Advent Calendar 2012の16日目の記事としてQiitaに投稿したものです。

はじめに

あんまりRuby関係ないんですが、一応、The RSpec Bookを読んでいて知ったのがきっかけなので…。

あらすじ

The RSpec Book のletの解説には以下のように記述されています。

   describe "テストコードはてきとう" do
     let(:test) { "TEST" }
     it "test let" do
       test.should == "TEST"
     end
   end

メソッドが最初に呼び出されたときに戻り値がキャッシュされ、それ以降、同じスコープ内でメソッドが呼び出されるたびにキャッシュされた値が返されることを意味します。

これをメモ化というそうです。で、メモ化って?

一番簡単なメモ化

調べてみると、だいたいフィボナッチ数を求める例が多い。なのでn番目のフィボナッチ数を求めるメソッドを書いてみる。

普通に書いた場合

def f(n)
  if n <= 1 then
    n
  else
    f(n-1) + f(n-2)
  end
end

実行していくと、己のPCスペックではf(30) = 832040 くらいからマシンがもたついてきた。

これを メモ化 してみる。

メモ化した場合

# こっちは変わらず
def f(n)
  if n <= 1 then
    n
  else
    f(n-1) + f(n-2)
  end
end
def f_memo(n)
  @cache ||= []
  @cache[n] ||= f(n)
end

実行すると、最初の一発目は同じくらい遅いんだけど、一回実行すればキャッシュとして @cache に格納されるので二回目以降はバク速で求められる。

||= とは

上の例の @cache[n] ||= f(n)@cache[n] = @cache[n] || f(n) と同義。@cache[n]が真ならば@cache[n]を返す。偽ならnのフィボナッチ数を求める。 @cache[n]が真ならば、f(n)は評価しない、実行しない 。だから早い。

||=の例。

irb(main):001:0> a          # 未定義の時はエラー
NameError: undefined local variable or method `a' for main:Object
from (irb):1
from C:/rubies/Ruby-193-p0/bin/irb:12:in `<main>'
irb(main):002:0> a ||= 500  # a || 500 a は偽なので500
=> 500
irb(main):003:0> a
=> 500
irb(main):004:0> b = 0
=> 0
irb(main):005:0> b
=> 0
irb(main):006:0> b ||= 500  # b || 500 b は真なので0のまま
=> 0
irb(main):007:0> b
=> 0

…って所まで調べて、初めてのRubyを読み返してみたらバッチリ書いてあるし!!

初期化イディオム として紹介されていました。。。

関連記事(この記事の初版より古い記事はリンクがグレーで表示されます)

  1. 2012/11/12 [Ruby] [Jekyll] はてなダイアリーのエントリをJekyllへ移行する
  2. 2012/10/10 [Jekyll] [Liquid] [Ruby] Jekyll(Liquid)で記事の目次を出力するプラグインを作ってみた
  3. 2012/10/01 [Ruby] [Bundle] [Windows] bundle execを省略したいのでバッチを作った(Windows版)
  4. 2012/09/21 [Python] [Ruby] [Jekyll] Pygmentsを使ってJekyll内記事のコードハイライトを実現する
  5. 2012/09/20 [Jekyll] [StartUp] [Ruby] JekyllをGitHub Pagesに上げるための準備
  6. 2012/09/14 [Ruby] [StartUp] [Jekyll] JekyllとJekyll Bootstrapでかんたん静的サイト生成…するための準備
  7. 2012/09/13 [Ruby] [Fluentd] Fluentdの自作プラグインがロードできないのでソースの中身を追ってみる…
  8. 2012/08/17 [Fluentd] [Ruby] Fluentdのプラグインを作成してみる(練習用)
  9. 2012/07/17 [Ruby] [Windows] [Redmine] Windows版Redmineをサービスに登録してブート時に起動させる(宿題あり)
  10. 2012/07/12 [Fluentd] [Ruby] Fluentdというログ収集ツールを使ってApacheのログを取得するまで
  11. 2012/06/03 [TDD] [RSpec] [Ruby] [イベント] TDD Boot Camp 大阪 1.0( #tddbc ) に参加しました
  12. 2012/05/28 [Ruby] [イベント] [Jenkins] [Redmine] Jenkins,Redmine使いこなし勉強会に参加しました と、ちょっとプラグイン作ってみた #jen_red
  13. 2012/05/21 [SlideShare] [Heroku] [Ruby] [API] SlideShareのAPIを叩いてスライドをDLするRubyスクリプトをHerokuにデプロイした
  14. 2012/04/20 [Ruby] [Windows] ZenTestで実行したRSpecの結果をGrowlで通知してくれるようにした
  15. 2012/03/27 [Windows] [Jenkins] [Ruby] simplecovとsimplecov-rcovを使ってJenkinsでカバレッジを確認