March 02, 2006

Rubyでディープコピー

Rubyでディープコピーをするのに目から鱗な技があります。

deep_copied = Marshal::load(Marshal::dump(target_object))
こんなことができるのもRubyのシリアライザMarshalが非常に優れていて、ほぼ全てのオブジェクトに対応しているからですが、対応し過ぎているために時に思った動作をしないことがあります。例えばディープコピー対象のオブジェクトにシングルトンオブジェクトが含まれていたらどうなると思いますか?当然といえば当然ですが、これらについてもディープコピーが取られてしまい、シングルトン性が破壊されます。

そういうときはMarshalの内部的な動作を規定するためのメソッド、_dumpと_loadをオーバーライドすればよいみたいです(Ver 1.8ではmarshal_dumpとmarshal_load)。コードの例は続きをどうぞ。

class Singleton
    def initialize
        @a = 1
        @b = 2
    end
    @@obj = Singleton::new
    def Singleton::getInstance
        return @@obj
    end
    def _dump(limit)
        Marshal::dump(self.instance_variables.collect{|var| instance_variable_get(var)}, limit)
    end
    def Singleton::_load(obj)
        vars = @@obj.instance_variables
        Marshal::load(obj).each_with_index{|value, i|
            @@obj.instance_variable_set(vars[i], value)
        }
        return @@obj
    end
    private :initialize
end

class Container
    def initialize
        @singleton_obj = Singleton::getInstance
    end
end

c = Container::new
p c
#=> #<Container:0x10196bd0 @singleton_obj=#<Singleton:0x101aca40 @b=2, @a=1>>
p Marshal::load(Marshal::dump(Container::new))
#=> #<Container:0x1018c718 @singleton_obj=#<Singleton:0x101aca40 @b=2, @a=1>>


Singletonのアロケートが変わっていないことに注目してください。

06:28 fenrir が投稿 : 固定リンク | | このエントリーを含むはてなブックマーク | この記事をdel.icio.usでブックマーク | トラックバック
このエントリーのトラックバックURL: http://fenrir.naruoka.org/mt/mt-tb.cgi/467
コメント
コメントする









名前、アドレスを登録しますか?
(次回以降コメント入力が楽になります)
  • 匿名でのコメントは受け付けておりません。
  • 名前(ハンドル名可)とメールアドレスは必ず入力してください。
  • メールアドレスを表示されたくないときはURLも必ず記入してください。
  • コメント欄でHTMLタグは使用できません。
  • コメント本文に日本語(全角文字)がある程度多く含まれている必要があります。
  • コメント欄内のURLと思われる文字列は自動的にリンクに変換されます。