Существует ли компактная операция Perl для вырезания альтернативных элементов из массива? - PullRequest
15 голосов
/ 13 октября 2010

Если у меня есть массив myarray в Python, я могу использовать запись среза

myarray[0::2]

, чтобы выбрать только элементы с четным индексом.Например:

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

Есть ли в Perl похожее средство?

Спасибо.

Ответы [ 10 ]

22 голосов
/ 13 октября 2010

Срез массива Perl - это @ перед именем массива, а затем список нужных вам индексов:

 @array[@indices];

Нет встроенного синтаксиса для выбора кратных, но это не тактак трудно.Используйте grep () для создания списка нужных вам индексов:

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

Если вы используете PDL , есть много хороших опций нарезки.

21 голосов
/ 13 октября 2010

Есть фрагменты массива:

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.

11 голосов
/ 13 октября 2010

Я написал модуль List :: Gen на CPAN, который предоставляет альтернативный способ сделать это:

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 теперь ленивыйlist (ссылка на массив), который будет генерировать его фрагменты по требованию, не обрабатывая ничего, о чем вы не просили.$slicer также имеет несколько методов доступа, если вы не хотите использовать его как массив. Ref.

9 голосов
/ 13 октября 2010

Я сделаю это в два этапа: сначала сгенерируйте нужные индексы, а затем используйте операцию среза для их извлечения:

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

Пошагово, вот:

  • создать список целых чисел от 0 до последнего элемента массива, разделенный на два
  • умножить каждое целое число на два: теперь у нас есть четные числа от нуля до индекса последнего элемента
  • извлечь эти элементы из исходного массива
7 голосов
/ 13 октября 2010

Perl 6 значительно улучшит ситуацию, но (пока?) Perl 5 имеет довольно ограниченную возможность нарезки: вам нужно явно указать нужные индексы, и он не может быть открытым.

Итак, вам нужно сделать:

@ar = ( "zero", "one", "two", "three", "four", "five", "six" );
print @ar[ grep $_ % 2 == 0, 0..$#ar ]
5 голосов
/ 13 октября 2010

Один из способов сделать это красивее - это обернуть его во что-то вроде 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 /

4 голосов
/ 13 октября 2010

Если вам не важен порядок, и если нечетные элементы списка уникальны, вы можете кратко преобразовать массив в хеш и взять values:

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

(Нет, это не серьезный ответ)

2 голосов
/ 14 октября 2010

Другой способ будет с использованием 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 
1 голос
/ 01 декабря 2015

Вот самый простой код без создания индексных массивов:

sub even { my $f=0; return grep {++$f%2} @_; }
sub odd { my $f=1; return grep {++$f%2} @_; }
1 голос
/ 22 июня 2013

Если вы не против использовать скрытую функцию $ |Вы можете сделать это:

{
    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 видит «истинное» значение каждый раз, заставляя его возвращать все остальные элементы, начиная с начального значения $ |.Обратите внимание, что вы должны начинать с 0 или 1, а не с произвольного индекса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...