2009-09-13 12 views
8

Ruby definisce #clone in Oggetto. Con mia sorpresa, alcune classi sollevano eccezioni quando la chiamano. Ho trovato NilClass, TrueClass, FalseClass, Fixnum avere questo comportamento.Quali classi Ruby supportano .clone?

1) Esiste un elenco completo di classi (almeno core-class) che non consentono #clone? Oppure c'è un modo per rilevare se una classe specifica supporta #clone?

2) Cosa c'è di sbagliato con 42.clone?

+2

voglio davvero sapere come si prova se una classe è clonabile me stesso. Sembra che se una classe non vuole permettere a se stessa di essere clonata, allora dovrebbe rendere privato il metodo clone che eredita da Object in modo che tu possa testare la sua esistenza solo sotto public_methods. Sembra il buon senso per me. –

risposta

7

Non penso che ci sia un elenco formale, almeno a meno che non conti di aver letto la fonte. Il motivo 2) non funziona a causa di un'ottimizzazione applicata a Fixnums. Vengono memorizzati/passati internamente come valori effettivi (quindi sono veri, falsi e nulli) e non come puntatori. La soluzione ingenua è semplicemente avere 42 restituendo lo stesso 42, ma in tal caso l'obj.clone.object_id != obj.object_id invariabile non dovrebbe più contenere, 42.clone non sarebbe effettivamente la clonazione.

+0

'obj.clone.object_id! = Obj.object_id' è vero e' obj. clone.object_id == obj.object_id 'non sempre l'essere vero è diverso. Che il primo non regga non significa che il secondo no. – sawa

0

Non è possibile clonare classi immutabili. Cioè puoi avere una sola istanza dell'oggetto 42 (come un Fixnum), ma può avere molte istanze di "42" (perché la stringa è mutabile). Non è possibile clonare anche simboli poiché sono qualcosa di simile a stringhe immutabili.

È possibile verificarlo in IRB con il metodo object_id. (i simboli e i fixnum ti danno lo stesso object_id dopo le chiamate ripetitive)

+1

La mutabilità non ha nulla a che fare con esso (infatti, è possibile aggiungere lo stato a un Fixnum). – Chuck

+0

Comportamento predefinito veramente bizzarro per Fixnum, soprattutto dato che ha il metodo clone, d.class.method_defined? (: Clone) == true –

5

Fixnum è una classe speciale con un trattamento speciale dalla lingua. Dal momento in cui il tuo programma viene lanciato, c'è esattamente un Fixnum per ogni numero che la classe può rappresentare, e viene data una rappresentazione speciale che non occupa spazio extra - in questo modo, le operazioni matematiche di base non stanno allocando e deallocando memoria come un matto. Per questo motivo, non possono esserci più di un 42.

Per gli altri, hanno tutti una cosa in comune: sono single. C'è solo un'istanza di una classe singleton per definizione, quindi provare a clonarlo è un errore.

+1

"Per questo motivo non può esserci più di un 42.". E perché dovrebbe essere necessario? È perfetto. –

1

io ancora non so come eseguire il test per clonabilità correttamente ma ecco un modo male molto goffo per verificare clonablity usando l'intercettazione degli errori:

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

Ed ecco come si può clonare anche il non clonabile. Almeno per le poche classi con cui mi sono stancato.

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

Ecco alcune prove a campione:

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

ho fatto un git grep "can't clone" del codice sorgente di YARV, e ottenuto

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" 

La prima e la terza riga indicare non è possibile clonare un singleton .

La seconda riga si riferisce a rb_special_const_p(obj). Ma questo va oltre la mia comprensione.

Problemi correlati