2009-09-23 13 views
5

(Seguimiento de mi pregunta anterior, Ruby: how can I copy a variable without pointing to the same object?)Ruby: ¿cómo puedo copiar esta matriz?

Estoy escribiendo un programa simple de Ruby para hacer algunas sustituciones en un archivo .svg. El primer paso es extraer información del archivo y colocarlo en una matriz. Para no leer el archivo del disco cada vez que se llama a esta función, intento utilizar el patrón de diseño de memoria: use un resultado en caché en cada llamada después de la primera.

Para hacer esto, estoy usando una variable global, definida justo antes de la función. Pero a pesar de que I .dup es variable a una local antes de devolver la variable local, la función que llama a esta aún está modificando la variable global.

Aquí está mi código real:

#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 

Dos preguntas (respuesta o ambas):

  1. ¿Por qué otras funciones, que tienen la sesión y modificar el valor devuelto aquí, también afectan a la variable global, aunque utilicé .dup para copiarlo?
  2. Soy nuevo en Ruby y estoy seguro de que esta no es la forma más Rubyesque de hacer esto (y de todos modos no me gustan las variables globales). ¿Puedes sugerir una mejor estrategia?
+0

P.S. Me doy cuenta de que realmente debería ser $ svg_filedata_cache [nombre de archivo], para permitir llamadas de función usando diferentes nombres de archivo, pero eso no es necesario en este caso. –

+0

BTW, los objetos globales y devueltos tienen diferentes object_id, creo que estás mencionando las cadenas dentro de la matriz devuelta, ¿verdad? – khelll

+0

@khell: sí, basé mi afirmación en el hecho de que los contenidos de la matriz original estaban siendo modificados. –

Respuesta

9

La modificación de la matriz duplicada no afectará al original. Sin embargo, las modificaciones en las cadenas dentro de la matriz serán visibles globalmente porque la matriz global y la matriz duplicada aún contienen referencias a las mismas cadenas (dup no realiza una copia profunda).

O bien realice una copia profunda (svg_filedata_cache = $svg_filedata_cache.map {|line| line.dup}) o simplemente evite las operaciones de mutación en las cadenas.

+1

¡No me di cuenta de que cada cuerda del conjunto era su propio objeto! Creo que es verdad que "TODO en Ruby es un objeto". :) –

6

Mejora el código un poco:

$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 

Actualización: un truco simple de hacer una copia completa en general es:

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

Entonces || = significa "si el lado izquierdo es falso (vacío), use el lado derecho?" –

+2

significa asignar el valor del lado derecho a la variable del lado izquierdo solo si esa variable no está ya establecida. – khelll

2

lo global es probable que no se modificó, pero los elementos que esto y tu referencia .dup están cambiando. Para hacerlo más rubí canónico, deshacerse de lo global, usar una clase y leer el archivo en la función initialize. (El constructor). Haga que la matriz sea una variable de instancia con @v.

Cuestiones relacionadas