2011-08-25 8 views
5

Disculpa, esta parece ser una pregunta tan básica pero sigo sin entender. Si tengo un hash, por ejemplo:¿Por qué Perl piensa que hay un elemento hash multinivel inexistente?

my %md_hash =(); 
$md_hash{'top'}{'primary'}{'secondary'} = 0; 

¿Cómo es que esto es cierto?

if ($md_hash{'top'}{'foobar'}{'secondary'} == 0) { 
    print "I'm true even though I'm not in that hash\n"; 
} 

No hay un nivel "foobar" en el hash, ¿no debería ser falso?

TIA

Respuesta

6

intente una búsqueda en "Perl autovivification".

Los valores de hash "entran en existencia" cuando se accede por primera vez. En este caso, el valor es undef, que cuando se interpreta como un número es cero.

para probar la existencia de un valor hash y sin auto-vivificándolo, utilice el operador exists:

if (exists $md_hash{'top'}{'foobar'}{'secondary'} 
    && $md_hash{'top'}{'foobar'}{'secondary'} == 0) { 
    print "I exist and I am zero\n"; 
} 

Tenga en cuenta que esto sigue siendo auto-vivificar $md_hash{'top'} y $md_hash{'top'}{'foobar'} (es decir, los sub-hash).

[editar]

Como tchrist señala en un comentario, es un estilo pobre para comparar undef contra cualquier cosa. Por lo tanto una mejor manera de escribir este código sería:

if (defined $md_hash{'top'}{'foobar'}{'secondary'} 
    && $md_hash{'top'}{'foobar'}{'secondary'} == 0) { 
    print "I exist and I am zero\n"; 
} 

(. A pesar de esto ahora auto-vivificar los tres niveles del resumen anidada, estableciendo el nivel más bajo de undef')

+0

Awesome !! Tu ejemplo solucionó mi problema y tu consejo me llevó a http://www.perlmonks.org/?node=Autovivification, ¡ahora lo sé! ¡Gracias! – Analog

+0

@Analog: Pero esa no es la respuesta correcta. La respuesta correcta es no aplicar pruebas numéricas de igualdad contra valores indefinidos. – tchrist

+0

@tchrist: Entonces, cada afirmación que hice era 100% correcta, y resolví el problema del OP, pero como violé tu sentido personal de estilo, mi respuesta fue rechazada. ¿Lo tengo bien? ¡De buen tono! – Nemo

7

está probando un valor indefinido para el cero numérico. ¡Por supuesto que obtienes un verdadero resultado! ¿Que estabas esperando?

También debería recibir una advertencia en use warnings. ¿Por qué no?

Si no inicia un programa con:

use v5.12; # or whatever it is you are using 
use strict; 
use warnings; 

que realmente no debería molestarse. :)


EDITAR

Nota: Sólo estoy aclarando la corrección, porque las líneas de comentarios no son buenos para eso. Realmente no podría preocuparme un poco menos por los puntos de reputación. Solo quiero que la gente lo entienda.

Incluso bajo el módulo CPAN autovivification, nada cambia. Testigo:

use v5.10; 
use strict; 
use warnings; 
no autovivification; 

my %md_hash =(); 

$md_hash{top}{primary}{secondary} = 0; 

if ($md_hash{top}{foobar}{secondary} == 0) { 
    say "yup, that was zero."; 
} 

Cuando se ejecuta, que dice:

$ perl /tmp/demo 
Use of uninitialized value in numeric eq (==) at /tmp/demo line 10. 
yup, that was zero. 

La prueba es el operador ==. Su RHS es 0.Su LHS es undefindependientemente de la autovibición. Dado que undef es numéricamente 0, eso significa que tanto LHS como RHS contienen 0, que el == identifica correctamente como que tiene el mismo número.

Autovivificación no es el problema aquí, ya que se observa correctamente cuando escribe que "Esto no es una pregunta específica multidimensional de hash." Todo lo que importa es lo que pasa a ==. Y undef es numéricamente 0.

Puede detener autoviv si realmente, realmente desea - mediante el uso del pragma CPAN. Pero nunca podrás cambiar lo que sucede aquí al suprimir autoviv. Eso muestra que no es un asunto de autoviv, solo un undef.

