2009-11-05 73 views
5

給予代碼:Perl如何決定在表達式中評估術語的順序?

my $x = 1; 

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

我希望$x180

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

但取而代之的則是30;但是,如果我更改條款的順序:

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

我得到180。我很困惑的原因是perldoc perlop很清楚地說:

TERM在Perl中的優先級最高。它們包括變量, 引用和引用類運算符,括號中的任何表達式和任何其參數括在括號內的函數 。

由於($x += 5)位於括號內,因此它應該是一個術語,因此無論表達式的順序如何,都應該先執行。

+4

你知道,先學習過C,我從來沒有做過這樣的事情,並期望它按照我認爲應該工作的方式工作:http://c-faq.com/expr/index.html ;-) – 2009-11-05 17:51:12

+3

我也來自ANSI C,是的,這不是我要寫的代碼,而是我在確定我理解Perl中的優先級之前,先向別人解釋它。使用這樣的副作用是一個主要的禁忌,但在Perl中仍然合法。在ANSI C中,如果在表達式中有多個副作用,那麼結果是未定義的,但在Perl中,副作用更好定義,但仍然是一個非常糟糕的主意。 – 2009-11-05 18:12:11

+0

我曾經對comp.lang.perl.misc做過一篇關於這個帖子的可愛帖子,並且從未能夠再次找到它。 – 2009-11-06 18:42:07

回答

16

輸入問題的行爲給我帶來了答案:術語具有最高優先級。這意味着,在代碼的第一個塊的$x被評估併產生1,然後5被評估和產量5,然後($x += 5)是評估和產率6(與設置$x6副作用):

$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 

類似地,第二實施例減少了這樣的:

$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

正確。絆倒你的不是優先順序,而是評價順序。 '($ x + = 5)'在第一次乘法發生之後纔會被評估。括號僅用於確保'+ ='發生在(第二)乘法之前。在這種情況下,這可以防止語法錯誤,因爲乘法具有比賦值更高的優先級,並且乘法的結果不是有效的左值。 – 2009-11-05 20:23:38

4

$x通過本身也是TERM。由於它首先遇到(在你的第一個例子中),它首先被評估。

9

每當我有這樣的東西,我的困惑先拔出perldoc perlop,然後如果我真不知道,還是希望看到一個特定的代碼塊將得到執行,我用B::Deparse

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

^d

給出:

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

在每個所以代值階段給出:

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

因此,事實上,$ X被臨時設置爲6並沒有真正影響到什麼,因爲早期的值(1)已經被代入表達,並表達到底現在是30

+0

有趣。但是它提出了一個問題,即$ x * 5左右的元素來自哪裏。 – innaM 2009-11-05 18:52:01

+4

'*'是一個從左到右的二元運算符,所以'$ x * $ y * $ z'會評估爲'(($ x * $ y)* $ z)'。 – Ether 2009-11-05 19:03:58

+0

以太,*評估*從左到右不同於*關聯*從左到右。即使'*'從右到左關聯,它仍然可以從左到右進行評估:首先評估$ x並將其值存儲在臨時值中;然後評估$ y,然後評估$ z;接下來將它們相乘並將結果乘以從$ x保存的臨時值。優先級不會確定評估順序,除非您可以找到一些針對Perl的文檔。 (例如,Java被定義爲從左到右評估,即使右側運算符具有更高的優先級。) – 2009-11-05 23:28:43

2

*運算符的結合性向左,所以最左邊的術語最右邊的項之前始終評估。其他運算符(例如**)是正確的關聯關係,並且在聲明的其餘部分之前將會評估($x += 5)

+0

您正在混合關聯性,優先級和評估順序。他們是三件獨立的事情。 – 2009-11-05 23:42:55