Perl“反向逗号运算符”(例如,编程Perl,第4版)

时间:2016-04-25 19:41:28

标签: perl operators comma scalar-context

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

示例:

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

本书然后在几页之后(第82页)给出了“反向逗号运算符”的示例

# A "reverse comma operator".
return (pop(@foo), pop(@foo))[0];

然而对我而言,这似乎并不反过来。?

示例:

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

# Implementation of the "reverse comma operator"
sub reverse_comma {
    return (pop(@_), pop(@_))[0];
}

这与普通逗号运算符的反转有何不同?结果是一样的,没有逆转!

以下是确切页面的link。该示例位于底部附近。

4 个答案:

答案 0 :(得分:8)

这是一个不好的例子,应该被遗忘。

它的演示很简单:

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

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

    my $x = (1, 2, 3)[0];
    

    由于某种原因,本书将其称为"反向逗号运算符"。这用词不当;它只是一个列出第一个元素的列表。

通过在参数中使用pop函数两次,本书混淆了进一步。这些是从左到右评估的,因此第一个pop评估为"three",第二个评估为"two"

在任何情况下:不要使用逗号或"反向逗号"实际代码中的运算符。两者都可能让未来的读者感到困惑。

答案 1 :(得分:5)

这是一个可爱而聪明的例子,但是过于考虑它会分散其目的。本书的这一部分展示了列表切片。除了列表中的内容之外,切换列表之外的任何内容都与示例的目的无关。

你只是在一本非常大的书的第82页上(因为我们处于绑定方法的极限,我们实际上不能再适合任何页面),所以不会太多我们可以扔你。在其他列表切片示例中,有一个我不会在实际代码中使用的聪明的例子。这虽然是人为举例的诅咒。

但是,让我们说有一个反向逗号运算符。它必须评估逗号的两侧。许多答案都是正确的,只需返回第一件事"。但这并不是这个功能。即使你保留其中一个,你也必须访问每个表达。

考虑这个非常高级的版本,带有一系列我立即取消引用的匿名子程序,每个子程序打印一些然后返回结果:

use v5.10;

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

parens只存在优先级,因为赋值运算符比逗号运算符绑定得更紧密。这里没有列表,即使看起来有一个。

输出显示Perl评估每个,即使它保留在最后一个:

First
wantarray is 0
Second
Third
Scalar is [137]

名称不佳的wantarray内置返回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 to csh转换器基本上是他的收件箱(或comp.lang.perl)。你会在他的例子中看到一些类似于成语的shell。类似于使用一个语句交换两个数值的技巧:

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,我们有一个示例,其中有副作用的东西是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";

这表明评估了所有逗号两边的每个表达式。我投入了子程序包装器,因为我们没有显示完整的例子:

list: g h j k
list: g h

但是,这一点都不是该部分的重点,它正在索引到列表文字中。该示例的要点不是返回与其他示例或Perl功能不同的结果。假设逗号运算符采取不同的行为,它将返回相同的内容。该部分是关于列表切片并显示列表切片,因此列表中的内容不是重要部分。

答案 2 :(得分:2)

让我们的例子更相似。

逗号运算符:

$scalar = ("one", "two", "three");
# $scalar now contains "three"

反向逗号运算符:

$scalar = ("one", "two", "three")[0];
# $scalar now contains "one"

它是一个反向逗号"因为$scalar得到第一个表达式的结果,而普通的逗号运算符给出了最后一个表达式。 (如果你对Lisp有所了解,那就像prognprog1之间的区别一样。)

"反向逗号运算符"的实现作为一个子程序看起来像这样:

sub reverse_comma {
    return shift @_;
}

答案 3 :(得分:0)

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

my $v = $a, $b

$v设置为$b

的值

为了演示列表切片,Camel提出了一些行为类似于逗号运算符的代码,而是计算其操作数,然后返回左手操作数的值

这样的事情可以使用列表切片来完成,比如

my $v = ($a, $b)[0]

$v设置为$a

的值

真的有这一切。本书并没有试图建议应该有一个反向逗号子程序,它只是考虑按顺序评估两个表达式并返回第一个表达式的问题。仅当两个表达式具有副作用时,评估顺序才相关,这就是为什么本书中的示例使用pop,它会更改数组以及返回值

想象中的问题是这个

  

假设我想要一个子程序来删除数组的最后两个元素,然后返回以前是最后一个元素的值

通常需要一个临时变量,比如

my $last = pop @foo;
pop @foo;
return $last;

作为列表切片的一个例子代码表明这也可行

# A "reverse comma operator".
return (pop(@foo), pop(@foo))[0];

请理解此不是推荐。有几种方法可以做到这一点。另一种单一陈述方式是

return scalar splice @foo, -2;

但是并没有使用列表切片,这是本书那一部分的主题。实际上,我怀疑这本书的作者是否会提出除了临时变量的简单解决方案以外的任何内容。它纯粹是列表切片可以做什么的一个例子

我希望有帮助