在Perl中自动获取foreach循环中的循环索引


77

如果我在Perl中有以下数组:

@x = qw(a b c);

然后使用进行遍历foreach,然后$_引用数组中的当前元素

foreach (@x) {
    print;
}

将打印:

abc

是否有类似的方法来获取当前元素的索引,而无需手动更新计数器?如:

foreach (@x) {
    print $index;
}

在哪里$index更新$_以产生输出:

012

Answers:


110

就像codehead所说的那样,您必须遍历数组索引而不是其元素。我更喜欢这种变体而不是C风格的for循环:

for my $i (0 .. $#x) {
    print "$i: $x[$i]\n";
}

5
如果数组索引序列为2、5、8、19(带间隔),则这不是您想要的。我认为问题在于有关当前元素的索引。
安迪N

概括带有间隙的数组的唯一方法是next unless defined $x[$i];在循环的开头放置一行。对于涉及while / each和for / key的所有更具风险的解决方案,这似乎也是正确的。显然,数组迭代器会查看每个顺序索引是否存在。
阿诺德·克罗斯

我只是想过一种写该行的更好方法:$x[$i] // next;
Arnold Cross

46

在5.10之前的Perl中,您可以说

#!/usr/bin/perl

use strict;
use warnings;

my @a = qw/a b c d e/;

my $index;
for my $elem (@a) {
    print "At index ", $index++, ", I saw $elem\n";
}

#or

for my $index (0 .. $#a) {
    print "At index $index I saw $a[$elem]\n";
}

在Perl 5.10中,您可以使用state声明一个永远不会重新初始化的变量(与使用my创建的变量不同)。这使您可以将$index变量保持在较小的范围内,但可能会导致错误(如果第二次进入循环,它将仍然具有最后一个值):

#!/usr/bin/perl

use 5.010;
use strict;
use warnings;

my @a = qw/a b c d e/;

for my $elem (@a) {
    state $index;
    say "At index ", $index++, ", I saw $elem";
}

在Perl 5.12中,您可以说

#!/usr/bin/perl

use 5.012; # This enables strict
use warnings;

my @a = qw/a b c d e/;

while (my ($index, $elem) = each @a) {
    say "At index $index I saw $elem";
}

但请注意:在使用进行迭代时,您对操作的限制@aeach

它现在对您无济于事,但是在Perl 6中,您可以说

#!/usr/bin/perl6

my @a = <a b c d e>;
for @a Z 0 .. Inf -> $elem, $index {
    say "at index $index, I saw $elem"
}

所述Z操作者的两个列表拉链一起(即,它需要一个元件从第一清单中,然后从第二一种元素,然后从第一,依此类推一种元素)。第二个列表是一个惰性列表,其中包含从0到无穷大的每个整数(至少在理论上是这样)。该-> $elem, $index说,我们正处在一个时间从zip的结果取两个值。其余的看起来对您来说是正常的(除非您对say5.10版的功能还不熟悉)。


我不喜欢上面的部分state,我认为最好与它搭配使用{ my $index; ... }
布拉德·吉尔伯特

22

perldoc perlvar 似乎没有建议任何此类变量。


6
AndrewKS:这当然违反StackOverflow的精神。如果您尝试过perldoc perlvar,您会知道为什么。仅perldoc perlvar存在列出的变量。正如我已经说过的,这种变量不存在。
Alan Haggai Alavi '04

5
对计算器的精神,因为精神是提供一个答案可以回答它的整体的问题,无需外部资源。提供可选资源(例如链接或终端命令以运行以进行学习)是一种奖励,但不是精神。您必须在评论中提供更多信息这一事实证明,仅靠这个答案是不够的。
SgtPooki 2015年

1
这基本上与仅链接的答案相同。
jinawee

也许扩大您的答案?
彼得·莫滕森

8

不与foreach

如果您确实需要数组中的元素基数,请使用“ for”迭代器:

for ($i=0; $i<@x; ++$i) {
  print "Element at index $i is " , $x[$i] , "\n";
}

5
foreach和for是可互换的同义词。一些介绍性材料试图用于表示C风格的循环和用于列表迭代器循环的foreach,但是术语的使用并不是每个人都熟悉的。
2009年

2
如该实例所示,foreach和for是/ not /可互换的。
马修·弗拉申(Martin Flaschen)2009年

9
引用“ perldoc perlvar”:“ foreach”关键字实际上是“ for”关键字的同义词,因此您可以使用“ foreach”表示可读性,或使用“ for”表示简短性。结束语。因此它们可互换的。尝试一下。
user55400 2009年

1
关键字是可以互换的,但是循环本身具有不同的含义。“ for”循环是一个具有递增(或递减)变量的循环,即使它是与foreach关键字一起引入的。“ foreach”循环具有内部迭代器,即使它与for关键字一起引入也是如此。OP希望访问迭代器,因此无论使用什么关键字,他都希望有一个“ for”循环。
Andrew Barnett

4
C样式的for循环(大家一直在调用“ for循环”)不必增减变量,例如:for(my $ iter = new_iter(); $ iter; $ iter = $ iter -> next()){...}
ysth

8

可以通过while循环来完成(foreach不支持此操作):

my @arr = (1111, 2222, 3333);

while (my ($index, $element) = each(@arr))
{
   # You may need to "use feature 'say';"
   say "Index: $index, Element: $element";
}

输出:

Index: 0, Element: 1111
Index: 1, Element: 2222
Index: 2, Element: 3333

Perl版本:5.14.4


