首先迭代perl数组的最多n个元素

时间:2016-03-23 12:59:29

标签: arrays perl loops

什么是最令人愉快的惯用写作方式

for (take(100,@array)) {...}

假设没有take(它接受列表的第一个 n 元素,但是如果没有 n 元素则更少)?

我考虑的事情:

  • for (@array[0..99]) {...}

    但如果@array少于100个元素

  • ,那就会失败
  • for (@array[0..min(99,$#array)]) {...}

    min不是Perl中的标准函数

  • for (splice @array,0,100) {...}

    更改数组。

7 个答案:

答案 0 :(得分:7)

  

for (@array[0..min(99,$#array)]) {...}
  但是min不是Perl中的标准函数

min是模块List::Util中的标准函数,它是5.7.3中核心的一部分。

use List::Util qw(min);

for (@array[0..min(99,$#array)]) {  # generator in 5.8.8+
  ...
}

请注意,perl 5.8.8以及之前的版本可能足够聪明,可以将表达式理解为生成器而不是切片。也就是说,从@array一次一个地获取元素0到$ terminus,而不是取出和复制一个匿名片。

答案 1 :(得分:6)

您想要CPAN模块List::Slice

use List::Slice 'head';

foreach my $elem ( head 100, @things ) { ... }

答案 2 :(得分:2)

您已经表明您发现以下内容最干净:

take(100, @array)

所以回答你的问题是什么是最干净的,那就是!我不明白为什么你要找一个替代品。

答案 3 :(得分:2)

如何使用map

my @array = qw ( 1 2 3 4 );
print join "\n", map { $_ // () } @array[0..10]; 

这会从列表中获取10个元素,但会对其应用“已定义”测试 - 如果未定义,则返回空列表。

所以你可以:

for ( map { $_ // () } @array[0..100] ) { 
   #do something
}

注意 - //是一个已定义的或运算符,仅可从perl 5.10+获得。您可以使用defined三元组:

print join "\n", map { defined ? $_ : () } @array[0..10]; 

答案 4 :(得分:1)

您可以在循环中添加额外的检查,以便在到达结束时中断。

my @arr = (1 .. 90);

for ( @arr[0..99]) {
    last unless defined $_;
    say;
}

但这对于中间有undef值的数组不起作用,例如:

my @foo = (1, 2, undef, 4);
my @bar;
$bar[2] = 'foo'; # (undef, undef, 'foo')

答案 5 :(得分:1)

其他回复已涵盖这一点,但为了彻底, 在CPAN上实施了几个“南瓜perl”gather/take :-)

还有Damian Conway的Perl6::Gather几乎相同但需要Perl6::Export

他们让您以您想要的方式处理列表。 例如到“take”字母表的一半:

 perl -E 'use List::Gather; @lpha = ("a" .. "z"); 
         @half = gather { for (@lpha){ take $_ if gathered < 13 } } ; say @half'
 abcdefghijklm
如果我们还没到中途,那么

或更少:

 perl -E 'use List::Gather; @lpha = ("a" .. "c");  
         @half = gather { for (@lpha) { take $_ if gathered < 13 } } ; say @half'
 abc

使用List::Gather gather块可以循环(因为gather{}内部的词法作用域??)并且块内需要主题$_

perl -E 'use List::Gather; @lpha = ("a" .. "g"); 
          @half = gather for (@lpha) { take $_ if gathered < 13 }; say @half'

使用Syntax::Keyword::Gather,您可以在gather{}块中执行此操作(List::Gather也可以这样做):

perl -E 'use Syntax::Keyword::Gather; @lpha = ("a".."g"); 
        @half = gather { for (@lpha){ take if gathered < 13 } }; say @half'

我发现gather/take是使用列表的一种不错的替代方法。是否足够好,有一天可以发送perl - 比如在List::Util中 - 是你问题的一个有趣的隐含部分;-)但它们是CPAN上的。

<强>后记

为了解决@simbabque,@ azid和@Joachim Breitner提出的关于defined的一些问题,可以在take()例程中添加更多检查。

我在这里使用Ingy的boolean

perl -E 'use boolean; use List::Gather; 
        @lpha = ("a" .. "g", "", undef, undef, "x", "0", "z"); 
        @half = gather { for (@lpha){ take $_ if boolean($_) && gathered < 13 }};
        use DDP; p @half;'

<强>输出:

[
    [0] "a",
    [1] "b",
    [2] "c",
    [3] "d",
    [4] "e",
    [5] "f",
    [6] "g",
    [7] "x",
    [8] "z"
]

答案 6 :(得分:0)

我认为你应该使用迭代器模式,即

my $iterator = create_iterator(100);
while (my $element = $iterator->()) {
   ...;
}

limit可能嵌入到迭代器创建中,即

sub create_iterator {
     my $limit = shift;
     my @data = (0 x 1000);
     my $i = 0;
     return sub {
        return $data[$i++] if ($i < @data);
     }
}

PS。有一个限制,undef不能成为@data

的一部分