Ruby'nin dup ve clone yöntemleri arasındaki fark nedir?


214

İçin Yakut dokümanlardup söz hakkından:

Genel olarak cloneve dupsoyundan gelen sınıflarda farklı anlambilime sahip olabilir. İken clone, iç durumunda dahil bir nesne, çoğaltmak için kullanılır, dupgenelde yeni bir örneğini oluşturmak için soyundan nesnenin sınıfını kullanır.

Ama bazı testler yaptığımda aslında aynı olduklarını gördüm:

class Test
   attr_accessor :x
end

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

Peki iki yöntem arasındaki farklar nelerdir?


29
Ben de sadece fark değil bilseydim neyi dup ve cloneyapar, ancak neden diğer yerine birini kullanmayı tercih ediyorum.
Andrew Grimm

1
İşte iyi bir bağlantı da - coderwall.com/p/1zflyg
Arup

Yanıtlar:


298

Alt sınıflar, farklı anlambilim sağlamak için bu yöntemleri geçersiz kılabilir. Kendi Objectiçinde iki temel fark vardır.

Birincisi, clonesingleton sınıfını kopyalar, oysa kopyalamaz dup.

o = Object.new
def o.foo
  42
end

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

İkincisi, clonedonmuş durumu korur, ancak dupkorumaz.

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

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

Bu yöntemlerin için Rubinius uygulaması oldukça net ve oldukça uyumlu Yakut uygulama olduğundan, genellikle bu soruların cevaplarını benim kaynağıdır.


15
Birinin bunu tekrar değiştirmeye çalışması durumunda: Ruby'de iyi tanımlanmış bir terim olan "singleton sınıfı" yalnızca singleton yöntemlerini değil, singleton sınıfında tanımlanan sabitleri de içerir . Düşünün: o = Object.new; class << o; A=5; end; puts ( class << o.clone; A; end ); puts ( class << o.dup; A; end ).
Jeremy Roman

3
harika bir cevap, ardından harika bir yorum geldi, ama bu sözdizimini anlamam için vahşi bir kaz peşinde koştum. bu, karıştırılabilecek başka herkese de yardımcı olacaktır: devalot.com/articles/2008/09/ruby-singleton
davidpm4

1
Bence "singleton sınıfı" extendorijinal nesne üzerinde ed herhangi modülleri içerir bahsetmeye değer . Yani Object.new.extend(Enumerable).dup.is_a?(Enumerable)yanlış döndürür.
Daniel

Her ne kadar bu cevaplar soruyu cevaplıyor ve farklılıkları belirtiyor. Ayrıca her iki yöntemin Object # dup belgelerinde belirtildiği gibi farklı durumlar için tasarlandığını belirtmek gerekir . Klon için kullanım örneği , bir nesneyi aynı örnek olarak kullanmak üzere bir nesneyi klonlamaktır (farklı bir nesne kimliğine sahipken), dup'nin yeni bir örnek için temel olarak bir nesneyi çoğaltması amaçlanır.
3limin4t0r

189

ActiveRecord ile uğraşırken de önemli bir fark var:

dup kimliği ayarlanmadan yeni bir nesne oluşturur, böylece yeni bir nesneyi vurarak veritabanına kaydedebilirsiniz .save

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

clone aynı kimliğe sahip yeni bir nesne oluşturur, bu nedenle bu yeni nesnede yapılan tüm değişiklikler, isabet durumunda orijinal kaydın üzerine yazılır .save

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

43
Bu cevap IMO'nun en önemli pratik bilgiye sahip olduğu cevaptır ... diğer cevaplar esoterica'da bulunurken, bu cevap kritik bir pratik farkı belirler.
jpw

37
Yukarıdaki ActiveRecord için özel olsa da; standart Ruby'de fark çok daha incedir.
ahmacleod

1
@Stefan ve @jvalanen: Nesneme uyguladığımda dupve cloneyöntemler uygularken ActiveRecord, cevapta bahsettiklerinizin ters sonuçlarını alıyorum. yani ben kullanırken dup, yeni bir nesne idayarlanmış ile cloneoluşturur ve kullanırken bir nesne idayarlanmadan oluşturur. lütfen ona tekrar bakıp temizleyebilir misin? . Thnx
huzefa biyawarwala

Rails 5'te de hiçbir şey değişmedi: api.rubyonrails.org/classes/ActiveRecord/… . Bu yüzden senin
davanda

Ancak, clonehiç kaydedilmemiş yeni bir kayıt yapmak o zaman oldukça güvenli olmalı mı? Bu şekilde bir "şablon nesnesi" oluşturabilir ve belirli örnekleri kaydetmek için klonlayabilir miyim?
Cyril Duchon-Doris

30

Farklardan biri donmuş nesnelerdir. clone(A ise donmuş nesne de dondurulur dupdonmuş nesne değildir).

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

Diğer bir fark tekil yöntemlerdir. Burada aynı hikaye, dupbunları kopyalamıyor, ama kopyalıyor 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!

Bu benim için çok faydalı oldu. Dondurulmuş bir sabit değer oluşturuyorsanız ve bunu böyle bir şeye geçiriyorsanız : github.com/rack/rack/blob/master/lib/rack/utils.rb#L248 (Rails çerez işlemesi) kolayca hata alabilirsiniz onlar size farkında olmadan klonlar ve sonra klonu değiştirmeye çalışırlar. donmuş değerinizi çoğaltmak ve bunu geçmek, en azından kimsenin Rack'i kırmadan sabitinizi yanlışlıkla değiştirmediğini garanti etmenizi sağlar.
XP84

4

Her ikisi de neredeyse aynıdır, ancak klon dup'den bir şey daha yapar. Klonda, nesnenin donmuş hali de kopyalanır. Dup olarak, her zaman çözülecektir.

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

