繁体   English   中英

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

或者有人可以对这种行为给出合理的解释吗?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM