あなたと深く発展したいだけです..


まず古いネタをください.
参照
深発展銀行が「あなたと深く発展したい」という知的な広告を出してから、銀行業界関係者はさらに知的な姉妹編を自作した.「光大はだめだ」
本題:rubyにおける深いコピー(deep clone)の問題を検討する.
1.=
rubyでは、オブジェクトをコピーする最も簡単な方法です.次に、効果を見てみましょう.

irb(main):002:0> a = "Hooopo"
=> "Hooopo"
irb(main):003:0> b = a
=> "Hooopo"
irb(main):004:0> b.object_id
=> 23424840
irb(main):005:0> a.object_id
=> 23424840
irb(main):006:0> b.gsub!("o","-")
=> "H---p-"
irb(main):007:0> p a
"H---p-"
=> nil
irb(main):008:0> p b
"H---p-"
=> nil

b修正後、aも修正され、aとbのobject_idと同じように、同じオブジェクトですか~
2.dupとclone(dupとcloneの差は多くないが、いくつかの違いがある)

irb(main):001:0> a = "Hooopo"
=> "Hooopo"
irb(main):002:0> b = a.dup
=> "Hooopo"
irb(main):003:0> a.object_id
=> 23428740
irb(main):004:0> b.object_id
=> 23425010
irb(main):005:0> b.gsub!("o","-")
=> "H---p-"
irb(main):006:0> p b
"H---p-"
=> nil
irb(main):007:0> p a
"Hooopo"
=> nil
irb(main):008:0>

bとaは異なるオブジェクトであり、bを変更してもaは変更されない.これは私たちが望んでいるものです.しかし、あまり早く喜ばないでください.次の例を見てください.

class Klass
    attr_accessor :str
 end
 s1 = Klass.new     
 s1.str = "Hooopo"   
 p s1
 s2 = s1.dup       
 p s2
 s2.str.gsub!("o","-")   
 p s1         
 p s2  
#results
#<Klass:0x2d2cd30 @str="Hooopo">
#<Klass:0x2d2cc7c @str="Hooopo">
#<Klass:0x2d2cd30 @str="H---p-">
#<Klass:0x2d2cc7c @str="H---p-">  

明らかに、問題はまたあり、s 2を変えた後、s 1も変わった.同じ罠もArrayとHashで発生し、次のコードを見てください.

irb(main):008:0> arr = [1,[1,1]]
=> [1, [1, 1]]
irb(main):009:0> arr_dup = arr.dup
=> [1, [1, 1]]
irb(main):010:0> arr_dup[0] = 2
=> 2
irb(main):011:0> arr
=> [1, [1, 1]]
irb(main):012:0> arr_dup[1][0] = 2
=> 2
irb(main):013:0> arr
=> [1, [2, 1]]

最初にarr_にdup[0]複素値の場合arrは変化せず、2回目のarr_dup[1][0]のときarrもそれに伴って変化した.つまりArray#dupは1階だけコピーして、まだ深くありません..同様にHashも、次のコードを見てください.

irb(main):001:0> hash = {:key => {:key => "value"}}
=> {:key=>{:key=>"value"}}
irb(main):002:0> hash_dup = hash.dup
=> {:key=>{:key=>"value"}}
irb(main):003:0> hash_dup[:key][:key] = "value_changed"
=> "value_changed"
irb(main):004:0> hash_dup
=> {:key=>{:key=>"value_changed"}}
irb(main):005:0> hash
=> {:key=>{:key=>"value_changed"}}

3.シーケンス化による深いコピー

irb(main):014:0> arr = [1,[1,1]]
=> [1, [1, 1]]
irb(main):015:0> arr_dump = Marshal.load(Marshal.dump(arr))
=> [1, [1, 1]]
irb(main):016:0> arr_dump.object_id
=> 22807940
irb(main):017:0> arr.object_id
=> 22850200
irb(main):018:0> arr_dump[1][1] = 2
=> 2
irb(main):019:0> arr_dump
=> [1, [1, 2]]
irb(main):020:0> arr
=> [1, [1, 1]]

irb(main):012:0> hash = {:key => {:key => "value"}}
=> {:key=>{:key=>"value"}}
irb(main):013:0> hash_dump = Marshal.load(Marshal.dump(hash))
=> {:key=>{:key=>"value"}}
irb(main):014:0> hash.object_id
=> 22790990
irb(main):015:0> hash_dump.object_id
=> 22755440
irb(main):016:0> hash_dump[:key][:key] = "value not changed"
=> "value not changed"
irb(main):017:0> hash
=> {:key=>{:key=>"value"}}
irb(main):018:0> hash_dump
=> {:key=>{:key=>"value not changed"}}

