为什么山羊运营商有效?

Nat*_*enn 10 syntax perl variable-assignment goatse

数组和列表之间以及列表和标量上下文之间的区别已经在去年的Perl社区中进行了相当多的讨论(并且每年都是如此).我已阅读过chromaticfriedo的文章,以及这个推荐的修士节点.现在我想了解goatse运营商,中记录perlsecret.

这是我用来研究它的一些代码:

# right side gets scalar context, so commas return rightmost item
$string = qw(stuff junk things); 
say $string; # things

# right side gets list context, so middle is list assigned in scalar context
$string = () = qw(stuff junk things);
say $string; # 3

# right side gets list context, so creates a list, assigns an item to $string2, and
# evaluates list in scalar context to assign to $string
$string = ($string2) = qw(stuff junk things);
say $string; # 3
say $string2; # stuff
Run Code Online (Sandbox Code Playgroud)

我想我已经足够了解所有列表和标量上下文工作.标量上下文中的逗号运算符返回其右侧,因此第一个示例只是将逗号表达式中的最后一项(不带任何逗号)分配给$string.在其他示例中,将逗号表达式分配给列表将其放在列表上下文中,因此创建列表,并在标量上下文中评估的列表返回其大小.

有两部分我不明白.

首先,列表应该是不可变的.这是由friedo反复强调的.我想通过=列表到列表的分配将一个列表中的项目的分配分配给另一个列表中的项目,这就是为什么在第二个示例中$string2获取'stuff',以及为什么我们可以@_通过列表分配解压缩.但是,我不明白如何分配()一个空列表可能会起作用.根据我目前的理解,由于列表是不可变的,列表的大小将保持为0,然后$stuff在示例2和3中分配大小将使其值为0.列表实际上是不可变的吗?

其次,我已多次读过列表在标量上下文中实际不存在的情况.但是对goatse运算符的解释是它是标量上下文中的列表赋值.这不是列表在标量上下文中不存在的语句的反例吗?或者还有其他事情发生在这里?

更新:在理解答案后,我认为额外的一对括号有助于概念化它的工作原理:

$string = ( () = qw(stuff junk things) );
Run Code Online (Sandbox Code Playgroud)

在parens中,它=是对'aggregate'的赋值,列表赋值运算符也是如此(它与标量赋值运算符不同,不应与"list context"混淆;列表和标量赋值可能发生在列表或标量上下文).()不会以任何方式改变.=在Perl中有一个返回值,列表赋值的结果$string通过左侧分配=.赋值$string给RHS的标量上下文(parens中的所有内容),在标量上下文中,列表赋值运算符的返回值是RHS中的项目数.

您可以将RHS列表分配放入列表上下文中:

($string) = ( () = qw(stuff junk things) );
Run Code Online (Sandbox Code Playgroud)

根据列表上下文中的perlop列表分配,返回已分配值的列表,这里为空,因为没有任何内容可以分配().所以这里$ string会是undef.

yst*_*sth 10

你误会了.在标量上下文中评估的列表不会得到它们的大小.实际上,在标量上下文中列出一个列表几乎是不可能的.在这里,你有一个带有两个操作数的标量赋值,左边是一个标量变量,右边是一个列表赋值(由标量赋值给定标量上下文).标量上下文中的列表分配评估为分配右侧的项目数.

所以,在:

1 $foo
2 =
3 ()
4 =
5 ('bar')
Run Code Online (Sandbox Code Playgroud)

2,标量赋值,给出1和4个标量上下文.4,列表赋值,给出3和5列表上下文,但是它本身在标量上下文中并且适当地返回.

(当=是列表赋值或标量赋值完全由周围语法确定;如果左操作数是散列,数组,散列切片,数组切片或括号中,则它是列表赋值,否则它是标量分配.)

标量上下文中对列表赋值的这种处理使得可能的代码如下:

