रूबी के डुबकी और क्लोन तरीकों में क्या अंतर है?


214

के लिए रूबी डॉक्सdup कहते हैं:

सामान्य तौर पर, cloneऔर dupविभिन्न वर्गों में अलग-अलग शब्दार्थ हो सकते हैं। जबकि cloneइसका आंतरिक राज्य सहित किसी ऑब्जेक्ट को डुप्लिकेट करने के लिए उपयोग किया जाता है, dupआम तौर पर नए उदाहरण को बनाने के लिए वंश ऑब्जेक्ट के वर्ग का उपयोग करता है।

लेकिन जब मैं कुछ परीक्षण करता हूं तो मैंने पाया कि वे वास्तव में समान हैं:

class Test
   attr_accessor :x
end

x = Test.new
x.x = 7
y = x.dup
z = x.clone
y.x => 7
z.x => 7

तो दो तरीकों के बीच अंतर क्या हैं?


29
काश मुझे पता होता कि मैं क्या dup और क्याclone करता है, में अंतर नहीं जानता , लेकिन आप एक के बजाय दूसरे का उपयोग क्यों करेंगे।
एंड्रयू ग्रिम

1
यहाँ एक अच्छा लिंक भी है - coderwall.com/p/1zflyg
अरूप रक्षित

जवाबों:


298

विभिन्न शब्दार्थ प्रदान करने के लिए उपवर्ग इन विधियों को ओवरराइड कर सकते हैं। अपने Objectआप में, दो प्रमुख अंतर हैं।

सबसे पहले, cloneएकल वर्ग को कॉपी dupकरता है , जबकि ऐसा नहीं करता है।

o = Object.new
def o.foo
  42
end

o.dup.foo   # raises NoMethodError
o.clone.foo # returns 42

दूसरा, cloneजमे हुए राज्य को संरक्षित dupकरता है , जबकि नहीं करता है।

class Foo
  attr_accessor :bar
end
o = Foo.new
o.freeze

o.dup.bar = 10   # succeeds
o.clone.bar = 10 # raises RuntimeError

इन तरीकों के लिए Rubinius कार्यान्वयन अक्सर इन सवालों के जवाब के लिए अपने स्रोत है, के बाद से यह काफी स्पष्ट है, और एक काफी आज्ञाकारी रूबी कार्यान्वयन है।


15
यदि कोई भी इसे फिर से बदलने की कोशिश करता है: "सिंगलटन क्लास", जो रूबी में एक अच्छी तरह से परिभाषित शब्द है, इसमें न केवल सिंगलटन तरीके शामिल हैं , बल्कि सिंगलटन क्लास पर परिभाषित कोई भी स्थिरांक भी शामिल है। पर विचार करें: o = Object.new; class << o; A=5; end; puts ( class << o.clone; A; end ); puts ( class << o.dup; A; end )
जेरेमी रोमन

3
महान जवाब, एक महान टिप्पणी के बाद, लेकिन यह मुझे उस वाक्यविन्यास को समझने के लिए एक जंगली हंस पीछा पर ले गया। इससे किसी और को मदद मिलेगी जो भ्रमित भी हो सकता है: devalot.com/articles/2008/09/ruby-singleton
davidpm4

1
मुझे लगता है कि यह उल्लेखनीय है कि "सिंगलटन क्लास" में कोई भी मॉड्यूल शामिल है extendजो मूल ऑब्जेक्ट पर एड किया गया है । इसलिए Object.new.extend(Enumerable).dup.is_a?(Enumerable)झूठा लौट आता है।
डैनियल

यद्यपि यह उत्तर प्रश्न का उत्तर देता है और मतभेद बताता है। यह भी ध्यान देने योग्य है कि दोनों विधियाँ अलग-अलग स्थितियों के लिए होती हैं जैसा कि ऑब्जेक्ट # डुबकी प्रलेखन द्वारा कहा गया है । के लिए उपयोग के मामले क्लोन , इरादा (जबकि एक अलग वस्तु आईडी वाले) है कि एक ही उदाहरण के रूप में उपयोग करने के लिए के साथ एक वस्तु क्लोनिंग, जबकि है dup एक नया उदाहरण के लिए आधार के रूप में एक वस्तु की नकल करने का इरादा है।
3limin4t0r

