Perl";反向逗号运算符“;(示例摘自《Perl编程》,第4版)

Perl";反向逗号运算符“;(示例摘自《Perl编程》,第4版),perl,operators,comma,scalar-context,Perl,Operators,Comma,Scalar Context,我在读“”时,遇到了一个奇怪的例子,似乎没有什么意义。本书描述了Perl中的逗号运算符在标量上下文中使用时如何仅返回最后一个结果 示例: # After this statement, $stuff = "three" $stuff = ("one", "two", "three"); # After this statement, $stuff = "three" $stuff = reverse_comma("one", "two", "three"); # Implementation

我在读“”时,遇到了一个奇怪的例子,似乎没有什么意义。本书描述了Perl中的逗号运算符在标量上下文中使用时如何仅返回最后一个结果

示例:

# After this statement, $stuff = "three"
$stuff = ("one", "two", "three");
# After this statement, $stuff = "three"
$stuff = reverse_comma("one", "two", "three");

# Implementation of the "reverse comma operator"
sub reverse_comma {
    return (pop(@_), pop(@_))[0];
}
几页之后,本书给出了“反向逗号运算符”的示例(第82页)

然而,对我来说,这似乎根本不是相反的

示例:

# After this statement, $stuff = "three"
$stuff = ("one", "two", "three");
# After this statement, $stuff = "three"
$stuff = reverse_comma("one", "two", "three");

# Implementation of the "reverse comma operator"
sub reverse_comma {
    return (pop(@_), pop(@_))[0];
}
这与普通的逗号运算符有何相反之处?结果是一样的,不是相反的


这是一个精确的页面。这个例子接近底部。

这是一个可爱而聪明的例子,但如果想得太多,就会偏离它的目的。这本书的那一部分正在展示一系列的片段。除了切片列表之外的任何内容,无论列表中有什么内容,都与示例的目的无关


你只在一本非常大的书的第82页上(我们实际上无法容纳更多的页面,因为我们已经达到了绑定方法的极限),所以我们无法向你扔太多东西。在其他列表切片示例中,有一个我不会在实际代码中使用的聪明示例。这是人为的例子的诅咒

但假设有一个反逗号运算符。它必须计算逗号的两边。许多答案都适用于“第一件事就是归还”。但这不是功能。你必须访问每一个表情,即使你保留了其中的一个

考虑一下这个非常高级的版本,它包含一系列我立即取消引用的匿名子例程,每个子例程打印一些内容,然后返回一个结果:

use v5.10;

my $scalar = (
    sub { say "First"; 35 } -> (),
    sub { say "wantarray is ", 0+wantarray } -> (),
    sub { say "Second"; 27 } -> (),
    sub { say "Third"; 137 } -> ()
    );
paren仅用于优先级,因为赋值运算符比逗号运算符绑定得更紧密。这里没有列表,尽管看起来有一个

输出显示Perl对每一个都进行了求值,即使它保留了最后一个:

First
wantarray is 0
Second
Third
Scalar is [137]
命名不好的内置函数返回false,注意子例程认为它在标量上下文中

现在,假设您想要翻转它,使它仍然计算每个表达式,但保留第一个表达式。您可以使用文字列表访问:

my $scalar = (
    sub { say "First"; 35 } -> (),
    sub { say "wantarray is ", 0+wantarray } -> (),
    sub { say "Second"; 27 } -> (),
    sub { say "Third"; 137 } -> ()
    )[0];
随着订阅的增加,右侧现在是一个列表,我拉出第一项。注意,第二个子例程认为它现在在列表上下文中。我得到第一个子例程的结果:

First
wantarray is 1
Second
Third
Scalar is [35]
但是,让我们把它放到一个子程序中。我仍然需要调用每个子例程,即使我不会使用其他子例程的结果:

my $scalar = reverse_comma(
    sub { say "First"; 35 },
    sub { say "wantarray is ", 0+wantarray },
    sub { say "Second"; 27 },
    sub { say "Third"; 137 }
    );

say "Scalar is [$scalar]";

sub reverse_comma { ( map { $_->() } @_ )[0] }
或者,我会使用其他结果吗?如果我做了一些稍微不同的事情呢。我将在计算表达式中添加设置
$last
的副作用:

use v5.10;

my $last;

