あなたと深く発展したいだけです..
まず古いネタをください.
参照
深発展銀行が「あなたと深く発展したい」という知的な広告を出してから、銀行業界関係者はさらに知的な姉妹編を自作した.「光大はだめだ」
本題:rubyにおける深いコピー(deep clone)の問題を検討する.
1.=
rubyでは、オブジェクトをコピーする最も簡単な方法です.次に、効果を見てみましょう.
b修正後、aも修正され、aとbのobject_idと同じように、同じオブジェクトですか~
2.dupとclone(dupとcloneの差は多くないが、いくつかの違いがある)
bとaは異なるオブジェクトであり、bを変更してもaは変更されない.これは私たちが望んでいるものです.しかし、あまり早く喜ばないでください.次の例を見てください.
明らかに、問題はまたあり、s 2を変えた後、s 1も変わった.同じ罠もArrayとHashで発生し、次のコードを見てください.
最初にarr_にdup[0]複素値の場合arrは変化せず、2回目のarr_dup[1][0]のときarrもそれに伴って変化した.つまりArray#dupは1階だけコピーして、まだ深くありません..同様にHashも、次のコードを見てください.
3.シーケンス化による深いコピー
状況はだいぶよくなったようだ.しかし、もう一つの問題は、Marshalが一般オブジェクト、配列ハッシュ、高度なオブジェクトのみをシーケンス化できることです(IO、Proc、singletonなど)
次は2つのdeep cloneの実装(via:http://www.artima.com/forums/flat.jsp?forum=123&thread=40913)
もっと複雑(via:http://d.hatena.ne.jp/pegacorn/20070417/1176817721)...
Something about dup and clone
1.ruby字面量(Fixnum,true,false,nil,Symbol)はdupメソッドとcloneメソッドを呼び出してTypeErrorに報告することはできません.
2.オブジェクトのstate(taint,frozen)の変更:dupはfrozenのオブジェクトをunfrozenし、cloneはしません.
3.単体メソッドのコピー:cloneは単体メソッドとともにコピーされ、dupはコピーされません.
参照
深発展銀行が「あなたと深く発展したい」という知的な広告を出してから、銀行業界関係者はさらに知的な姉妹編を自作した.「光大はだめだ」
本題: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