我应该在Perl中使用哪种“for”循环?

10

使用哪种 for 循环来计数整数的参数是什么?

for my $i (0..$n) { ... }

for (my $i = 0; $i <= $n; $i++) { ... }

1
请注意,这两个示例是不同的 - 第一个包括上限$n$,第二个则不包括。 - Paul Beckingham
2
这正是为什么我会选择第一个版本的原因 :) - Dave Cross
5个回答

11

在你的情况下,没有任何区别。

在其他用例中,在 for-each 循环中,您无法控制或访问迭代器。

使用你的示例语法:

for my $i (0..10)
{
   print "$i";
   $i++;
}

上述实际上创建了一个foreach循环 - 它等同于说foreach my $i (0..10)$i是来自列表的,而不是迭代器。 迭代器是内部的,您无法访问它; 您无法控制循环的流程。

上面的输出将打印012345678910

这个:

for ( my $i = 0; $i++ ; $i <= 10)
{
    print $i;
    $i++;
}

这是一个真正的 for 循环。您可以控制并输出迭代器。它将输出:0246810

另外:

当您使用 for (1..$n) 时,您正在调用范围运算符,而不是在循环顶部执行简单的比较。然而,性能差异是微不足道的。


2
@Brian - 我相信 forforeach 是同义词。它们之间从来没有任何区别(除了可读性)。 - Ted Hopp
1
在Perl中,“for”和“foreach”标记是完全可以互换的。它们之间没有任何不同的含义。决定它创建FOROP还是WHILEOP完全取决于跟随任一关键字的语法结构。请参阅“perly.y”以了解原因。 - tchrist
1
@Brian - 根据文档所述:foreach关键字实际上是for关键字的同义词,因此您可以使用foreach来提高可读性或使用for来缩短代码。 - Ted Hopp
2
@Eric - foreach (my $i = 0; $i < 5; ++$i) { print "$i\n"; } 运行得非常好。 - Ted Hopp
1
抱歉,但显然您在三部分for循环中混淆了条件和增量的位置。 - Wolf
显示剩余8条评论

6
两种Perl中的for循环样式,其区别在于清晰度和效率。
当您查看“for my $i(0..$n){...}”时,您可以立即看到使用的范围,而无需在脑海中解析更大的表达式。
对于“for(my $i = 0; $i <= $n; $i ++){...}”,需要查看的内容要多得多,错误也会更容易出现。
此外,对于一个范围内的foreach循环比等效的C风格循环更快,如下面的基准测试所示:
use Benchmark 'cmpthese';

for my $mag (map 10**$_, 1 .. 6) {
    print "\n$mag:\n";
    cmpthese -2 => {
        loop => sub {my $x = 0; for (my $i = 0; $i <= $mag; $i++) {$x += $i}},
        each => sub {my $x = 0; for my $i (0 .. $mag) {$x += $i}},
    };
}

输出结果如下:

10:
         每次循环  每个元素
循环 613877/s   --  -2%
每个元素 625568/s   2%   --
100: 每次循环 每个元素 循环 79481/s -- -24% 每个元素 104758/s 32% --
1000: 每次循环 每个元素 循环 8140/s -- -27% 每个元素 11220/s 38% --
10000: 每次循环 每个元素 循环 832/s -- -26% 每个元素 1124/s 35% --
100000: 每次循环 每个元素 循环 81.6/s -- -26% 每个元素 110/s 34% --
1000000: 每次循环 每个元素 循环 6.90/s -- -26% 每个元素 9.27/s 34% --

今天学习Benchmark - perldoc.perl.org很不错。 - Wolf

5

使用三部分的for循环,你可以在循环内部执行$i += 3等操作,但是使用foreach循环则不行。这就是它们之间的区别。


4

你最初在C风格循环中遇到的那个“差一”错误表明,C风格循环更容易出错,只有在实际需要访问循环计数器时才应使用。使用foreach循环会使得出现“差一”错误变得更加困难。


3
对于 $i 在数组 $array 的所有索引范围内进行循环:for $i ( 0 .. $#array ) { ... } - tchrist
是的,但在这种情况下,您是通过选择迭代索引,而不是因为语法强制要求您这样做,而且更容易考虑要迭代的索引范围,而不是正确获取终止条件。 - Ryan C. Thompson
1
Perl 5.14 带来了一种更简单的遍历数组的方法:use 5.14.0; while (my ($i, $elem) = each @a) { ... } - Joel Berger

2

我认为您发布的两个循环示例行为相同。在这两种情况下,$i 的作用域是词法作用域(因为您使用了 my 关键字)。我认为 0..$n 语法更清晰,但(惊喜!)并不是每个人都会同意。


1
@tchrist - 就我所看到的,这并不是基于原始查询的。 - Ted Hopp
@tchrist - $#array@array 的最后一个有效索引,而不是数组的长度。要获取 @array 的最后一个元素,必须使用 0 .. @array0 .. $#array + 1 - Ted Hopp
".. "是包含-包含的,所以使用0 .. $#array可以正确获得所有有效的索引。顺便说一下,我认为这种范围操作符有点问题。请参见EWD831 - Svante
@Svante - 哦。我忽略了关于范围运算符的那个小细节。 - Ted Hopp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接