2009-11-05 10 views
5

względu na kod:W jaki sposób Perl decyduje, która kolejność oceny terminów w wyrażeniu?

my $x = 1; 

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

spodziewałbym $x być 180:

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

Ale zamiast tego 30; Jednak, jeśli mogę zmienić kolejność warunków:

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

ja rozumiem 180. Powodem dla którego jestem zdezorientowany jest to, że perldoc perlop mówi bardzo wyraźnie:

TERM ma najwyższy priorytet w Perlu. Obejmują one zmienne, operatory kwotowania i cytowań, dowolne wyrażenie w nawiasach i dowolną funkcję , której argumenty są nawiasami.

Ponieważ ($x += 5) znajduje się w nawiasach, powinien być terminem, a zatem wykonywany jako pierwszy, niezależnie od kolejności wyrażenia.

+4

Po pierwszym zapoznaniu się z C, nigdy nie robię takich rzeczy i oczekuję, że będą działać tak, jak powinienem: http://c-faq.com/expr/index.html ;-) –

+3

Ja też pochodziłem z ANSI C, i tak, to nie jest kod, który bym napisał, to ja próbuję upewnić się, że rozumiem pierwszeństwo w Perlu, zanim wyjaśnię to komuś innemu. Korzystanie z efektów ubocznych, takich jak ten, jest głównym nie-nie, ale wciąż legalne w Perlu. W ANSI C, jeśli miałeś więcej niż jeden efekt uboczny w wyrażeniu, wyniki były niezdefiniowane, w Perlu efekty uboczne są lepiej zdefiniowane, ale nadal jest to naprawdę zły pomysł. –

+0

Kiedyś zrobiłem piękny post o tym na comp.lang.perl.misc i nigdy nie udało mi się go znaleźć ponownie. –

Odpowiedz

16

Czynność wpisania pytania dała mi odpowiedź: warunki mają najwyższy priorytet. Oznacza to, że $x w pierwszym kawałku kodu ocenie i otrzymano 1, następnie 5 oceniano i wydajności 5, następnie ($x += 5) to ocenić i wydajności 6 (z efektem ubocznym ustalania $x do 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 

Podobnie, drugi przykład zmniejsza tak:

$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

Prawidłowo. To, co cię potykało, nie było precedensem, lecz porządkiem oceny. '($ X + = 5)' nie jest oceniane aż do momentu pierwszego mnożenia. Nawiasy służą tylko zapewnieniu, że '+ =' stanie się przed (drugim) mnożeniem. W tym przypadku zapobiega to błędowi składni, ponieważ mnożenie ma wyższy priorytet niż przypisanie, a wynik mnożenia nie jest poprawną lwartością. –

4

$x sam jest termin. Ponieważ jest napotkany pierwszy (w twoim pierwszym przykładzie), jest oceniany jako pierwszy.

9

Ilekroć mam zamieszanie o rzeczy, jak to ja pierwszy wyciągnąć perldoc perlop, a następnie, jeśli nadal nie jestem pewien, czy chcę zobaczyć jak dany fragment kodu będzie się stracony, używam B::Deparse:

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

^D

otrzymujemy:

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

więc, podstawiając wartości na każdym etap daje:

($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; 

więc fakt, że $ x został tymczasowo ustawić do 6 tak naprawdę nie wpływa na nic, bo wcześniej wartość (1) był już podstawiony do wypowiedzi, a pod koniec tego wyrazu to jest teraz 30.

+0

Interesujące. Ale pojawia się pytanie, skąd pochodzą pareny około $ x * 5. – innaM

+4

'*' jest operatorem binarnym, który ocenia od lewej do prawej, więc '$ x * $ y * $ z' oceniłby jako' (($ x * $ y) * $ z) '. – Ether

+0

Eter, * ocena * od lewej do prawej to nie to samo, co * powiązanie * od lewej do prawej. Nawet jeśli "*" jest skojarzone z prawą stroną do lewej, może być nadal oceniane od lewej do prawej: najpierw oceń x $ i zapisz jego wartość tymczasowo; następnie oceń $ y, następnie $ z; następnie pomnóż je i pomnóż wynik z zapisaną wartością tymczasową z $ x. Pierwszeństwo nie określa kolejności oceny, chyba że możesz znaleźć dokumentację, która mówi inaczej w przypadku Perla. (Na przykład Java jest zdefiniowana do oceny od lewej do prawej, nawet jeśli prawy operator ma wyższy priorytet). –

2

Zależność operatora * jest lewostronna, więc lewy najbardziej termin jest zawsze oceniany przed właściwym terminem.Inni operatorzy, na przykład **, są stowarzyszeni prawostronnie i przed pozostałą częścią instrukcji oceniliby numer ($x += 5).

+0

Łączysz asocjatywność, pierwszeństwo i kolejność oceny. Są to trzy oddzielne rzeczy. –

Powiązane problemy