Genellikle bir Ruby'deki değer. Bu basit gibi görünse de, bir verinin kopyasını almanız gerektiğinde basit nesneler içindir. aynı nesne üzerinde birden fazla dizi veya karma ile yapısı, hızlı bir şekilde birçok vardır bulacaksınız tuzaklar.
Nesneler ve Referanslar
Neler olduğunu anlamak için basit bir koda bakalım. İlk olarak, bir POD (Düz Eski Veri) türü kullanan atama operatörü Yakut.
a = 1
b = a
a + = 1
b koyar
Burada, atama operatörü şu değerin bir kopyasını oluşturuyor: bir ve b atama operatörünü kullanarak. Herhangi bir değişiklik bir yansıtılmayacak b. Peki ya daha karmaşık bir şey? Bunu düşün.
a = [1,2]
b = a
a << 3
b.inspect koyar
Yukarıdaki programı çalıştırmadan önce, çıktının ne olacağını ve nedenini tahmin etmeye çalışın. Bu önceki örnekle aynı değil, yapılan değişiklikler bir yansıtılır b, ama neden? Çünkü Dizi nesnesi bir POD türü değil. Atama operatörü değerin bir kopyasını oluşturmaz, yalnızca referans Array nesnesine. bir ve b değişkenler şimdi Referanslar aynı Array nesnesine, her iki değişkende de herhangi bir değişiklik diğerinde görülecektir.
Ve şimdi önemsiz olmayan nesneleri başka nesnelere referanslarla kopyalamanın neden zor olabileceğini görebilirsiniz. Sadece nesnenin bir kopyasını yaparsanız, referansları yalnızca daha derin nesnelere kopyalarsınız, böylece kopyanıza "sığ kopya" denir.
Ruby Neler Sağlar: dup ve clone
Ruby, derin kopyalar yapmak için yapılabilecekler de dahil olmak üzere, nesnelerin kopyalarını oluşturmak için iki yöntem sunar. Nesne # dup yöntemi nesnenin sığ bir kopyasını oluşturur. Bunu başarmak için, dup yöntemi arayacak initialize_copy o sınıfın yöntemi. Bunun tam olarak yaptığı şey sınıfa bağlıdır. Dizi gibi bazı sınıflarda, orijinal diziyle aynı üyelere sahip yeni bir dizi başlatır. Ancak bu derin bir kopya değildir. Aşağıdakileri göz önünde bulundur.
a = [1,2]
b = a.dup
a << 3
b.inspect koyar
a = [[1,2]]
b = a.dup
a [0] << 3
b.inspect koyar
Burada ne oldu? Dizi # initialize_copy yöntemi aslında bir Dizinin bir kopyasını oluşturur, ancak bu kopyanın kendisi sığ bir kopyadır. Dizinizde POD olmayan başka türler varsa, dup yalnızca kısmen derin bir kopya olacaktır. Sadece ilk dizi kadar derin, daha derin diziler, karmaları veya diğer nesneler yalnızca sığ kopyalanır.
Bahsetmeye değer başka bir yöntem var, klon. Klon yöntemi aynı şeyi yapar dup önemli bir ayrımla: nesnelerin bu yöntemi derin kopyalar yapabilen bir yöntemle geçersiz kılması bekleniyor.
Peki pratikte bu ne anlama geliyor? Sınıflarınızın her birinin, o nesnenin derin bir kopyasını oluşturacak bir klonlama yöntemi tanımlayabileceği anlamına gelir. Ayrıca, yaptığınız her sınıf için bir klon yöntemi yazmanız gerektiği anlamına gelir.
Bir Hile: Marshalling
Bir nesneyi "Marshalling" etmek, bir nesneyi "serileştirmek" demenin başka bir yoludur. Başka bir deyişle, o nesneyi, daha sonra aynı nesneyi elde etmek için "mareşalsiz" veya "diziselleştiremediğiniz" bir dosyaya yazılabilen bir karakter akışına dönüştürün. Herhangi bir nesnenin derin bir kopyasını elde etmek için bundan faydalanılabilir.
a = [[1,2]]
b = Mareşal.load (Mareşal.dump (a))
a [0] << 3
b.inspect koyar
Burada ne oldu? Marshal.dump içinde saklanan iç içe dizinin bir "dökümünü" oluşturur bir. Bu döküm, bir dosyada saklanması amaçlanan bir ikili karakter dizesidir. Dizinin tüm içeriğini, tam bir derin kopyasını barındırır. Sonraki, Marshal.load tam tersini yapar. Bu ikili karakter dizisini ayrıştırır ve tamamen yeni Array öğeleriyle tamamen yeni bir Array oluşturur.
Ama bu bir hile. Verimsizdir, tüm nesnelerde çalışmaz (bir ağ bağlantısını bu şekilde klonlamaya çalışırsanız ne olur?) Ve muhtemelen çok hızlı değildir. Ancak, derin kopyaları özelden daha kısa hale getirmenin en kolay yolu initialize_copy veya klon yöntemleri. Ayrıca, aynı şey aşağıdaki gibi yöntemlerle yapılabilir to_yaml veya to_xml bunları desteklemek için yüklenmiş kitaplıklarınız varsa.