是否有紧凑的Perl操作可以从数组中分割出备用元素?

问题描述 投票:15回答:10

如果我在Python中有数组myarray,则可以使用切片符号

myarray[0::2]

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

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

Perl中有类似的设施吗?

谢谢。

perl syntax
10个回答
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中编写代码。


23
投票

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

 @array[@indices];

没有内置的语法可以选择倍数,但并不难。使用grep生成所需的索引列表:

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

如果使用PDL,则有很多不错的切片选项。


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用作数组引用,则还有很多访问器方法。


9
投票

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

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

逐步,多数民众赞成:

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

7
投票

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

所以您必须做:

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

5
投票

制作此prettier的一种方法是将其包装在autobox之类的东西中。

例如,使用autobox

autobox::Core

这是定义这些自动装箱方法的方式:

autobox::Core

/ I3az /


4
投票

如果您不关心顺序,并且列表中的奇数元素是唯一的,则可以将数组简洁地转换为哈希并采用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;
}

(不,这不是认真的答案)


2
投票

另一个方式将通过使用values

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

1
投票

[如果您不介意使用$ |的晦涩功能,您可以这样做:

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开头,而不是任意索引。


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
}
© www.soinside.com 2019 - 2024. All rights reserved.