2009-09-13 20 views
8

Ruby definiert #clone in Objekt. Zu meiner Überraschung, einige Klassen auslösen Ausnahmen beim Aufruf. fand ich NilClass, Trueclass, False, Fixnum dieses Verhalten haben.Welche Ruby-Klassen unterstützen .clone?

1) Gibt es eine vollständige Liste von Klassen (mindestens Kernklassen), die #clone nicht zulassen? Oder gibt es eine Möglichkeit zu erkennen, ob eine bestimmte Klasse #clone unterstützt?

2) Was ist los mit 42.clone?

+2

Ich möchte wirklich wissen, wie Sie testen, ob eine Klasse klonbar ist mir. Es scheint so, als ob eine Klasse sich nicht klonen lassen möchte, dann sollte sie die Klon-Methode privat machen, die sie von Object erbt, so dass Sie nur unter public_methods auf ihre Existenz testen können. Es scheint mir der gesunde Menschenverstand. –

Antwort

7

Ich glaube nicht, dass es eine formelle Liste gibt, zumindest wenn Sie das Lesen der Quelle zählen. Der Grund 2) funktioniert nicht, weil eine Optimierung auf Fixnums angewendet wurde. Sie werden intern als ihre tatsächlichen Werte gespeichert/übergeben (also sind wahr, falsch und null) und nicht als Zeiger. Die naive Lösung ist, einfach 42.clone die gleiche 42 zurückzugeben, aber dann würde die unveränderliche obj.clone.object_id != obj.object_id würde nicht mehr halten, 42.clone wäre nicht tatsächlich Klonen.

+0

'obj.clone.object_id! = Obj.object_id' ist true und' obj. clone.object_id == obj.object_id 'nicht immer wahr sind anders. Dass das erstere nicht gilt, bedeutet nicht, dass das letztere nicht gilt. – sawa

0

Sie können unveränderliche Klassen nicht klonen. I.e. Sie können nur eine Instanz von Objekt 42 (als Fixnum) haben, aber viele Instanzen von "42" (weil String veränderbar ist). Sie können auch keine Symbole klonen, da sie so etwas wie unveränderliche Zeichenketten sind.

Sie können dies in IRB mit object_id-Methode überprüfen. (Symbole und Fixnums geben Ihnen die gleiche object_id nach wiederholten Anrufen)

+1

Änderbarkeit hat nichts damit zu tun (in der Tat können Sie einen Zustand zu einem Fixnum hinzufügen). – Chuck

+0

Wirklich bizarres Standardverhalten für Fixnum, insbesondere da es die clone-Methode d.class.method_defined hat? (: Clone) == true –

5

Fixnum ist eine spezielle Klasse, die von der Sprache speziell behandelt wird. Ab dem Zeitpunkt, an dem das Programm gestartet wird, gibt es genau eine Fixnum für jede Zahl, die die Klasse darstellen kann, und sie erhalten eine spezielle Darstellung, die keinen zusätzlichen Platz benötigt - auf diese Weise werden grundlegende mathematische Operationen nicht zugeordnet und nicht zugewiesen Erinnerung wie verrückt. Aus diesem Grund kann es nicht mehr als eine 42.

Für die anderen haben sie alle eines gemeinsam: Sie sind Singletons. Es gibt nur eine Instanz einer Singleton-Klasse per Definition, daher ist es ein Fehler, sie zu klonen.

+1

"Aus diesem Grund kann es nicht mehr als eine 42 sein.". Und warum sollte es sein müssen? Es ist perfekt. –

1

Ich weiß noch nicht, wie für Klonierbarkeit richtig zu testen, aber hier ist ein sehr klobig, bösen Weg für clonablity zu testen Trapping mit Fehler:

def clonable?(value) 
    begin 
    clone = value.clone 
    true 
    rescue 
    false 
    end 
end 

Und hier ist, wie Sie selbst die nicht klonbare klonen. Zumindest für die wenigen Klassen, mit denen ich es vermasselt habe.

def super_mega_clone(value) 
    eval(value.inspect) 
end 

Hier einige Beispiel-Tests:

b = :b 
puts "clonable? #{clonable? b}" 

b = proc { b == "b" } 
puts "clonable? #{clonable? b}" 

b = [:a, :b, :c] 
c = super_mega_clone(b) 

puts "c: #{c.object_id}" 
puts "b: #{b.object_id}" 
puts "b == c => #{b == c}" 
b.each_with_index do |value, index| 
    puts "[#{index}] b: #{b[index].object_id} c: #{c[index].object_id}" 
end 
b[0] = :z 

puts "b == c => #{b == c}" 
b.each_with_index do |value, index| 
    puts "[#{index}] b: #{b[index].object_id} c: #{c[index].object_id}" 
end 

b = :a 
c = super_mega_clone(b) 
puts "b: #{b.object_id} c: #{c.object_id}" 

> clonable? false 
> clonable? true 
> c: 2153757040 
> b: 2153757480 
> b == c => true 
> [0] b: 255528 c: 255528 
> [1] b: 255688 c: 255688 
> [2] b: 374568 c: 374568 
> b == c => false 
> [0] b: 1023528 c: 255528 
> [1] b: 255688 c: 255688 
> [2] b: 374568 c: 374568 
> b: 255528 c: 255528 
1

ich ein git grep "can't clone" von YARV Quellcode hat, und

bekam
lib/singleton.rb: raise TypeError, "can't clone instance of singleton #{self.class}" 
object.c:  rb_raise(rb_eTypeError, "can't clone %s", rb_obj_classname(obj)); 
test/test_singleton.rb: expected = "can't clone instance of singleton TestSingleton::SingletonTest" 

der ersten und dritten Linien zeigen Sie eine Singleton nicht klonen .

Die zweite Zeile bezieht sich auf rb_special_const_p(obj). Aber das geht über meinen Verstand hinaus.

Verwandte Themen