2009-09-23 19 views
5

(follow-up alla mia precedente domanda, Ruby: how can I copy a variable without pointing to the same object?)Ruby: come posso copiare questo array?

Sto scrivendo un semplice programma Ruby per fare alcune sostituzioni in un file .svg. Il primo passo è quello di estrarre le informazioni dal file e inserirle in un array. Per evitare di leggere il file dal disco ogni volta che viene chiamata questa funzione, sto provando a utilizzare lo schema di progettazione di memoize: utilizzare un risultato memorizzato nella cache per ogni chiamata successiva alla prima.

Per fare ciò, sto utilizzando una variabile globale, definita appena prima della funzione. Ma anche se I .dup quella variabile a uno locale prima di restituire la variabile locale, la funzione che chiama questo sta ancora modificando la variabile globale.

Ecco il mio codice vero e proprio:

#memoize to keep from having to read original file on each pass 
$svg_filedata_cache = [] #the global variable 
def svg_filedata(filename) 
    if $svg_filedata_cache.empty? 
     File.open(filename, "r"){|f| $svg_filedata_cache = f.readlines} 
    end 
    svg_filedata_cache = $svg_filedata_cache.dup #try to copy it 
    return svg_filedata_cache #SHOULD point to a different object (but doesn't) 
end 

Due domande (risposta o entrambe):

  1. Perché le altre funzioni, che accolgono e modificare il valore restituito qui, influenzano anche il variabile globale, anche se ho usato .dup per copiarlo?
  2. Sono nuovo di Ruby e sono sicuro che questo non è il modo più rubyesco per farlo (e non mi piacciono le variabili globali, comunque). Puoi suggerire una strategia migliore?
+0

P.S. Mi rendo conto che dovrebbe essere davvero $ svg_filedata_cache [nomefile], per consentire chiamate di funzione usando nomi di file diversi, ma in questo caso non è necessario. –

+0

BTW, gli oggetti globali e quelli restituiti hanno object_id diverso, credo che tu stia menzionando le stringhe all'interno dell'array restituito, giusto? – khelll

+0

@khell - sì, ho basato la mia affermazione sul fatto che i contenuti della matrice originale venivano cambiati. –

risposta

9

La modifica della matrice di duplicazione non influisce sull'originale. Tuttavia, le modifiche alle stringhe all'interno dell'array saranno visibili a livello globale perché l'array globale e l'array duped contengono ancora riferimenti alle stesse stringhe (dup non esegue una copia profonda).

Quindi, eseguire una copia profonda (svg_filedata_cache = $svg_filedata_cache.map {|line| line.dup}) o semplicemente evitare operazioni di muting sulle stringhe.

+1

Non mi sono reso conto che ogni stringa nell'array era il suo oggetto! Immagino sia vero che "TUTTO in Ruby è un oggetto". :) –

6

Migliorare il codice un po ':

$svg_filedata_cache = [] #the global variable 
def svg_filedata(filename) 
    # Use ||= for memoiziation 
    $svg_filedata_cache ||= File.open(filename, "r"){|f| $svg_filedata_cache = f.readlines} 
    $svg_filedata_cache.dup #shallow copying 
end 

Aggiornamento: un semplice trucco per fare una copia profonda in generale è:

def deep_copy(obj) 
    Marshal.load(Marshal.dump(obj)) 
end 
+0

Quindi || = significa "se il lato sinistro è falso (vuoto), usa il lato destro?" –

+2

significa assegnare il valore del lato destro alla variabile di sinistra solo se quella variabile non è già impostata. – khelll

2

Il mondiale non è probabilmente in corso di modifica, ma gli elementi che stanno cambiando il tuo riferimento .dup. Per renderlo più rubale canonico, eliminare il globale, utilizzare una classe e leggere il file nella funzione initialize. (Il costruttore). Rendi la matrice come una variabile di istanza con @v.