Perldoc说不要使用While-Each ---在开始循环之前很容易地显式重置迭代器很容易,但是没有办法将循环使用的迭代器状态与其他在执行过程中可能执行的迭代器状态隔离开循环体。为避免这些问题,请使用foreach循环而不是while-each。---perldoc.perl.org/functions/each.html
Able Mac

5

不,您必须创建自己的柜台。又一个例子:

my $index;
foreach (@x) {
    print $index++;
}

用于索引时

my $index;
foreach (@x) {
    print $x[$index]+$y[$index];
    $index++;
}

当然,您可以使用等等local $index;代替my $index;


1
不需要0+;如果递增的变量是undef,则postincrement返回0。
2009年

@ysth:当应用程序中某人使用与您相同的全局$ index变量时,您可能会感到惊讶。但是,如果您编写了简短的脚本并且假设...不,请不要这样做。
海尼克

可以说,这个答案比的for循环语法更具可读性。for(my $index; $index <= $#x; $index++){}
Able Mac

4

是。我已经检查了很多书籍和其他博客...结论是,循环计数器没有任何系统变量。我们必须做自己的柜台。如果我错了纠正我。


您是对的,没有可访问的系统计数器。---“做自己的柜台”可以有很多方法;最常见的可能是带有可选长语法的for(aka foreachfor($i=0;$i<=$#array;$i++){}
Able Mac

3

autobox::Core提供了许多方便的for方法:

use autobox::Core;

['a'..'z']->for( sub{
    my ($index, $value) = @_;
    say "$index => $value";
});

或者,查看迭代器模块,例如: Array::Iterator

use Array::Iterator;

my $iter = Array::Iterator->new( ['a'..'z'] );
while ($iter->hasNext) {
    $iter->getNext;
    say $iter->currentIndex . ' => ' . $iter->current;
}

另请参阅:


我将使用以下形式的链接替换发行版的链接:search.cpan.org/perldoc/Array
Brad Gilbert 2010年

@布拉德·吉尔伯特:变了。虽然我个人更喜欢search.cpan.org/dist/Array-Iterator
draegtun 2010年

我同意有时指向Dist视图而不是perldoc视图很有用。仅仅是因为我替换链接,并不一定意味着您应该这样做
布拉德·吉尔伯特

@Brad Gilbert:这也许是perl中TIMTOWTDI的一个很好的例子?:)不管怎样,如果perldoc链接在SO上变得越来越普遍,那么我很乐意切换到使用它。
draegtun

2

哦,可以!(有点,但您不应该)。each(@array)标量上下文中的值可为您提供数组的当前索引。

@a = (a..z);
for (@a) { 
  print each(@a) . "\t" . $_ . "\n"; 
}

each(@a)是在标量上下文中,并且仅返回索引,而不返回该索引处的值。由于我们处于for循环中,因此我们已经在$ _中拥有了值。在while-each循环中经常使用相同的机制。同样的问题。

如果您再做for(@a)一次,问题就来了。索引没有回到您期望的0;它undef随后0,1,2 ......一个报数。的perldoceach()说避免了这个问题。使用for循环来跟踪索引。

基本上:

for(my $i=0; $i<=$#a; $i++) {
  print "The Element at $i is $a[$i]\n";
}

我是替代方法的粉丝:

my $index=0;
for (@a) {
  print "The Element at $index is $a[$index]\n";
  $index++;
}

不错的尝试,但是没有。每个都不访问for循环使用的同一迭代器。每个迭代器都是数组或哈希的一部分。for为数组创建自己的别名列表并对其进行迭代。您可以看到我each(@a)在循环中再次使用的意思,例如print each(@a)."=< $_ >=".each(@a);
阿诺德·克罗斯

1

请考虑:

print "Element at index $_ is $x[$_]\n" for keys @x;

我认为这种keys @x行为不符合您的想法。我希望它返回每个偶数索引的元素。
弥敦道·费尔曼

for keys @x在列表上下文中,返回当前索引,并获取值,您必须引用它$x[$_]---一种更易读的方式来编写完全相同的内容是for my $idx (keys @x){print "Element at index $idx is $x[$idx]\n";}
Able Mac

1

好吧,这是这样的:

use List::Rubyish;

$list = List::Rubyish->new( [ qw<a b c> ] );
$list->each_index( sub { say "\$_=$_" } );

参见List :: Rubyish


0

在大多数情况下,您不需要了解索引。你可以这样做:

my @arr = (1, 2, 3);
foreach (@arr) {
    $_++;
}
print join(", ", @arr);

在这种情况下,输出将为2、3、4,因为foreach设置了实际元素的别名,而不仅仅是副本。


如果将第一行替换为:my @arr =('foo','bar','foo');
匿名2009年

由于在数字上下文中使用字符串标量时,结果为零,因此它将打印1、1、1。
匿名co

7
我知道在大多数情况下不需要索引。问题是当我确实需要它时最好的获取方法。
内森·费尔曼

0

我尝试过...

@array = qw /tomato banana papaya potato/;             # Example array
my $count;                                             # Local variable initial value will be 0.
print "\nBefore For loop value of counter is $count";  # Just printing value before entering the loop.

for (@array) { print "\n",$count++," $_" ; }           # String and variable seperated by comma to
                                                       # execute the value and print.
undef $count;                                          # Undefining so that later parts again it will
                                                       # be reset to 0.

print "\nAfter for loop value of counter is $count";   # Checking the counter value after for loop.

简而言之...

@array = qw /a b c d/;
my $count;
for (@array) { print "\n",$count++," $_"; }
undef $count;

因此,您基本上建议我手动跟踪索引。
弥敦道·费尔曼
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.