for (take(100,@array)) {...}
如果没有 take
(取列表的前n个元素,但如果没有n个元素,则取少于n个元素),怎么办?
我考虑过以下几点:
for (@array[0..99]) {...}
但如果
@array
的元素少于100个,则会失败。for (@array[0..min(99,$#array)]) {...}
但
min
不是Perl中的标准函数。for (splice @array,0,100) {...}
但这会改变数组本身。
for (take(100,@array)) {...}
如果没有 take
(取列表的前n个元素,但如果没有n个元素,则取少于n个元素),怎么办?
我考虑过以下几点:
for (@array[0..99]) {...}
但如果@array
的元素少于100个,则会失败。
for (@array[0..min(99,$#array)]) {...}
但min
不是Perl中的标准函数。
for (splice @array,0,100) {...}
但这会改变数组本身。
for (@array[0..min(99,$#array)]) {...}
但在Perl中min不是标准函数
min
是List::Util模块中的标准函数,自5.7.3起已经成为核心模块。
use List::Util qw(min);
for (@array[0..min(99,$#array)]) { # generator in 5.8.8+
...
}
tie()
数组,每次FETCH都会发出警告,以说服自己这个优化已经实现。(每个循环只有一个连续的FETCH,而不是一次性全部执行。) - pilcrowgather/take
这样的花哨东西。 - G. Citofor
循环中,此函数将复制 n 个元素到一个匿名数组中,然后对其进行迭代。相比之下,在 for
循环中使用显式传统切片会在每次迭代中逐个获取元素。除非 n 变得很大,否则不要紧。 - pilcrow您表示以下内容最清晰:
take(100, @array)
map
如何?my @array = qw ( 1 2 3 4 );
print join "\n", map { $_ // () } @array[0..10];
这段代码从一个列表中取出10个元素,并对其进行“defined”测试,如果未定义,则返回一个空列表。
因此,您可以:
for ( map { $_ // () } @array[0..100] ) {
#do something
}
//
是一个定义或运算符,仅在Perl 5.10+及以上版本中可用。您可以使用一个defined
三元运算符代替:print join "\n", map { defined ? $_ : () } @array[0..10];
boolean
在这里也可以工作 :-D perl -Mboolean -E '@array = ("a" .. "g", "", undef, undef, "x", "0", "z"); print join "\n", map { boolean($_) ? $_ : () } @array[0..100]; '
- G. Citoundef
,作为它所做的结果。我认为这不是什么大问题,因为我很少在循环上下文中需要处理存在但未定义的情况。 - Sobriquemy @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')
undef
... 任何被评估为假 (0
, ""
) 的东西。 - Zaid还有达米安·康威(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、@zaid和@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"
]
我认为你应该使用迭代器模式,即
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);
}
}
注意:有一个限制,即undef
不能成为@data
的一部分。
undef
,它就会中断。 - Joachim Breitner
take
做什么? - simbabque