结合了 `foreach` 和 `if` 的 Perl 语法:它不应该起作用,即:为什么它不起作用?

Perl Syntax combining `foreach` and `if`: Shouldn't it work, i.e.: Why doesn't it work?

尝试获取不能包含负数的数组中的最大值我试过:

my @v;
#...
my $max = 0;
$max = $_
    if ($_ > $max)
        foreach (@v);

我收到 perl 5.18.2 的语法错误。

但是(1)statement($_) foreach (@v);和(2)$max = $_ if ($_ > $max);都还可以,他们做了他们应该做的。

那么如果 (1) 和 (2) 都是有效的陈述,为什么不能将它们组合在用于 (1) 的模式中?

只是没有人需要建议;这是我使用不同语法解决问题的方法:

foreach (@v) {
    $max = $_
        if ($_ > $max);
}

So if (1) and (2) are both valid statements, why can't they be combined in the pattern used for (1)?

您在这里使用的后缀语法(又名语句修饰符)不支持这种语句。

documentation says(强调我的):

Any simple statement may optionally be followed by a SINGLE modifier, just before the terminating semicolon (or block ending).

至于您的任务本身:从数组中获取最大值是一个非常常见的要求。一种简单且标准的方法使用 function max() from core module List::Util:

use List::Util qw/max/;
my @v;
my $max = max 0, @v;

我将添加这个更通用的答案,因为想要组合 forif 语句修饰符是很常见的,不仅适用于可以使用(更快的)CPAN 模块的微不足道的情况. 您可能不想执行使用块的标准 foreach 的原因是,块会花费时间,如果循环内部发生的事情很慢,这可能无关紧要,但如果您正在做某事,则可能会超过 10% 的开销就像一个简单的任务。例如这个简单的例子:

my @v = map { rand } ( 1..100 );
cmpthese(-2, {
    postfix => sub {
        my $last;
        $last = $_ for @v;
    },
    block => sub {
        my $last;
        foreach (@v) {$last = $_}
    },
});

给我:

            Rate   block postfix
block   214369/s      --    -11%
postfix 241751/s     13%      --

所以...虽然您不能使用两个语句修饰符(后缀 iffor),但大多数时候您可以将 for 替换为 map,保持无块循环的速度。例如 OP 的例子是 map {$max = $_ if $_ > $max} @v 和基准测试:

my @v = map { rand } ( 1..100 );
cmpthese(-2, {
    map => sub {
        my $max = 0;
        map {$max = $_ if $_ > $max} @v;
    },
    foreach => sub {
        my $max = 0;
        foreach (@v) {$max = $_ if $_ > $max}
    },
});

给出:

            Rate foreach     map
foreach 205585/s      --    -13%
map     236303/s     15%      --

正如我所说,在特定情况下,您绝对应该使用用 C 编写的 List::Util,速度会快一个数量级...