my $scalar = reverse_comma(
    sub { say "First"; 35 },
    sub { say "wantarray is ", 0+wantarray },
    sub { say "Second"; 27 },
    sub { say "Third"; 137 }
    );

say "Scalar is [$scalar]";
say "Last is [$last]";

sub reverse_comma { ( map { $last = $_->() } @_ )[0] }
现在我看到了使标量逗号变得有趣的特性。它计算所有表达式,其中一些表达式可能有副作用:

First
wantarray is 0
Second
Third
Scalar is [35]
Last is [137]
tchrist使用shell脚本很方便,这并不是什么大秘密。Perl到csh转换器基本上就是他的收件箱(或comp.lang.Perl)。你会在他的例子中看到一些外壳式的习惯用法。类似于用一条语句交换两个数值的技巧:

use v5.10;

my $x = 17;
my $y = 137;

say "x => $x, y => $y";

$x = (
    $x = $x + $y,
    $y = $x - $y,
    $x - $y,
    );

say "x => $x, y => $y";
副作用很重要

那么,回到Camel,我们有一个例子,其中有副作用的是数组操作符:

use v5.10;

my @foo = qw( g h j k );

say "list: @foo";

my $scalar = sub { return ( pop(@foo), pop(@foo) )[0] }->();

say "list: @foo";
$scalar = ("one", "two", "three");
# $scalar now contains "three"
$scalar = ("one", "two", "three")[0];
# $scalar now contains "one"
这表明对所有逗号任一侧的每个表达式都进行了求值。我加入了子程序包装器,因为我们没有显示完整的示例:

list: g h j k
list: g h

但是,这些都不是该节的重点,该节将索引转换为列表文字。该示例的要点是不返回与其他示例或Perl特性不同的结果。假设逗号运算符的操作不同,则返回相同的结果。本节是关于列表片段的,并展示列表片段,因此列表中的内容不是重要部分。

这是一个糟糕的示例,应该忘记

它演示的内容很简单:

  • 通常,如果在标量上下文中有一个由逗号分隔的表达式序列,则可以将其解释为逗号运算符的实例,其计算结果为序列中的最后一项

  • 但是,如果您将该序列放在括号中,并在末尾粘贴
    [0]
    ,它会将该序列转换为一个列表,并获取其第一个元素,例如

    my $x = (1, 2, 3)[0];
    
    出于某种原因,这本书称之为“反向逗号运算符”。这是用词不当;这只是一个列表,它的第一个元素已被获取

这本书在参数中使用了两次
pop
函数,进一步混淆了问题。这些值是从左到右计算的,因此第一个
pop
计算为
“三个”
,第二个计算为
“两个”


在任何情况下:不要在实际代码中使用逗号或“反向逗号”运算符。这两个例子很可能会让未来的读者感到困惑。

让我们把例子做得更相似一些

逗号运算符:

use v5.10;

my @foo = qw( g h j k );

say "list: @foo";

my $scalar = sub { return ( pop(@foo), pop(@foo) )[0] }->();

say "list: @foo";
$scalar = ("one", "two", "three");
# $scalar now contains "three"
$scalar = ("one", "two", "three")[0];
# $scalar now contains "one"
反向逗号运算符:

use v5.10;

my @foo = qw( g h j k );

say "list: @foo";

my $scalar = sub { return ( pop(@foo), pop(@foo) )[0] }->();

say "list: @foo";
$scalar = ("one", "two", "three");
# $scalar now contains "three"
$scalar = ("one", "two", "three")[0];
# $scalar now contains "one"
这是一个“反向逗号”,因为
$scalar
获取第一个表达式的结果,其中普通逗号运算符给出最后一个表达式。(如果您了解Lisp,这就像
progn
prog1
之间的区别一样)

“反向逗号运算符”作为子例程的实现如下所示:

sub reverse_comma {
    return shift @_;
}

普通逗号将计算其操作数,然后返回右侧操作数的值

my $v = $a, $b
$v
设置为
$b

为了演示列表切片,Camel提出了一些类似于逗号运算符的代码,但会对其操作数求值,然后返回左侧操作数的值

my $v = $a, $b
类似的事情可以通过列表切片来完成,比如

my $v = ($a, $b)[0]
my $last = pop @foo;
pop @foo;
return $last;
$v
设置为
$a

就这些