Perl 定义或在列表上下文中,为什么是标量?
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();
或者有人能给我一个合理的解释吗?
您观察到的行为是预期行为。这记录在 perlop, in the section Logical Defined-Or:
EXPR1 // EXPR2
returns the value of EXPR1
if it's defined, otherwise, the value of EXPR2
is returned. (EXPR1
is evaluated in scalar context, EXPR2
in the context of // itself).
并且,perldoc 稍后提供了以下示例:
In particular, this means that you shouldn't use this for selecting between two aggregates for assignment:
@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.
defined(@array)
有点奇怪,Perl 正试图摆脱它。来自 perldoc -f defined
:
Use of "defined" on aggregates (hashes and arrays) is no longer supported. It used to report whether memory for that aggregate had ever been allocated.
这不是在测试它当前是否有任何元素!要检查大小,只需在标量上下文中使用数组变量(例如条件):
if( @array ) { ... }
而且,达达已经解释过,这给它添加了它自己的曲折。
//
必须在标量上下文中评估其左侧运算符,因为它需要标量来评估定义性。
@a
returns 数组在标量上下文中包含的元素数。
所以 @a // ...
总是 returns @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
总是 return 是其元素的列表,并且当在标量上下文中计算时,这会被强制计入计数。但事实并非如此。
根本就没有列表这样的东西。当我们说“评估一个列表”或“returns 一个列表”时,我们只是指“向堆栈添加零个或多个标量”。 return 一个标量和 returning “一个标量的列表”之间绝对没有区别。两者都是指将标量添加到堆栈。
由于没有 returned 的列表数据结构,因此在标量上下文中没有任何东西可以神奇地强制转换为标量;在标量上下文中评估时,每个运算符都可以 return 单个标量。这允许每个操作员在标量上下文中选择他们想要 return 的内容。 It varies greatly.
简而言之,@a
return 是标量上下文中的元素数,而不是它包含的元素,就像它在列表上下文中那样。
+-------------------------- (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
是在上面的标量上下文中计算的,并且它 return 是数组中元素的数量 in array 。
嗯,数值不一定是undef
,所以都是定义的。所以这意味着 @row // ...
右边的大小永远不会被评估。你还不如写 scalar(@row)
.
//
和 ||
首先在其左侧参数上施加标量上下文。 //
检查是否定义了该参数。 ||
检查该参数是否“不为假”。 false
在 Perl 中是任何值 undef
、0、“0”或“”。
数组的标量值始终是已定义的,因此您不能像以前那样在检查中使用它。 @row // (10,20,30)
与 defined(scalar(@row)) or (10,20,30)
相同。
有关上下文的更详细讨论,请参阅 Want 模块的文档。
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();
或者有人能给我一个合理的解释吗?
您观察到的行为是预期行为。这记录在 perlop, in the section Logical Defined-Or:
EXPR1 // EXPR2
returns the value ofEXPR1
if it's defined, otherwise, the value ofEXPR2
is returned. (EXPR1
is evaluated in scalar context,EXPR2
in the context of // itself).
并且,perldoc 稍后提供了以下示例:
In particular, this means that you shouldn't use this for selecting between two aggregates for assignment:
@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.
defined(@array)
有点奇怪,Perl 正试图摆脱它。来自 perldoc -f defined
:
Use of "defined" on aggregates (hashes and arrays) is no longer supported. It used to report whether memory for that aggregate had ever been allocated.
这不是在测试它当前是否有任何元素!要检查大小,只需在标量上下文中使用数组变量(例如条件):
if( @array ) { ... }
而且,达达已经解释过
//
必须在标量上下文中评估其左侧运算符,因为它需要标量来评估定义性。
@a
returns 数组在标量上下文中包含的元素数。
所以 @a // ...
总是 returns @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
总是 return 是其元素的列表,并且当在标量上下文中计算时,这会被强制计入计数。但事实并非如此。
根本就没有列表这样的东西。当我们说“评估一个列表”或“returns 一个列表”时,我们只是指“向堆栈添加零个或多个标量”。 return 一个标量和 returning “一个标量的列表”之间绝对没有区别。两者都是指将标量添加到堆栈。
由于没有 returned 的列表数据结构,因此在标量上下文中没有任何东西可以神奇地强制转换为标量;在标量上下文中评估时,每个运算符都可以 return 单个标量。这允许每个操作员在标量上下文中选择他们想要 return 的内容。 It varies greatly.
简而言之,@a
return 是标量上下文中的元素数,而不是它包含的元素,就像它在列表上下文中那样。
+-------------------------- (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
是在上面的标量上下文中计算的,并且它 return 是数组中元素的数量 in array 。
嗯,数值不一定是undef
,所以都是定义的。所以这意味着 @row // ...
右边的大小永远不会被评估。你还不如写 scalar(@row)
.
//
和 ||
首先在其左侧参数上施加标量上下文。 //
检查是否定义了该参数。 ||
检查该参数是否“不为假”。 false
在 Perl 中是任何值 undef
、0、“0”或“”。
数组的标量值始终是已定义的,因此您不能像以前那样在检查中使用它。 @row // (10,20,30)
与 defined(scalar(@row)) or (10,20,30)
相同。
有关上下文的更详细讨论,请参阅 Want 模块的文档。