189

ActiveRecord से निपटने के दौरान एक महत्वपूर्ण अंतर भी है:

dup अपनी आईडी सेट किए बिना एक नई ऑब्जेक्ट बनाता है, जिससे आप डेटाबेस से एक नई ऑब्जेक्ट को हिट करके बचा सकते हैं .save

category2 = category.dup
#=> #<Category id: nil, name: "Favorites"> 

clone एक ही आईडी के साथ एक नया ऑब्जेक्ट बनाता है, इसलिए उस नए ऑब्जेक्ट में किए गए सभी परिवर्तन मूल रिकॉर्ड को ओवरराइट कर देंगे यदि हिट किया गया है .save

category2 = category.clone
#=> #<Category id: 1, name: "Favorites">

43
यह उत्तर वह है जिसमें IMO सबसे महत्वपूर्ण व्यावहारिक जानकारी है ... अन्य उत्तर esoterica पर रहते हैं, जबकि यह उत्तर एक महत्वपूर्ण व्यावहारिक अंतर को इंगित करता है।
jpw

37
हालांकि ActiveRecord के लिए ऊपर विशिष्ट है; मानक रूबी में भेद कहीं अधिक सूक्ष्म है।
एहमकलॉड

1
@Stefan और @jvalanen: जब मैं आवेदन कर रहा हूं dupऔर cloneमेरी ActiveRecordवस्तु पर तरीके हैं , तो मुझे जवाब में आपके द्वारा बताए गए परिणामों के विपरीत परिणाम मिल रहे हैं। जिसका मतलब है कि जब मैं उपयोग कर रहा हूं dup, तो यह idसेट होने के साथ एक नई वस्तु बनाता है और cloneइसका उपयोग करते समय यह idसेट किए बिना एक ऑब्जेक्ट बनाता है । क्या आप इसे फिर से देख सकते हैं और स्पष्ट कर सकते हैं? । Thnx
huzefa biyawarwala

रेल 5 में कुछ भी नहीं बदला है: api.rubyonrails.org/classes/ActiveRecord/… । इसलिए मेरा मानना ​​है कि आपके मामले में कुछ खास है ...
jvalanen

हालाँकि, cloneएक नया रिकॉर्ड जो कभी भी सहेजा नहीं गया है वह बहुत सुरक्षित होना चाहिए? क्या मैं इस तरह से "टेम्पलेट ऑब्जेक्ट" बना सकता हूं, और विशिष्ट उदाहरणों को बचाने के लिए इसे क्लोन कर सकता हूं?
सिरिल डचोन-डोरिस

30

एक अंतर जमे हुए वस्तुओं के साथ है। cloneएक जमे हुए वस्तु की भी जम जाता है (जबकि एक dupएक जमे हुए वस्तु की नहीं है)।

class Test
  attr_accessor :x
end
x = Test.new
x.x = 7
x.freeze
y = x.dup
z = x.clone
y.x = 5 => 5
z.x = 5 => TypeError: can't modify frozen object

एक और अंतर सिंगलटन विधियों के साथ है। एक ही कहानी यहाँ, dupउन लोगों की नकल नहीं है, लेकिन cloneकरता है।

def x.cool_method
  puts "Goodbye Space!"
