[英]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.