简体   繁体   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();

Or can anybody give me a reasonable explanation for this behavior?或者有人可以对这种行为给出合理的解释吗?

defined(@array) is a bit of a weird construct and Perl is trying to get rid of it. defined(@array)是一个有点奇怪的结构,Perl 正试图摆脱它。 From perldoc -f defined :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.它用于报告该聚合的内存是否曾经被分配过。

That's not testing if it currently has any elements!这不是测试它当前是否有任何元素! To check for size, simply use the array variable in scalar context (such as a conditional):要检查大小,只需在标量上下文(例如条件)中使用数组变量:

if( @array ) { ... }

And, Dada has already explained what the defined-or operator does , which adds its own twist to it.而且,达达已经解释了定义或运算符的作用,这增加了它自己的扭曲。

// must evaluate its left-hand side operator in scalar context since it requires a scalar to evaluate for defined-ness. //必须在标量上下文中评估其左侧运算符,因为它需要标量来评估定义性。

@a returns the number of elements the array contains in scalar context. @a返回数组在标量上下文中包含的元素数。

So @a // ... always returns the number of elements in @a (since all numbers are defined values).所以@a // ...总是返回@a中元素的数量(因为所有数字都是定义的值)。


The concepts of defined-ness or truth-ness aren't defined for a sequence of scalars ("list") as a whole.定义性或真实性的概念不是为整个标量序列(“列表”)定义的。 They only apply to individual scalars.它们仅适用于单个标量。 As such, // , && , and , ||因此, // , && , and , || , or , ! , or , ! , not and ?: require a scalar from their left-most operand, so they evaluate it in scalar context. , not?:需要来自其最左侧操作数的标量,因此它们在标量上下文中对其进行评估。 xor needs to test the truth of both of its operands, and thus evaluates both in scalar context. 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

You might be under the incorrect impression that @a always returns a list of its elements, and that this gets coerced into a count when evaluated in scalar context.您可能会有这样的错误印象,即@a总是返回其元素的列表,并且在标量上下文中进行评估时,这会被强制转换为计数。 But that's not the case.但事实并非如此。

There's simply no such thing as a list.根本就没有列表这样的东西。 When we say "evaluates to a list" or "returns a list", we simply mean "adds zero or more scalar to the stack".当我们说“对列表求值”或“返回列表”时,我们只是指“向堆栈添加零个或多个标量”。 There's absolutely no difference between return a scalar and returning "a list of one scalar".返回标量和返回“一个标量的列表”之间绝对没有区别。 Both refer to adding a scalar to the stack.两者都是指向堆栈添加标量。 Since only scalars are added to the stack, there's nothing that can be cor由于只有标量被添加到堆栈中,所以没有什么可以证明的

Since there's no list data structure being returned, there's nothing that can be magically coerced to a scalar in scalar context;由于没有返回列表数据结构,因此在标量上下文中没有任何东西可以神奇地强制转换为标量; its up to every operator to return a single scalar when evaluated in scalar context.当在标量上下文中计算时,它取决于每个运算符返回单个标量。 This permits each operator to choose what they want to return in scalar context.这允许每个运算符选择他们想要在标量上下文中返回的内容。 It varies greatly .它变化很大

In short, @a returns the number of elements in scalar context, not the elements it contains like it does in list context.简而言之, @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);

Finally, let's analyze @row // ... .最后,让我们分析@row // ...

We've already established that @row is evaluated in scalar context in the above, and that it returns the number of elements in the array in array when it does.我们已经确定@row在上面的标量上下文中被评估,并且当它执行时它返回数组中数组中的元素数。

Well, numerical values are necessarily not undef , so they are all defined.好吧,数值不一定是undef ,所以它们都是定义的。 So that means that the right-hand size of @row // ... will never get evaluated.所以这意味着@row // ...的右侧大小永远不会被评估。 You might as well have written scalar(@row) .你也可以写scalar(@row)

The behavior you are observing is the intended one.您正在观察的行为是预期的行为。 This is documented in perlop , in the section Logical Defined-Or :这在perlop中的Logical Defined-Or部分有记录:

EXPR1 // EXPR2 returns the value of EXPR1 if it's defined, otherwise, the value of EXPR2 is returned. EXPR1 // EXPR2如果定义了EXPR2则返回EXPR1的值,否则返回EXPR2的值。 ( EXPR1 is evaluated in scalar context, EXPR2 in the context of // itself). EXPR1在标量上下文中计算, EXPR2在 // 本身的上下文中计算)。

And, perldoc later provides the following example:并且,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.

// and || //|| first impose scalar context on their left side argument.首先在他们的左侧参数上强加标量上下文。 // checks whether that argumend is defined. //检查是否定义了该参数。 || checks whether that argument is "not false".检查该参数是否“不假”。 false in Perl is any of the values undef , 0, "0", or "". Perl 中的false是任何值undef 、0、“0”或“”。

The scalar value of an array is always defined so you can't use that in a check the way you did.数组的标量值始终是定义的,因此您不能像以前那样在检查中使用它。 @row // (10,20,30) is the same as defined(scalar(@row)) or (10,20,30) . @row // (10,20,30)defined(scalar(@row)) or (10,20,30)

For a longer discussion of context, see the documentation for the Want module.有关上下文的详细讨论,请参阅Want模块的文档。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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