2011-05-09 5 views
6

Quiero usar el valor predeterminado para reiniciar mi ary cuando lo necesito. Pero no puedo entender cómo cambiar los valores de los valores predeterminados cuando los valores de ary cambian.¿Cómo se usa el valor predeterminado en un hash de matrices vacías?

> default = {"a"=>[], "b"=>[], "c"=>[]} 
=> {"a"=>[], "b"=>[], "c"=>[]} 

> ary = default.clone 
=> {"a"=>[], "b"=>[], "c"=>[]} 

> ary["a"] << "foo" 
=> ["foo"] 

> default 
=> {"a"=>["foo"], "b"=>[], "c"=>[]} 
+0

¿Puedo preguntarte por qué estás haciendo esto? –

+0

Básicamente es una lista de estadísticas asignadas a diferentes estados, siendo los estados las claves. Periódicamente quiero restablecer la lista de estadísticas pero mantener todos los estados intactos. –

Respuesta

7

Lo que se ha descubierto es que Hash#clone sólo un clon sin profundidad, es decir que sólo se replica a sí mismo, pero no los objetos que se hace referencia dentro de ella.

Hay una serie de joyas "profunda clonar" que abordan este problema específico, o puede escribir su propia para trabajar alrededor de ella:

class Hash 
    def deep_clone 
    Hash[collect { |k,v| [ k, v.respond_to?(:deep_clone) : v.deep_clone : v ] }] 
    end 
end 

class Array 
    def deep_clone 
    collect { |v| v.respond_to?(:deep_clone) : v.deep_clone : v } 
    end 
end 

Esto le permitirá clonar Hash y matrices de objetos arbitrarios según sea necesario .

+0

Gracias por la ayuda, pero obtengo errores de sintaxis con respecto a la declaración responder_a? (: Clonar). –

+0

Eso solo comprueba si el objeto en cuestión admite el método 'deep_clone' antes de llamarlo, ya que no puede clonar cosas como números o' true' y 'false' entre otras cosas. Lo único que necesitarás para realizar una clonación profunda en la práctica son los objetos tipo contenedor. – tadman

3

Tanto el clon como el dup crean una copia superficial de su objeto, lo que da como resultado este comportamiento. No estoy seguro de cuál es la forma correcta para lograr una copia profunda es, pero en lugar de:

ary = default.clone 

Probar:

ary = Marshal.load(Marshal.dump(default)) 

Esto está tomado de un entorno real de rubí 2.3.8 1.8. 7

+0

Marshal no puede manejar todo lo que le arrojas, así que ten cuidado. Además, esta tiene que ser una de las formas más caras de realizar este tipo de cosas. – tadman

+0

Por eso dije que no estaba seguro de cuál era la mejor manera de hacerlo. Me gusta su sugerencia como una solución más específica para cada caso, así que obtuvo mi voto popular;) –

2

clone solo hace copias poco profundas, por lo que la clonación de hash aún mantiene todo apuntando a las mismas matrices anidadas.

Esto se puede evitar a través de la clase Marshal por el dumping y luego cargar en los valores de objeto:

> default = {"a" => [], "b" => [], "c" => []} 
=> {"a"=>[], "b"=>[], "c"=>[]} 
> ary = Marshal.load(Marshal.dump(default)) 
=> {"a"=>[], "b"=>[], "c"=>[]} 
> ary["a"] << "foo" 
=> ["foo"] 
> default 
=> {"a"=>[], "b"=>[], "c"=>[]} 
2
class Object 
    def deep_clone 
    Marshal::load(Marshal.dump(self)) 
    end 
end 

default = {"a"=>[], "b"=>[], "c"=>[]} 
ary = default.deep_clone 
ary["a"] << "foo" 
default {"a"=>[], "b"=>[], "c"=>[]} 
+0

Es extraño que use la notación '::' para un método y '.' para otro, pero +1 en general. – Phrogz

+0

El hecho de que pueda llamar a métodos con '::' siempre me pareció peculiar y confuso. – tadman

2

Una forma de hacerlo es la siguiente:

ary = Marshal.load(Marshal.dump(default)) 
2

Dependiendo lo que desea hacer, una alternativa más sencilla a la escritura de un método de clonación profunda podría ser escribir un método que crea una nueva matriz predeterminada cada vez que se llame:

def default 
    {"a"=>[], "b"=>[], "c"=>[]} 
end 

ary = default #=> {"a"=>[], "b"=>[], "c"=>[]} 

ary["a"] << "foo" #=> {"a"=>["foo"], "b"=>[], "c"=>[]} 

default #=> {"a"=>[], "b"=>[], "c"=>[]} 

Por supuesto, si el contenido de su hash predeterminado cambia en el transcurso del programa, esto no funcionará y tendrá que buscar las técnicas de clonación o clasificación, pero si se resuelven los contenidos esto podría ser una solución más directa.

Cuestiones relacionadas