LazyArray作ってみた

ヽ( ・∀・)ノくまくまー(2008-12-02) 優しいMerbの育て方にて紹介されているLazyArrayクラス。
ソースを見たところ何故かArrayのサブクラスじゃないで、もしArrayのサブクラスとして実装してみたらどうなるかというのをやってみた。

  1. まず初期値はブロックで渡すのは基本だよな。
  2. そのブロックを実行して初期化するメソッドを作って……
  3. Arrayの全部のメソッドの前でその初期化メソッドを実行すればいいわけだ。
  4. でも毎回loadedフラグ見るのダサいよな……
  5. 初期化が終わったらremove_methodしてArrayのメソッドに戻せばいいんじゃね?
  6. そうすると上書きメソッドは全部シングルトンだよな。class << selfっと。
  7. いちいち全部定義するの面倒だからdefine_methodだよな。
  8. Arrayにのみ定義されてるインスタンスメソッドを取るには、Array.instance_methods(false)か。
  9. よし、動いた!
  10. EnumerableのメソッドはArray#eachが必ず呼ばれるから定義しなくて大丈夫だよな。
  11. それ以前にArray#each単体でセグった!
  12. と思ったらdefine_methodでblockを受け取るようにしてなかったのが問題だった。 <- imkk

と、舞波メソッドで現状を表現してみました。

環境

C:ruby>ruby -v
ruby 1.9.0 (2008-10-04 revision 19669) [i386-mswin32]

LazyArray class

class LazyArray < Array
  OVERWRITE_METHODS = Array.instance_methods(false)
  
  def initialize &block
    @block = block
    
    class << self
      OVERWRITE_METHODS.each do |method_name|
        define_method method_name do |*args, &block|
          lazy_load
          super *args, &block
        end
      end
      
      private
      def lazy_load
        class << self
          remove_method *OVERWRITE_METHODS
          remove_method :lazy_load
        end
        if @block.arity == 0
          replace @block.call
        else
          @block.call self
        end
      end
    end
  end
end

補足

単純に技術的興味でやったので使えるかどうかは正直良くわからない。
define_methodとremove_methodのコストとEager loadingのコストを比較してないのでパフォーマンスも疑問。
remove_methodの使いどころがわかったような気がするのが収穫か。