4

Yeni Doktor iyi bir örnek içerir:

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

Klon'u Ruby'de prototip tabanlı programlama yapmak için kullanabilirsiniz. Ruby'nin Object sınıfı hem klon yöntemini hem de dup yöntemini tanımlar. Hem klon hem de dup, kopyaladığı nesnenin sığ bir kopyasını üretir; yani, nesnenin örnek değişkenleri kopyalanır, ancak başvurdukları nesneler kopyalanmaz. Bir örnek göstereceğim:

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"

Yukarıdaki örnekte, turuncu klon, elma nesnesinin durumunu (yani, örnek değişkenleri) kopyalar, ancak elma nesnesinin diğer nesnelere (Dize nesnesi rengi gibi) başvurduğu yerlerde, bu başvurular kopyalanmaz. Bunun yerine, elma ve portakal aynı nesneyi ifade ediyor! Örneğimizde, referans 'kırmızı' dize nesnesidir. Turuncu, varolan String nesnesini değiştirmek için ekleme yöntemini << kullandığında, dize nesnesini 'kırmızı turuncu' olarak değiştirir. Her ikisi de aynı String nesnesini işaret ettiğinden, bu aslında apple.color'u da değiştirir.

Bir yan not olarak, atama operatörü =, yeni bir nesne atar ve böylece bir referansı yok eder. İşte bir gösteri:

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'

Yukarıdaki örnekte, turuncu klonun renk örneği yöntemine yeni bir nesne atadığımızda, artık elma ile aynı nesneye başvurmuyor. Bu nedenle, şimdi, elmanın renk yöntemini etkilemeden portakalın renk yöntemini değiştirebiliriz, ancak elmadan başka bir nesneyi klonlarsak, bu yeni nesne, kopyalanan örnek değişkenlerindeki elma ile aynı nesnelere başvuracaktır.

dup, kopyaladığı nesnenin sığ bir kopyasını da üretir ve dup için yukarıda gösterilenle aynı gösterimi yaparsanız, tam olarak aynı şekilde çalıştığını görürsünüz. Ancak klon ve dup arasında iki büyük fark vardır. Birincisi, diğerlerinin de belirttiği gibi, klon dondurulmuş durumu kopyalar ve dup kopyalamaz. Ne anlama geliyor? Ruby'de 'dondurulmuş' terimi değişmez için ezoterik bir terimdir, ki bu da bilgisayar biliminde bir terminolojidir, yani bir şeyin değiştirilemeyeceği anlamına gelir. Böylece, Ruby'deki donmuş bir nesne hiçbir şekilde değiştirilemez; aslında değişmezdir. Dondurulmuş bir nesneyi değiştirmeye çalışırsanız, Ruby bir RuntimeError istisnası oluşturur. Klon dondurulmuş durumu kopyaladığından, klonlanmış bir nesneyi değiştirmeye çalışırsanız, bir RuntimeError istisnası yükselir. Tersine, dup dondurulmuş durumu kopyalamadığından,

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

İkincisi, ve daha ilginç bir şekilde, klon singleton sınıfını (ve dolayısıyla yöntemlerini) kopyalar! Ruby'de prototip tabanlı programlama yapmak istiyorsanız bu çok yararlıdır. İlk olarak, gerçekten de singleton yöntemlerinin klonla kopyalandığını gösterelim ve sonra bunu Ruby'deki prototip tabanlı programlama örneğinde uygulayabiliriz.

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 

Gördüğünüz gibi, meyve nesnesi örneğinin singleton sınıfı klonuna kopyalanır. Ve böylece klonlanmış nesnenin singleton yöntemine erişimi vardır: tohumlanmış? Ancak dup ile durum böyle değil:

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

Şimdi prototip tabanlı programlamada, diğer sınıfları genişleten ve daha sonra yöntemleri bir plan görevi gören bir üst sınıftan türetilen sınıf örnekleri oluşturan sınıflarınız yoktur. Bunun yerine, bir temel nesneniz var ve daha sonra nesnelerle yöntemleri ve kopyalanan durumu ile yeni bir nesne oluşturuyorsunuz (elbette, klon yoluyla sığ kopyalar yaptığımız için, örnek değişkenlerinin başvurduğu tüm nesneler JavaScript'te olduğu gibi paylaşılacaktır prototipler). Daha sonra, klonlanan yöntemlerin ayrıntılarını doldurarak nesnenin durumunu doldurabilir veya değiştirebilirsiniz. Aşağıdaki örnekte, temel meyve nesnesimiz var. Tüm meyvelerin tohumları vardır, bu nedenle tohumlar_sayısı için bir yöntem oluştururuz. Ancak elmanın bir tohumu var ve bu yüzden bir klon yaratıyoruz ve ayrıntıları dolduruyoruz. Elmayı klonladığımızda, sadece yöntemleri klonlamakla kalmadık, aynı zamanda durumu klonladık! Klonun durumun sığ bir kopyasını yaptığını unutmayın (örnek değişkenler). Ve bu nedenle, bir red_apple elde etmek için elmayı klonladığımızda, red_apple otomatik olarak 1 çekirdeğe sahip olacak! Red_apple'ı, Apple'dan miras alan ve karşılığında Fruit'ten miras alan bir nesne olarak düşünebilirsiniz. Bu yüzden Fruit ve Apple'dan büyük harf aldım. Sınıflar ve nesneler arasındaki klonun izniyle ayrıldık.

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 

Tabii ki, prototip tabanlı programlamada bir yapıcı yöntemine sahip olabiliriz:

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 

Sonuçta, klon kullanarak JavaScript prototip davranışına benzer bir şey elde edebilirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.