Ruby東海32回に参加した

Ruby東海 第32回 勉強会 - Ruby東海 | Doorkeeperに参加した。

@antimon2さんからメモ化の発表があって、ひとりで「わからん」って騒いでたのが私です。

コードを超絶簡略化すると

class MemoSample
  def hello(name)
    @greet ||= Hash.new { |hash, name| hash[name] = "hello #{name}" }
    @greet[name]
  end
end

memo = MemoSample.new
puts memo.hello("Tom")
puts memo.hello("Ken")
puts memo.hello("Tom")

ですが、ハッシュにメモを記録する所が全然わからんかった。Ruby力なさすぎて辛い。

つまった所1

memo.hello("Tom") で1回だけ @greet が作成されるので、memo.hello("Ken") では @greet["Ken"] で nil が返ってくるのでは?

こたえ1

Hash.new に渡したブロックが遅延評価されるので、"Ken" に応答できる。
イメージとしては

@greet = { "Tom" => "hello Tom", "Ken" => {後で計算する}, "Marry" => {後で計算する} }

というハッシュが最初に作られて、後で計算する所は @greet["Ken"] が呼ばれてからブロックを実行する。
いったん計算すれば、

@greet = { "Tom" => "hello Tom", "Ken" => "hello Ken", "Marry" => {後で計算する} }

になるから、メモ化 ( ゚Д゚)ウマー てこと。

つまった所2

後で計算するとして、ブロック { |hash, name| hash[name] = "hello #{name}" } は memo.hello("Tom") の呼び出し時に定義している。
結局 @greet[key] にはいつも "hello Tom" が格納されてしまうのでは?

こたえ2

ブロック引数に渡した name と hello の引数は別物 { |hash, key| hash[key] = "hello #{key}" } と同じ。
{後で計算する}箇所は指定されたキー "Ken" によって計算されるだけ。