[英]Perl defined-or in a list context, why a scalar?
use strict;
use warnings;
use Test::More;
subtest 'explicit array' => sub {
my @row = (1,2,3);
# let's disassamble the array.
# without default it works:
my ($one, $two, $three) = @row;
is($one, 1, 'one');
# this works too:
($one, $two, $three) = @row ? @row : (10,20,30);
is($one, 1, 'one');
# and the default hits
my @emptyness;
($one, $two, $three) = @emptyness ? @emptyness : (10,20,30);
is($one, 10, 'one default');
# however, this squashes the array to a scalar
($one, $two, $three) = @row // (10,20,30);
is($one, 3, 'scalar, length');
is($two, undef, 'nothing else');
# shouldn't 'defined-or' be equivalent to a ternary with a check against undef?
# ($one, $two, $three) = defined @emptyness ? @emptyness : (10,20,30); # fails!
# "Can't use 'defined(@array)' (Maybe you should just omit the defined()?)"
# Probably @array // ... should fail in the same way, but instead it returns @array
# in a scalar context.
# so maybe this is a bug
};
done_testing();
或者有人可以對這種行為給出合理的解釋嗎?
defined(@array)
是一個有點奇怪的結構,Perl 正試圖擺脫它。 從perldoc -f defined
:
不再支持在聚合(散列和數組)上使用“定義”。 它用於報告該聚合的內存是否曾經被分配過。
這不是測試它當前是否有任何元素! 要檢查大小,只需在標量上下文(例如條件)中使用數組變量:
if( @array ) { ... }
而且,達達已經解釋了定義或運算符的作用,這增加了它自己的扭曲。
//
必須在標量上下文中評估其左側運算符,因為它需要標量來評估定義性。
@a
返回數組在標量上下文中包含的元素數。
所以@a // ...
總是返回@a
中元素的數量(因為所有數字都是定義的值)。
定義性或真實性的概念不是為整個標量序列(“列表”)定義的。 它們僅適用於單個標量。 因此, //
, &&
, and
, ||
, or
, !
, not
和?:
需要來自其最左側操作數的標量,因此它們在標量上下文中對其進行評估。 xor
需要測試其兩個操作數的真值,因此在標量上下文中評估兩者。
$ perl -M5.010 -e'
sub cx { print wantarray ? " list " : " scalar"; $_[0] }
print "// "; @a = cx($u) // cx(); say "";
print "&& "; @a = cx(0) && cx(); say "";
print "and"; @a = ( cx(1) and cx() ); say "";
print "|| "; @a = cx(0) || cx(); say "";
print "or "; @a = ( cx(0) or cx() ); say "";
print "xor"; @a = ( cx(0) xor cx(0) ); say "";
print "! "; @a = ! cx(); say "";
print "not"; @a = not cx(); say "";
print "?: "; @a = cx() ? 0 : 0;
@a = 1 ? cx() : 0;
@a = 0 ? 0 : cx(); say "";
'
// scalar list
&& scalar
and scalar list
|| scalar list
or scalar list
xor scalar scalar
! scalar
not scalar
?: scalar list list
您可能會有這樣的錯誤印象,即@a
總是返回其元素的列表,並且在標量上下文中進行評估時,這會被強制轉換為計數。 但事實並非如此。
根本就沒有列表這樣的東西。 當我們說“對列表求值”或“返回列表”時,我們只是指“向堆棧添加零個或多個標量”。 返回標量和返回“一個標量的列表”之間絕對沒有區別。 兩者都是指向堆棧添加標量。 由於只有標量被添加到堆棧中,所以沒有什么可以證明的
由於沒有返回列表數據結構,因此在標量上下文中沒有任何東西可以神奇地強制轉換為標量; 當在標量上下文中計算時,它取決於每個運算符返回單個標量。 這允許每個運算符選擇他們想要在標量上下文中返回的內容。 它變化很大。
簡而言之, @a
返回標量上下文中元素的數量,而不是它在列表上下文中包含的元素。
+-------------------------- (Scalar context) Returns 3
| +------------- (List context) Returns three scalars
| |
vvvv vvvvvvvv
() = @row // (10,20,30);
+-------------------------- (Scalar context) Returns 3
| +------------------- (List context) Returns three scalars
| | +------- (List context) Returns three scalars
| | |
vvvv vvvv vvvvvvvv
() = @row ? @row : (10,20,30);
最后,讓我們分析@row // ...
。
我們已經確定@row
在上面的標量上下文中被評估,並且當它執行時它返回數組中數組中的元素數。
好吧,數值不一定是undef
,所以它們都是定義的。 所以這意味着@row // ...
的右側大小永遠不會被評估。 你也可以寫scalar(@row)
。
您正在觀察的行為是預期的行為。 這在perlop中的Logical Defined-Or部分有記錄:
EXPR1 // EXPR2
如果定義了EXPR2
則返回EXPR1
的值,否則返回EXPR2
的值。 (EXPR1
在標量上下文中計算,EXPR2
在 // 本身的上下文中計算)。
並且,perldoc 稍后提供了以下示例:
特別是,這意味着您不應該使用它在兩個聚合之間進行選擇以進行分配:
@a = @b || @c; # This doesn't do the right thing @a = scalar(@b) || @c; # because it really means this. @a = @b ? @b : @c; # This works fine, though.
//
和||
首先在他們的左側參數上強加標量上下文。 //
檢查是否定義了該參數。 ||
檢查該參數是否“不假”。 Perl 中的false
是任何值undef
、0、“0”或“”。
數組的標量值始終是定義的,因此您不能像以前那樣在檢查中使用它。 @row // (10,20,30)
與defined(scalar(@row)) or (10,20,30)
。
有關上下文的詳細討論,請參閱Want模塊的文檔。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.