end
y = x.dup
z = x.clone
y.cool_method => NoMethodError: undefined method `cool_method'
z.cool_method => Goodbye Space!

यह मेरे लिए बहुत उपयोगी था। यदि आप एक स्थिर स्थिर मूल्य बना रहे हैं और इसे कुछ इस तरह से पास कर रहे हैं: github.com/rack/rack/blob/master/lib/rack/utils.rb#L248 (रेल कुकी हैंडलिंग) तो आप आसानी से एक त्रुटि प्राप्त कर सकते हैं जब वे आपके साथ अनभिज्ञ होते हैं तो वे इसे क्लोन करते हैं और फिर क्लोन को संशोधित करने का प्रयास करते हैं। अपने जमे हुए मूल्य को धोखा देना और पास करना जो आपको कम से कम गारंटी देता है कि कोई भी गलती से यहां रैक को तोड़ने के बिना, आपके निरंतर को संशोधित नहीं करता है।
एक्सपी ४४

4

दोनों लगभग समान हैं लेकिन क्लोन एक से बढ़कर एक चीजें करता है। क्लोन में, ऑब्जेक्ट के जमे हुए राज्य को भी कॉपी किया जाता है। दुपट्टे में, यह हमेशा विगलित होगा।

 f = 'Frozen'.freeze
  => "Frozen"
 f.frozen?
  => true 
 f.clone.frozen?
  => true
 f.dup.frozen?
  => false 

4

नए दस्तावेज़ एक अच्छा उदाहरण में शामिल हैं:

class Klass
  attr_accessor :str
end

module Foo
  def foo; 'foo'; end
end

s1 = Klass.new #=> #<Klass:0x401b3a38>
s1.extend(Foo) #=> #<Klass:0x401b3a38>
s1.foo #=> "foo"

s2 = s1.clone #=> #<Klass:0x401b3a38>
s2.foo #=> "foo"

s3 = s1.dup #=> #<Klass:0x401b3a38>
s3.foo #=> NoMethodError: undefined method `foo' for #<Klass:0x401b3a38>

0

आप रूबी में प्रोटोटाइप-आधारित प्रोग्रामिंग करने के लिए क्लोन का उपयोग कर सकते हैं। रूबी का ऑब्जेक्ट क्लास क्लोन विधि और डुप विधि दोनों को परिभाषित करता है। क्लोन और डुप दोनों ही उस वस्तु की एक उथली प्रतिलिपि का उत्पादन करते हैं जो इसे कॉपी कर रहा है; वह है, ऑब्जेक्ट के उदाहरण चर कॉपी किए जाते हैं, लेकिन वे ऑब्जेक्ट नहीं जो वे संदर्भ देते हैं। मैं एक उदाहरण प्रदर्शित करूंगा:

class Apple
  attr_accessor :color
  def initialize
    @color = 'red'
  end
end

apple = Apple.new
apple.color
 => "red"
orange = apple.clone
orange.color 
 => "red"
orange.color << ' orange'
 => "red orange" 
apple.color
 => "red orange"

उपरोक्त उदाहरण में नोटिस, नारंगी क्लोन सेब ऑब्जेक्ट की स्थिति (यानी, उदाहरण चर) को कॉपी करता है, लेकिन जहां सेब ऑब्जेक्ट अन्य ऑब्जेक्ट (जैसे स्ट्रिंग ऑब्जेक्ट रंग) का संदर्भ देता है, उन संदर्भों की प्रतिलिपि नहीं बनाई जाती है। इसके बजाय, सेब और नारंगी दोनों एक ही वस्तु को संदर्भित करते हैं! हमारे उदाहरण में, संदर्भ स्ट्रिंग ऑब्जेक्ट 'रेड' है। जब नारंगी एपेंड विधि का उपयोग करता है, <<, मौजूदा स्ट्रिंग ऑब्जेक्ट को संशोधित करने के लिए, यह स्ट्रिंग ऑब्जेक्ट को 'रेड ऑरेंज' में बदलता है। यह प्रभाव Apple.color में भी परिवर्तन करता है, क्योंकि वे दोनों एक ही स्ट्रिंग ऑब्जेक्ट की ओर इशारा कर रहे हैं।

एक साइड नोट के रूप में, असाइनमेंट ऑपरेटर, =, एक नई ऑब्जेक्ट असाइन करेगा और इस तरह एक संदर्भ को नष्ट कर देगा। यहाँ एक प्रदर्शन है:

class Apple
  attr_accessor :color
  def initialize
    @color = 'red'
  end
end

apple = Apple.new
apple.color
=> "red"
orange = apple.clone
orange.color
=> "red"
orange.color = 'orange'
orange.color
=> 'orange'
apple.color
=> 'red'