while ( my ($key, $value) = each %hash ) {
Run Code Online (Sandbox Code Playgroud)

其中list-context每个都是一个迭代器,它返回(在列表上下文中)每个调用的一个键和一个值,并在完成时返回一个空列表,给出while 0并终止循环.


ike*_*ami 6

首先,"列表"是一个含糊不清的术语.甚至问题也用它来指代两个不同的东西.我怀疑你可能在没有意识到这一点的情况下这样做,并且这是你混淆的重要原因.

我将使用"列表值"来表示运算符在列表上下文中返回的内容.相反,"列表运算符"是指运算符EXPR,EXPR,EXPR,...也称为"逗号运算符" [1].

其次,您应该阅读Scalar vs List Assignment Operator.


我想通过=从列表到列表的赋值将一个列表中的项目的赋值分配给另一个列表中的项目,这就是为什么在第二个示例中$ string2获取'stuff',以及为什么我们可以通过列表赋值解包@_.

正确.

我已多次读过列表在标量上下文中实际不存在的情况.

这个措辞非常含糊不清.您似乎在谈论列表值(在内存中找到),但标量上下文仅存在于代码中(找到运算符的位置).

  • 可以在标量上下文中评估列表/逗号运算符.
  • 无法在标量上下文中返回列表值.

标量上下文是可以评估运算符的上下文.

在标量上下文中计算的运算符无法返回列表.它必须返回一个标量.简而言之,您可以说在标量上下文中无法返回列表.

另一方面,可以在标量上下文中评估列表/逗号运算符.例如scalar(4,5,6).可以在任何上下文中评估每个运算符(尽管这样做并不一定有用).

但是对goatse运算符的解释是它是标量上下文中的列表赋值.

包括一个,是的.

列表值和列表赋值运算符是两个不同的东西.一个人的价值.另一个是一段代码.

与标识赋值运算符一样,可以在标量上下文中对其进行计算.标量上下文中的列表赋值运算符返回其RHS返回的标量数.

因此,如果您() = qw(a b c)在标量上下文中进行评估,它将返回三个,因为qw()在堆栈上放置了三个标量.

但是,我不明白如何将()(空列表)的赋值分配给它.

就像赋值($x,$y) = qw(stuff junk things)忽略RHS返回的第三个元素一样,() = qw(stuff junk things)忽略RHS 返回的所有元素.

根据我目前的理解,由于列表是不可变的,列表的大小将保持为0

说"列表的大小将保持为零" ()=qw(a b c)就像是说"标量的值将保持为4" 4+5.

对于初学者来说,问题是你在谈论哪个列表.LHS返回一个,RHS返回一个,赋值运算符可以返回一个.

LHS返回的列表值的长度为0.

RHS返回的列表值为3.

在标量上下文中,列表赋值运算符返回RHS(3)返回的标量数.

在列表上下文中,列表赋值运算符将LHS返回的标量返回为左值(空列表).

列表应该是不可变的.

如果您正在考虑列表可变性,那么您在某处出现了错误的转向.[2]


笔记:

  1. 文档调用EXPR,EXPR,EXPR,...二元运算符的两个实例,但它更容易理解为单个N元运算符,它实际上是作为单个N元运算符实现的.即使在标量语境中.

  2. 事实并非如此,但我们不要再往下走了.


hob*_*bbs 6

它有助于记住在Perl中,赋值是一个表达式,您应该考虑表达式的值(赋值运算符的值),而不是"列表的值".

的表达式的值qw(a b)('a', 'b')在列表环境和'b'在标量上下文,但表达的值(() = qw(a b))()在列表上下文和2在标量上下文.值(@a = qw(a b))遵循相同的模式.这是因为pp_aassign列表赋值运算符选择在标量上下文中返回一个计数:

else if (gimme == G_SCALAR) {
    dTARGET;
    SP = firstrelem;
    SETi(lastrelem - firstrelem + 1);
}
Run Code Online (Sandbox Code Playgroud)

(pp_hot.c第1257行;行号可能会发生变化,但它已接近尾声PP(pp_aassign).)

然后,除赋值运算符的之外还有赋值运算符的副作用.列表赋值的副作用是将值从右侧复制到左侧.如果右侧首先用完了值,则左侧的剩余元素得到undef; 如果左侧首先用完了值,则不会复制右侧的其余元素.当给出LHS时(),列表赋值不会复制任何地方.但是赋值本身的值仍然是RHS中元素的数量,如代码片段所示.