Ahora, usted obtendrá "extra" claves cuando haga esto, ya que el valor lvalue indefinido se completará en el camino a través de la cadena de referencia. Estos son todos iguales neccesarily:

$md_hash{top}{foobar}{secondary}   # implicit arrow for infix deref 
$md_hash{top}->{foobar}->{secondary}  # explicit arrow for infix deref 
${ ${ $md_hash{top} }{foobar} }{secondary} # explicit prefix deref 

Cada vez que un DEREF lvaluable undef en Perl, que la ubicación de almacenamiento siempre se llena con el tipo adecuado de referencia a un referente en el anonimato recién asignado del tipo adecuado. En resumen, autovivs.

Y que se puede detener suprimiendo o bien dejando de lado el autoviv. Sin embargo, negar el autoviv no es lo mismo que eludirlo, porque solo cambias qué tipo de cosas se devuelven. La evaluación general todavía se evalúa por completo: no hay cortocircuitos automáticos simplemente porque suprime autoviv. Eso es porque autoviv no es el problema (y si no estuviera allí, estarías realmente molesto: confía en mí).

Si quiere un cortocircuito, tiene que escribirlo usted mismo. Parece que nunca necesito hacerlo. En Perl, eso es. Por otro lado, los programadores de C son bastante acostumbrado a escribir

if (p && p->whatever) { ... } 

Y por lo que pueden hacer eso, también, si así lo desea. Sin embargo, es bastante raro en mi propia experiencia. Casi tiene que doblarse en Perl para que eso marque la diferencia, ya que es bastante fácil organizar su código y no para cambiar la forma en que actúa si hay niveles vacíos.

+0

re 'always', no es así. y es difícil establecer la regla general; las cosas se autocifran cuando no es molesto hacerlo, que es más o menos cuando tiene lugar un tipo de operación modificadora. Compara 'my $ foo; 1 si @ $ foo; imprime $ foo' a 'mi $ foo; undef @ $ foo; imprime $ foo'. – ysth

+0

Si esto es diferente de lo que recuerda, es posible que Larry y usted hayan dejado p5p durante tantos años, lo que ha llevado a tomar algunas decisiones con las que no estuvo de acuerdo (por ejemplo, la eliminación del paquete '; ') – ysth

+2

@ysth: nunca salí de p5p. Simplemente me salté la lectura durante varios años. Todavía tengo todo en mi partición de correo. :) – tchrist

7

Esta no es una pregunta específica de hash multidimensional.

funciona de la misma con

my %foo; 
if ($foo{'bar'} == 0) { 
    print "I'm true even though I'm not in that hash\n"; 
} 

$foo{'bar'} es undef, que se compara fiel a 0, aunque con una advertencia si tiene advertencias habilitados como debería.

Hay un efecto secundario adicional en su caso; cuando se dice

my %md_hash =(); 
$md_hash{'top'}{'primary'}{'secondary'} = 0; 

if ($md_hash{'top'}{'foobar'}{'secondary'} == 0) { 
    print "I'm true even though I'm not in that hash\n"; 
} 

$md_hash{'top'} devuelve una referencia a un hash, y la tecla 'parametro' se busca en ese hash. Debido a {'secondary'}, la búsqueda del elemento 'foobar' está en el contexto de desreferencia de hash.Esto hace $md_hash{'top'}{'foobar'} "autovivify", una referencia a un hash como el valor de la clave 'parametro', dejándole con esta estructura:

my %md_hash = (
    'top' => { 
     'primary' => { 
      'secondary' => 0, 
     }, 
     'foobar' => {}, 
    }, 
); 

El pragma autovivification se puede utilizar para desactivar este comportamiento. La gente a veces afirma que existe() tiene algún efecto sobre la autovifificación, pero esto no es cierto.

+1

Sí, exactamente. Esto no es un problema de autoviv. Es un problema "olvidé usar' advertencias' ". Nunca * he * tenido autoviv ser un problema, pero a menudo he tenido 'undef' ser un problema. Autoviv nunca causó un error en mi código, nunca. Solo el uso indebido de pruebas definidas sobre contenidos indefinidos tiene. – tchrist

+0

Suspiro, sabes que lo hice y nunca me di cuenta del "Uso del valor no inicializado en la concatenación (.) O cadena", estaba imprimiendo demasiadas cosas. Mayor falla de mi parte. – Analog

Cuestiones relacionadas