是否有一个紧凑的Perl操作来切割阵列中的替代元素?

时间:2010-10-13 15:56:09

标签: perl syntax

如果我在Python中有一个数组myarray,我可以使用切片表示法

myarray[0::2]

仅选择偶数索引元素。例如:

>>> ar = [ "zero", "one", "two", "three", "four", "five", "six" ]
>>> ar [ 0 : : 2 ]
['zero', 'two', 'four', 'six']

Perl中是否有类似的设施?

感谢。

10 个答案:

答案 0 :(得分:22)

Perl数组切片是数组名称前面的@,然后是你想要的索引列表:

 @array[@indices];

没有内置语法来选择倍数,但它并不那么难。使用grep()生成所需的索引列表:

 my @array = qw( zero one two three four five six );
 my @evens = @array[ grep { ! ($_ % 2) } 0 .. $#array ];

如果您使用的是PDL,那么有许多不错的切片选项。

答案 1 :(得分:21)

有数组切片:

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中以更自然的方式写出来。

答案 2 :(得分:11)

我在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也有一堆访问器方法。

答案 3 :(得分:9)

我将分两步完成:首先生成所需的索引,然后使用切片操作来提取它们:

@indices = map { $_ * 2 } (0 .. int($#array / 2));
my @extracted = @array[@indices];

一步一步,那是:

  • 生成一个整数列表,从0到数组的最后一个元素除以2
  • 将每个整数乘以2:现在我们有偶数从零到最后一个元素的索引
  • 从原始数组中提取这些元素

答案 4 :(得分:7)

Perl 6将大大改善,但(到目前为止?)Perl 5的切片功能非常有限:您必须明确指定所需的索引,并且它不能是开放式的。

所以你必须这样做:

@ar = ( "zero", "one", "two", "three", "four", "five", "six" );
print @ar[ grep $_ % 2 == 0, 0..$#ar ]

答案 5 :(得分:5)

使这个更漂亮的的一种方法是将其包裹在autobox之类的内容中。

例如使用autobox::Core

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;

这是您定义这些autobox方法的方法:

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;
}

/ I3az /

答案 6 :(得分:4)

如果您不关心顺序,并且列表的奇数元素是唯一的,您可以简单地将数组转换为哈希并取values

@even_elements = values %{{@array}};
@odd_elements = keys %{{@array}};

(不,这不是一个严肃的答案)

答案 7 :(得分:2)

另一种方式是使用grep

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 

答案 8 :(得分: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
}

或作为1班轮:

 { local $|=0; @even = grep --$|, @ar; }

基本上, - $ |触发器在0和1之间的值(尽管 - 通常会减少数值),因此grep每隔一段时间看到一个“true”值,从而导致它返回以$ |的初始值开头的每个其他项。请注意,您必须从0或1开始,而不是从任意索引开始。

答案 9 :(得分:1)

这是最简单的代码而不创建任何索引数组:

sub even { my $f=0; return grep {++$f%2} @_; }
sub odd { my $f=1; return grep {++$f%2} @_; }