状況はだいぶよくなったようだ.しかし、もう一つの問題は、Marshalが一般オブジェクト、配列ハッシュ、高度なオブジェクトのみをシーケンス化できることです(IO、Proc、singletonなど)
次は2つのdeep cloneの実装(via:http://www.artima.com/forums/flat.jsp?forum=123&thread=40913)

class Object
      def deep_clone
        Marshal::load(Marshal.dump(self))
      end
end

   class Object
      def dclone
        case self
          when Fixnum,Bignum,Float,NilClass,FalseClass,
               TrueClass,Continuation
            klone = self
          when Hash
            klone = self.clone
            self.each{|k,v| klone[k] = v.dclone}
          when Array
            klone = self.clone
            klone.clear
            self.each{|v| klone << v.dclone}
          else
            klone = self.clone
        end
        klone.instance_variables.each {|v|
          klone.instance_variable_set(v,
            klone.instance_variable_get(v).dclone)
        }
        klone
      end
    end

もっと複雑(via:http://d.hatena.ne.jp/pegacorn/20070417/1176817721)...

class Object
  def deep_clone
    _deep_clone({})
  end

  protected
  def _deep_clone(cloning_map)
    return cloning_map[self] if cloning_map.key? self
    cloning_obj = clone
    cloning_map[self] = cloning_obj
    cloning_obj.instance_variables.each do |var|
      val = cloning_obj.instance_variable_get(var)
      begin
        val = val._deep_clone(cloning_map)
      rescue TypeError
        next
      end
      cloning_obj.instance_variable_set(var, val)
    end
    cloning_map.delete(self)
  end
end

class Array
  protected
  def _deep_clone(cloning_map)
    return cloning_map[self] if cloning_map.key? self
    cloning_obj = super
    cloning_map[self] = cloning_obj
    cloning_obj.map! do |val|
      begin
        val = val._deep_clone(cloning_map)
      rescue TypeError
        #
      end
      val
    end
    cloning_map.delete(self)
  end
end

class Hash
  protected
  def _deep_clone(cloning_map)
    return cloning_map[self] if cloning_map.key? self
    cloning_obj = super
    cloning_map[self] = cloning_obj
    pairs = cloning_obj.to_a
    cloning_obj.clear
    pairs.each do |pair|
      pair.map! do |val|
        begin
          val = val._deep_clone(cloning_map)
        rescue TypeError
          #
        end
        val
      end
      cloning_obj[pair[0]] = pair[1]
    end
    cloning_map.delete(self)
  end
end

Something about dup and clone
1.ruby字面量(Fixnum,true,false,nil,Symbol)はdupメソッドとcloneメソッドを呼び出してTypeErrorに報告することはできません.
2.オブジェクトのstate(taint,frozen)の変更:dupはfrozenのオブジェクトをunfrozenし、cloneはしません.

irb(main):019:0> o = Object.new
=> #<Object:0x2b7855c>
irb(main):020:0> o.taint
=> #<Object:0x2b7855c>
irb(main):021:0> o.freeze
=> #<Object:0x2b7855c>
irb(main):024:0> [o.frozen?,o.tainted?]
=> [true, true]
irb(main):025:0> o_clone = o.clone
=> #<Object:0x2b44c0c>
irb(main):026:0> [o_clone.frozen?,o_clone.tainted?]
=> [true, true]
irb(main):027:0> o_dup = o.dup
=> #<Object:0x2b32e6c>
irb(main):028:0> [o_dup.frozen?,o_dup.tainted?]
=> [false, true]

3.単体メソッドのコピー:cloneは単体メソッドとともにコピーされ、dupはコピーされません.

irb(main):029:0> o = Object.new
=> #<Object:0x2b256a4>
irb(main):030:0> def o.say
irb(main):031:1>  puts "Hello,Hooopo"
irb(main):032:1> end
=> nil
irb(main):033:0> o_dup = o.dup
=> #<Object:0x296acec>
irb(main):035:0> o_clone = o.clone
=> #<Object:0x2dba194>
irb(main):037:0> o_dup.say
NoMethodError: undefined method `say' for #<Object:0x296acec>
        from (irb):37
        from :0
irb(main):038:0> o_clone.say
Hello,Hooopo