如果我在Python中有数组myarray
,则可以使用切片符号
myarray[0::2]
仅选择偶数索引元素。例如:
>>> ar = [ "zero", "one", "two", "three", "four", "five", "six" ]
>>> ar [ 0 : : 2 ]
['zero', 'two', 'four', 'six']
Perl中有类似的设施吗?
谢谢。
有数组切片:
my @slice = @array[1,42,23,0];
有一种生成$ x和$ y之间的列表的方法:
my @list = $x .. $y
有一种从列表构建新列表的方法:
my @new = map { $_ * 2 } @list;
还有一种获取数组长度的方法:
my $len = $#array;
放在一起:
my @even_indexed_elements = @array[map { $_ * 2 } 0 .. int($#array / 2)];
当然,不如python等效,但是它执行相同的工作,如果您经常使用它并希望节省编写工作,当然可以将其放在子例程中。
而且还有可能允许以更自然的方式在List::AllUtils
中编写代码。
Perl数组切片是数组名称前面的@
,然后是所需的索引列表:
@array[@indices];
没有内置的语法可以选择倍数,但并不难。使用grep
生成所需的索引列表:
my @array = qw( zero one two three four five six );
my @evens = @array[ grep { ! ($_ % 2) } 0 .. $#array ];
如果使用PDL,则有很多不错的切片选项。
我已经在CPAN上编写了模块List::Gen,它提供了执行此操作的替代方法:
use List::Gen qw/by/;
my @array = qw/zero one two three four five six/;
my @slice = map {$$_[0]} by 2 => @array;
by
将@array
划分为两个元素组成的组,并返回一个数组引用数组。然后map
获得此列表,因此映射中的每个$_
将是一个数组引用。 $$_[0]
(也可以写为$_->[0]
)然后获取by
创建的每个组的第一个元素。
或者,使用mapn
内部使用的by
功能:
use List::Gen qw/mapn/;
my @slice = mapn {$_[0]} 2 => @array;
或者,如果您的源列表很大,并且您可能只需要某些元素,则可以使用List::Gen
的惰性列表:
use List::Gen qw/by gen/;
my $slicer = gen {$$_[0]} by 2 => @array;
$slicer
现在是一个惰性列表(数组引用),它将按需生成切片,而不处理您不需要的任何内容。如果您不想将$slicer
用作数组引用,则还有很多访问器方法。
我将分两步进行:首先生成所需的索引,然后使用切片操作提取它们:
@indices = map { $_ * 2 } (0 .. int($#array / 2));
my @extracted = @array[@indices];
逐步,多数民众赞成:
Perl 6将大大改善性能,但是(到目前为止?)Perl 5的切片能力非常有限:您必须显式指定所需的索引,并且它不能是开放式的。
所以您必须做:
@ar = ( "zero", "one", "two", "three", "four", "five", "six" );
print @ar[ grep $_ % 2 == 0, 0..$#ar ]
如果您不关心顺序,并且列表中的奇数元素是唯一的,则可以将数组简洁地转换为哈希并采用use autobox::Core;
my @ar = qw/zero one two three four five six/;
# you could do this
@ar->slice_while( sub{ not $_ % 2 } );
# and this
@ar->slice_by(2);
# or even this
@ar->evens;
:
sub autobox::Core::ARRAY::slice_while {
my ($self, $code) = @_;
my @array;
for (my $i = 0; $i <= $#{ $self }; $i++) {
local $_ = $i;
push @array, $self->[ $i ] if $code->();
}
return wantarray ? @array : \@array;
}
sub autobox::Core::ARRAY::slice_by {
my ($self, $by) = @_;
my @array = @$self[ map { $_ * $by } 0 .. int( $#{$self} / $by )];
return wantarray ? @array : \@array;
}
sub autobox::Core::ARRAY::evens {
my $self = shift;
my @array = $self->slice_by(2);
return wantarray ? @array : \@array;
}
(不,这不是认真的答案)
另一个方式将通过使用values
:
@even_elements = values %{{@array}};
@odd_elements = keys %{{@array}};
[如果您不介意使用$ |的晦涩功能,您可以这样做:
grep
或作为1个底线:
my @array = qw( zero one two three four five six );
print map { "$_ " } @array[grep { !($_ & 1) } 0 .. $#array]; #even
Output:zero two four six
print map { "$_ " } @array[grep { ($_ & 1) } 0 .. $#array]; #odd
Output:one three five
基本上是-$ |触发器会在0到1的值之间(尽管通常会减小数值的-),因此grep每隔两次就会看到一个“ true”值,从而使它从初始值$ |开始返回所有其他项。请注意,您必须以0或1开头,而不是任意索引。
这是最简单的代码,不创建任何索引数组:
{
local $|; # don't mess with global $|
@ar = ( "zero", "one", "two", "three", "four", "five", "six" );
$| = 0;
@even = grep --$|, @ar;
$| = 1;
@odd = grep --$|, @ar;
print "even: @even\\n";
# even: zero two four six
print "odd: @odd\\n";
# odd: one three five
}