उपरोक्त उदाहरण में, जब हमने नारंगी क्लोन के रंग उदाहरण विधि के लिए एक नई नई वस्तु सौंपी, तो यह सेब के समान वस्तु का संदर्भ नहीं देता है। इसलिए, हम अब सेब की रंग विधि को प्रभावित किए बिना नारंगी के रंग विधि को संशोधित कर सकते हैं, लेकिन अगर हम सेब से किसी अन्य ऑब्जेक्ट को क्लोन करते हैं, तो वह नई वस्तु सेब के रूप में कॉपी किए गए उदाहरण चर में समान वस्तुओं को संदर्भित करेगी।

डुप भी उस ऑब्जेक्ट की एक उथली प्रतिलिपि का उत्पादन करेगा जो वह कॉपी कर रहा है, और यदि आप उसी प्रदर्शन को करने के लिए ऊपर दिखाए गए थे, तो आप देखेंगे कि यह ठीक उसी तरह से काम करता है। लेकिन क्लोन और डुप के बीच दो प्रमुख अंतर हैं। सबसे पहले, जैसा कि दूसरों ने उल्लेख किया है, क्लोन जमे हुए राज्य की नकल करता है और डुबकी नहीं करता है। इसका क्या मतलब है? रूबी में 'फ्रोजन' शब्द अपरिवर्तनीय के लिए एक गूढ़ शब्द है, जो खुद कंप्यूटर विज्ञान में एक नामकरण है, जिसका अर्थ है कि कुछ बदला नहीं जा सकता है। इस प्रकार, रूबी में एक जमे हुए ऑब्जेक्ट को किसी भी तरह से संशोधित नहीं किया जा सकता है; यह वास्तव में, अपरिवर्तनीय है। यदि आप एक जमे हुए ऑब्जेक्ट को संशोधित करने का प्रयास करते हैं, तो रूबी एक रनटाइम एट्रस अपवाद को बढ़ाएगा। चूंकि क्लोन जमे हुए राज्य को कॉपी करता है, यदि आप एक क्लोन ऑब्जेक्ट को संशोधित करने का प्रयास करते हैं, तो यह एक रनटाइम एट्रस्ट अपवाद को बढ़ाएगा। इसके विपरीत, चूंकि डुबकी जमे हुए राज्य की नकल नहीं करती है,

class Apple
  attr_accessor :color
  def initialize
    @color = 'red'
  end
end

apple = Apple.new
apple.frozen?
 => false 
apple.freeze
apple.frozen?
 => true 
apple.color = 'crimson'
RuntimeError: can't modify frozen Apple
apple.color << ' crimson' 
 => "red crimson" # we cannot modify the state of the object, but we can certainly modify objects it is referencing!
orange = apple.dup
orange.frozen?
 => false 
orange2 = apple.clone
orange2.frozen?
 => true 
orange.color = 'orange'
 => "orange" # we can modify the orange object since we used dup, which did not copy the frozen state
orange2.color = 'orange'
RuntimeError: can't modify frozen Apple # orange2 raises an exception since the frozen state was copied via clone

दूसरा, और, अधिक दिलचस्प बात यह है कि क्लोन एकल वर्ग (और इसलिए इसके तरीके) की नकल करता है! यह बहुत उपयोगी है अगर आप रूबी में प्रोटोटाइप-आधारित प्रोग्रामिंग करना चाहते हैं। पहले, आइए दिखाते हैं कि वास्तव में सिंगलटन विधियों को क्लोन के साथ कॉपी किया जाता है, और फिर हम इसे रूबी में प्रोटोटाइप-आधारित प्रोग्रामिंग के एक उदाहरण में लागू कर सकते हैं।

class Fruit
  attr_accessor :origin
  def initialize
    @origin = :plant
  end
end

fruit = Fruit.new
 => #<Fruit:0x007fc9e2a49260 @origin=:plant> 
def fruit.seeded?
  true
end
2.4.1 :013 > fruit.singleton_methods
 => [:seeded?] 
apple = fruit.clone
 => #<Fruit:0x007fc9e2a19a10 @origin=:plant> 
apple.seeded?
 => true 

