前田です。

matz / netlab.co.jp (Yukihiro Matsumoto) wrote:

>あ,ゴマカすって,インスタンス変数には使えない名前があるって
>ことだったんですねえ.やっぱり見落としでした.これだったらイ
>ンスタンス変数名は
>
>  @__#{id}__
>
>にしたらどうでしょう.展開すると @__5879__ みたいな感じにな
>ります.

なるほど。

>|そういえば、このonceは各インスタンス毎に一回ずつ評価される
>|のですが、Eiffelの場合全部のインスタンスを通じて一回しか
>|評価されなかったような気がします。
>|どちらの動作が望ましいのでしょう。
>
>きっと,Eiffel流でしょうね.その場合はどうやって実装するのが
>望ましいかな.インスタンス変数ではなく,定数でテーブルを用意
>するのかな.

そうですね。
メソッド本体と同様にHashで管理するようにしてみました。

--
前田 修吾
----------
# once-method.rb

module OnceMethod
  BODIES = {}
  VALUES = {}
end

class Object
private
  def def_once_method(name, &body)
    if name.kind_of? Integer
      name = name.id2name
    end
    OnceMethod::BODIES[body.id] = body
    def_once_method_i(name, body.id)
  end
    
  def def_once_method_i(name, id)
    eval %{
      def #{name}
        eval 'def #{name}; OnceMethod::VALUES[#{id}]; end', TOPLEVEL_BINDING
        OnceMethod::VALUES[#{id}] = OnceMethod::BODIES[#{id}].call
        OnceMethod::BODIES.delete(#{id})
        OnceMethod::VALUES[#{id}]
      end
    }, TOPLEVEL_BINDING
  end
end

class Module
private
  def def_once_method_i(name, id)
    module_eval %{
      def #{name}
        #{self}.module_eval 'def #{name}; OnceMethod::VALUES[#{id}]; end'
        OnceMethod::VALUES[#{id}] = instance_eval(&OnceMethod::BODIES[#{id}])
        OnceMethod::BODIES.delete(#{id})
        OnceMethod::VALUES[#{id}]
      end
    }
  end
end