2009-11-05 6 views
5

Teniendo en cuenta el código:¿Cómo decide Perl qué orden evaluar los términos en una expresión?

my $x = 1; 

$x = $x * 5 * ($x += 5); 

yo esperaría $x ser 180:

$x = $x * 5 * ($x += 5); #$x = 1 
$x = $x * 5 * 6;   #$x = 6 
$x = 30 * 6; 
$x = 180; 
180; 

sino que es 30; Sin embargo, si cambio el orden de los términos:

$x = ($x += 5) * $x * 5; 

me pongo 180. La razón por la que estoy confundido es que perldoc perlop dice muy claramente:

A TERM tiene la precedencia más alta en Perl. Incluyen variables, presupuesto y operadores de tipo cita, cualquier expresión entre paréntesis y cualquier función cuyos argumentos estén entre paréntesis.

Dado que ($x += 5) está entre paréntesis, debe ser un término y, por lo tanto, debe ejecutarse primero, independientemente del orden de la expresión.

+4

Ya sabes, habiendo aprendido C primero, nunca hago cosas como esta y espero que funcione de la manera que creo que debería funcionar: http://c-faq.com/expr/index.html ;-) –

+3

Yo también provengo de ANSI C, y sí, este no es el código que escribiría, estoy tratando de asegurarme de que entiendo la precedencia en Perl antes de que se lo explique a alguien más. Usar efectos secundarios como este es un gran no-no, pero aún legal en Perl. En ANSI C, si tenía más de un efecto secundario en una expresión, los resultados no estaban definidos, en Perl los efectos secundarios están mejor definidos, pero sigue siendo una muy mala idea. –

+0

Una vez publiqué una buena publicación sobre esto para comp.lang.perl.misc, y nunca he podido encontrarla de nuevo. –

Respuesta

16

El hecho de escribir la pregunta me dio la respuesta: los términos tienen la mayor precedencia. Eso significa que el $x en el primer trozo de código se evalúa y los rendimientos 1, entonces 5 se evalúa y los rendimientos 5, entonces ($x += 5) es evaluar y rendimientos 6 (con un efecto secundario de establecer $x-6):

$x = $x * 5 * ($x += 5); 
address of $x = $x * 5 * ($x += 5); #evaluate $x as an lvalue 
address of $x = 1 * 5 * ($x += 5); #evaluate $x as an rvalue 
address of $x = 1 * 5 * ($x += 5); #evaluate 5 
address of $x = 1 * 5 * 6;   #evaluate ($x += 5), $x is now 6 
address of $x = 1 * 5 * 6;   #evaluate 1 * 5 
address of $x = 5 * 6;    #evaluate 1 * 5 
address of $x = 30;     #evaluate 5 * 6 
30;         #evaluate address of $x = 30 

mismo modo, el segundo ejemplo se reduce de esta manera:

$x = ($x += 5) * $x * 5; 
address of $x = ($x += 5) * $x * 5; #evaluate $x as an lvalue 
address of $x = 6 * $x * 5;   #evaluate ($x += 5), $x is now 6 
address of $x = 6 * 6 * 5;   #evaluate $x as an rvalue 
address of $x = 6 * 6 * 5;   #evaluate 5 
address of $x = 36 * 5;    #evaluate 6 * 6 
address of $x = 180;    #evaluate 36 * 5 
180;        #evaluate $x = 180 
+7

Correcto. Lo que te hizo tropezar no fue la precedencia sino el orden de evaluación. El '($ x + = 5)' no se evalúa hasta que se produce la primera multiplicación. Los paréntesis solo sirven para garantizar que '' + = 'ocurra antes de la (segunda) multiplicación. En este caso, eso evita un error de sintaxis porque la multiplicación tiene una precedencia mayor que la asignación y el resultado de la multiplicación no es un valor l válido. –

4

$x por sí mismo es también un término. Como se encuentra primero (en su primer ejemplo), se evalúa primero.

9

Cada vez que tengo la confusión acerca de cosas como esta por primera vez sacar perldoc perlop, y luego, si todavía no estoy seguro, o quiero ver cómo conseguirá ejecuta un bloque de código en particular, yo uso B::Deparse:

perl -MO=Deparse,-p,-q,-sC 
my $x = 1; 
$x = $x * 5 * ($x += 5); 

^D

da:

(my $x = 1); 
($x = (($x * 5) * ($x += 5))); 
- syntax OK 

valores Así sustituyendo en cada etapa da:

($x = (($x * 5) * ($x += 5))); 
($x = ((1 * 5) * ($x += 5))); 
($x = ((5) * (6))); # and side-effect: $x is now 6 
($x = (5 * 6)); 
($x = (30)); 
($x = 30); 
$x = 30; 

Así que el hecho de que $ x se ajusta temporalmente en 6 realidad no afectar para nada, ya que el valor anterior (1) ya fue sustituido en la expresión, y para el final de la expresión se ahora es 30.

+0

Interesante. Pero plantea la pregunta de dónde provienen los parens de $ x * 5. – innaM

+4

'*' es un operador binario que evalúa de izquierda a derecha, por lo que '$ x * $ y * $ z' se evaluaría como' (($ x * $ y) * $ z) '. – Ether

+0

Ether, * evaluar * de izquierda a derecha no es lo mismo que * asociar * de izquierda a derecha. Incluso si '*' se asocia de derecha a izquierda, aún se puede evaluar de izquierda a derecha: primero evalúe $ x y almacene su valor de forma temporal; luego evalúa $ y, luego $ z; luego multiplíquelos y multiplique el resultado con el valor temporal guardado de $ x. La precedencia no determina el orden de evaluación, a menos que pueda encontrar alguna documentación que indique lo contrario para Perl. (Java, por ejemplo, se define para evaluar de izquierda a derecha, incluso si un operador de la derecha tiene mayor precedencia). –

2

La asociatividad del operador * está hacia la izquierda, por lo que el término más a la izquierda siempre se evalúa antes que el término más correcto.Otros operadores, como ** son asociativos correctos y habrían evaluado ($x += 5) antes del resto de la declaración.

+0

Está combinando asociatividad, precedencia y orden de evaluación. Son tres cosas separadas. –

Cuestiones relacionadas