जैसा कि आप देख सकते हैं, फल वस्तु उदाहरण के सिंगलटन वर्ग को क्लोन में कॉपी किया गया है। और इसलिए क्लोन किए गए ऑब्जेक्ट का सिंगलटन विधि तक पहुंच है: वरीयता प्राप्त। लेकिन यह मामला नहीं है:

apple = fruit.dup
 => #<Fruit:0x007fdafe0c6558 @origin=:plant> 
apple.seeded?
=> NoMethodError: undefined method `seeded?'

अब प्रोटोटाइप-आधारित प्रोग्रामिंग में, आपके पास ऐसी कक्षाएं नहीं हैं जो अन्य कक्षाओं का विस्तार करती हैं और फिर उन कक्षाओं का उदाहरण बनाती हैं, जिनके तरीके एक मूल श्रेणी से प्राप्त होते हैं जो एक खाका के रूप में कार्य करता है। इसके बजाय, आपके पास एक आधार ऑब्जेक्ट है और फिर आप ऑब्जेक्ट से एक नई वस्तु बनाते हैं, जिसके तरीकों और राज्य की नकल की जाती है (बेशक, चूंकि हम क्लोन के माध्यम से उथली प्रतियां कर रहे हैं, किसी भी ऑब्जेक्ट का उदाहरण चर संदर्भ जावास्क्रिप्ट में ही साझा किया जाएगा। प्रोटोटाइप)। फिर आप क्लोन किए गए तरीकों के विवरण को भरकर ऑब्जेक्ट की स्थिति को भर सकते हैं या बदल सकते हैं। नीचे दिए गए उदाहरण में, हमारे पास आधार फल वस्तु है। सभी फलों में बीज होते हैं, इसलिए हम एक तरीका बनाते हैं नंबर_ऑफ_सेड्स। लेकिन सेब में एक बीज होता है, और इसलिए हम एक क्लोन बनाते हैं और विवरण भरते हैं। अब जब हम सेब का क्लोन बनाते हैं, तो हम न केवल तरीकों का क्लोन बनाते हैं, बल्कि हम राज्य का क्लोन बनाते हैं! याद रखें क्लोन राज्य की एक उथली प्रतिलिपि (उदाहरण चर) है। और उसके कारण, जब हम एक लाल_प्पल पाने के लिए सेब को क्लोन करते हैं, तो red_apple में स्वचालित रूप से 1 बीज होगा! आप Red_apple को Apple से विरासत में मिली हुई वस्तु के रूप में सोच सकते हैं, जो फल से विरासत में मिली है। इसलिए, यही कारण है कि मैंने फ्रूट और एप्पल को कैपिटल किया। हमने कक्षाओं और वस्तुओं के बीच के अंतर को क्लोन के सौजन्य से दूर किया।

Fruit = Object.new
def Fruit.number_of_seeds=(number_of_seeds)
  @number_of_seeds = number_of_seeds
end
def Fruit.number_of_seeds
  @number_of_seeds
end
 Apple = Fruit.clone
 => #<Object:0x007fb1d78165d8> 
Apple.number_of_seeds = 1
Apple.number_of_seeds
=> 1
red_apple = Apple.clone
 => #<Object:0x007fb1d892ac20 @number_of_seeds=1> 
red_apple.number_of_seeds
 => 1 

बेशक, हमारे पास प्रोटॉयपे-आधारित प्रोग्रामिंग में एक निर्माता विधि हो सकती है:

Fruit = Object.new
def Fruit.number_of_seeds=(number_of_seeds)
  @number_of_seeds = number_of_seeds
end
def Fruit.number_of_seeds
  @number_of_seeds
end
def Fruit.init(number_of_seeds)
  fruit_clone = clone
  fruit_clone.number_of_seeds = number_of_seeds
  fruit_clone
end
Apple = Fruit.init(1)
 => #<Object:0x007fcd2a137f78 @number_of_seeds=1> 
red_apple = Apple.clone
 => #<Object:0x007fcd2a1271c8 @number_of_seeds=1> 
red_apple.number_of_seeds
 => 1 

अंततः, क्लोन का उपयोग करके, आप जावास्क्रिप्ट प्रोटोटाइप व्यवहार के समान कुछ प्राप्त कर सकते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.