RubyのObject#cloneがdeep copyしない理由は簡単。相互参照がある場合や、コピーできないものを内包していた場合の挙動をグローバルに(Object/Kernelで)定義するのが難しいから。

 もっと利用側寄りの理由としては、deep copyされたくないケースも多いから。特に、ArrayやHashのように何でも入れられるコンテナオブジェクトが、dup/cloneで各要素をdup/cloneして回るべきかと言われると躊躇する。コネクションオブジェクトやソケット、ファイルハンドルもdupする?

 一方、メンバの参照コピー(shallow copy)で足らない部分は、適宜自分のクラスのinitialize_copyメソッドで定義できる。たとえばメッセージダイジェストを計算するオブジェクトなら、内部ベクタなどについては”deep copy”する。でないと、クローンを操作したらコピー元の値や状態まで変わった、ということになってしまう。何をdeep copyすべきかというのは各オブジェクトが知っており、自分が固有に持つものについては自分が責任を持ってコピーする。

 Perlは文字列や配列がオブジェクトでないので、文字列の配列などは単なる代入によって実質deep copyになる。その辺が感覚の違いを生むのだろう。

 ちなみに、Rubyで手軽にdeep copyしたければMarshal.load(Marshal.dump(obj))が定石。これはPerlのStorable::dclone(obj)と等価。


Categories : Tech