2009-08-31 84 views
12

我想執行@a = @b || @c作業的一些複雜的變化,意圖如果非空(因此在布爾意義上是真的)@b,否則@c。該文件明確告訴我我不能。 (它是正確的事實,太多!)爲什麼我不能分配@b ||在Perl中@c到@a?

的 「||」, 「//」 和 「& &」 操作符返回評估 的最後一個值(與C不同的 「||」 和「& & 「,它返回0或1)。

[...]

具體而言,這意味着,你不應該使用這個選擇聚集 兩者之間進行分配:

@a = @b || @c;    # this is wrong 
@a = scalar(@b) || @c;  # really meant this 
@a = @b ? @b : @c;   # this works fine, though 

不幸的是,它並沒有真正告訴我爲什麼。

我預期會發生的是:

  • @a =是一個數組賦值,在右手側誘導列表環境。
  • @b || @c是右側,在列表上下文中進行評估。
  • ||是C型短路邏輯或。它從左向右評估(如果需要)並傳播上下文。
  • @b在列表上下文中進行評估。如果爲真(,即,非空),則返回。
  • 如果不是,@c也在列表上下文中進行評估並返回。

顯然,我的倒數第二個陳述是錯誤的。爲什麼?而且,更重要的是,哪部分文檔或來源解釋了這種行爲? PS:在問題的範圍內,我避免使用三元運算符的文檔建議的原因是我的@b實際上是一個臨時(函數調用結果)。

+2

說「真的意味着這個」的行告訴你爲什麼。 – 2009-08-31 20:46:16

+0

這不僅僅是爲什麼我。但我有偏見。 – 2009-08-31 21:02:57

+1

我寧願將評論「真的意味着這個」改爲「真的意味着這個」。事實上,我期待標量()的變化將@a設置爲等於@b--即作爲作者真正想用的東西 - 顯然並非如此。相反,第一行確實意味着第二行;他們都得到了相同的結果,這不是理想的答案。 – Rini 2009-09-01 15:14:11

回答

7

邏輯或運算符(「||」)在標量上下文中評估其左手參數。

它這樣做的原因是要弄清楚參數是否爲真。布爾上下文,作爲標量上下文的特例,將其強制爲標量上下文。


perldoc perlop"C-style-Logical-Or"

二進制 「||」 執行短路邏輯OR操作。也就是說,如果左操作數是,則右操作數甚至沒有被評估。 ...


perldoc perldata"Scalar values"

....布爾上下文是其中執行過任何轉換爲​​字符串或數字的特殊的上下文。

+0

你有我正在尋找的答案。我在perldata中發現了這一點:「布爾上下文只是一種特殊的標量上下文,在這種上下文中不會執行任何對字符串或數字的轉換。」雖然(「需要弄清楚它是否屬實)」的理由對我來說似乎有點可疑。 – 2009-08-31 14:33:54

+0

值可以爲真或假,但聚合不能。對於聚合「真實」通常意味着「非空」。在標量上下文中評估聚合時,如果聚合爲空,則會得到一個值,否則爲真。 – 2009-08-31 15:19:30

+0

@Michael:儘管你說的話與所有的解釋一致,但在我看來,對於perlsyn關於真假的段落來說,它有點神奇(即無法解釋)和矛盾,它大多數是這樣說的:「空列表是錯誤的,任何東西別的是真的「。我需要接受這一事實的相關信息是由布拉德帶來的:「布爾評估迫使標量。」來自perlsyn,IMO的重要演繹。 – 2009-08-31 16:29:09

7

在perlop中,短短几段節前你報價:

 
Binary "||" performs a short-circuit logical OR operation. That is, 
if the left operand is true, the right operand is not even evaluated. 
Scalar or list context propagates down to the right operand if it is 
evaluated. 

這並沒有明確指出列表上下文不會傳播到左操作數,但perlop得到國家的頂部:

 
With very few exceptions, these all operate on scalar values 
only, not array values. 

,所以我們可以假設,列表上下文傳播到右側操作數的 例外,以及有關的 左邊的背景下,沒有任何語句操作數意味着適用一般規則。

2

這是因爲||評估標量上下文中的左側,就像?:的測試參數一樣。 如果你不能使用三元,使用函數:

sub or_array (\@\@) { 
    return @{$_[0]} if (scalar @{$_[0]}); 
    return @{$_[1]}; 
} 

@a = or_array(@b, @c); 
+0

毫無疑問,perl6中有一個神祕的操作符來處理這個問題。 :) – Ether 2009-08-31 16:14:26

0

如果你不能直接使用條件運算符,你可以方便地使用稍微不太簡潔:

my $ref_b = [ @b ]; # Ideally, just return an arrayref from your function 
my @a = @$ref_b ? @$ref_b : @c; 

根據上面的答案,您的代碼不起作用,因爲||的左側評估的邏輯上下文是標量上下文,因此@b實際上變爲scalar(@b),這就是分配給@a的內容。