Ruby中如何複製對象 (deep clone)

用Ruby複製一個對象(object)也許沒有你想像的那麼容易. 今天我google了半天, 做個總結吧.
先從最簡單的開始, b = a 是複製嗎? 看代碼說話:
>> a= [0,[1,2]]
>> b=a
>> b[0]=88
>> b[1][0]=99
>> b 
=> [88, [99, 2]]
>> a 
=> [88, [99, 2]]
從上面代碼發現, 一但修改b, 原來的a也同時被改變了. 甚至:

>> b.equal?(a)
=> true
原來b跟a根本就是同一個object, 只是馬甲不一樣罷了. 所以b = a不是複製.
那 b = a.dup呢?? 還是看代碼:
>> a= [0,[1,2]]
>> b=a.dup
>> b[0]=88
>> b[1][0]=99
>> b
=> [88, [99, 2]]
>> a
=> [0, [99, 2]]
情況似乎有所好轉, 在修改b後, a還是有一部分被修改了.(0沒有變,但原來的1變成了99).
所以dup有時候是複製(如在Array只有一級時), 但有時不是複製哦.
再來一個, b = a.clone呢? 上代碼:
>> a= [0,[1,2]]
>> b=a.clone
>> b[0]=88
>> b[1][0]=99
>> b
=> [88, [99, 2]]
>> a
=> [0, [99, 2]]
情況幾乎跟dup一模一樣. 所以clone也不一定可以相信哦!
原來ruby中的dup和clone都是shallow複製, 只針對object的第一級屬性.
汗, 難道在Ruby中沒有辦法複製對像嗎? 也不完全是, 看這個:
>> a= [0,[1,2]]
>> b=Marshal.load(Marshal.dump(a))
>> b[0]=88
>> b[1][0]=99
>> b
=> [88, [99, 2]]
>> a= [0,[1,2]]
=> [0, [1, 2]]
修改b後a沒有被改變!!! 似乎終於成功找到複製的辦法了!!!
為什麼要加"似乎"呢? 因為有些object是不能被Marshal.dump的.如:
>> t=Object.new
>> def t.test; puts ‘test’ end
>> Marshal.dump(t)
TypeError: singleton can’t be dumped
    from (irb):59:in `dump’
    from (irb):59
更完善的複製方案可以考慮給ruby增加一個deep clone功能, 可以參考以下鏈接:
http://d.hatena.ne.jp/pegacorn/20070417/1176817721
http://www.artima.com/forums/flat.jsp?forum=123&thread=40913
好了, 複製這個小問題, 沒想到也能引出這麼長的文章來, 沒想到吧?

轉載請註明: 轉自船長日誌, 本文鏈接地址: http://www.cslog.cn/Content/ruby-deep-clone/zh-hant/

此條目發表在 Ruby on Rails 分類目錄。將固定鏈接加入收藏夾。

發表評論