Domanda

Ruby definisce #clone in Oggetto . Con mia sorpresa, alcune classi generano eccezioni quando la chiamano. Ho trovato NilClass , TrueClass , FalseClass , Fixnum con questo comportamento.

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

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

È stato utile?

Soluzione

Non penso che ci sia un elenco formale, almeno a meno che tu non conti la lettura della fonte. Il motivo 2) non funziona a causa di un'ottimizzazione applicata a Fixnums. Sono memorizzati / passati internamente come valori effettivi (quindi sono true, false e nil) e non come puntatori. La soluzione ingenua è semplicemente che 42.clone restituisca lo stesso 42 , ma poi l'invariante obj.clone.object_id! = Obj.object_id non reggerebbe più, 42.clone in realtà non clonerebbe.

Altri suggerimenti

Fixnum è una classe speciale data un trattamento speciale dalla lingua. Dal momento in cui il tuo programma viene avviato, esiste esattamente un Fixnum per ogni numero che la classe può rappresentare e viene data una rappresentazione speciale che non occupa spazio extra & # 8212; in questo modo, le operazioni matematiche di base non stanno allocando e deallocando la memoria come un matto. Per questo motivo, non ci può essere più di un 42.

Per gli altri, hanno tutti una cosa in comune: sono singoli. C'è solo un'istanza di una classe singleton per definizione, quindi cercare di clonarla è un errore.

Non so ancora come testare la clonabilità in modo corretto, ma ecco un modo molto volgare e malvagio per testare la clonabilità usando il trapping degli errori:

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

Ed ecco come puoi clonare anche l'inconveniente. Almeno per le pochissime lezioni con cui l'ho stancato.

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

Ecco alcuni test di esempio:

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

Ho fatto un git grep " impossibile clonare " del codice sorgente di YARV, e ho 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 indicano che non è possibile clonare un singleton.

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

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

Puoi verificarlo in IRB con il metodo object_id. (simboli e fixnum ti daranno lo stesso object_id dopo chiamate ripetitive)

Rails sembra estendere le classi menzionate con un " duplicabile? () " metodo.

http://api.rubyonrails.org/ file / ActiveSupport / lib / active_support / core_ext / oggetto / duplicable_rb.html

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top