Perl-эквивалент списка представлений Python со встроенным оператором if? - PullRequest
6 голосов
/ 21 января 2011

В python я могу сделать следующее, чтобы получить все объекты в списке с определенным свойством. В этом примере я получаю список id полей каждого obj в списке objs, где obj.id больше 100:

ids = [ obj.id for obj in objs if obj.id > 100]

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

Ответы [ 4 ]

13 голосов
/ 21 января 2011

Блок map может возвращать 0 или более элементов для каждого элемента в исходном списке. Чтобы пропустить элемент, просто верните пустой список ():

my @ids = map { $_->id > 100 ? $_->id : () } @objs;

Предполагается, что объекты в @objs имеют атрибут id и связанный метод доступа. Если вам нужен прямой хеш-доступ, вы можете сделать это тоже:

my @ids = map { $_->{id} > 100 ? $_->{id} : () } @objs;

Или вы можете просто объединить map и grep:

my @ids = map { $_->id } grep { $_->id > 100 } @objs;

# Or reverse the order to avoid calling $_->id twice:
my @ids = grep { $_ > 100 } map { $_->id } @objs;

Я не уверен, какой из них будет более эффективным, но если @objs действительно большой, вряд ли это будет иметь большое значение.

Если значение, которое вы извлекаете из объекта, слишком дорого для вычисления, вы можете кэшировать значение для теста и возвращаемое значение:

my @vals = map { my $v = $_->expensive_method;  $v > 100 ? $v : () } @objs;
2 голосов
/ 21 января 2011

Используйте grep для возврата только тех элементов, которые соответствуют условию.Это похоже на filter на других языках.

grep { condition } @array

Например:

my @nums = (1, 50, 7, 105, 200, 3, 1000);
my @numsover100 = grep { $_ > 100 } @nums;
foreach my $num (@numsover100) {
    print $num . "\n";
}
1 голос
/ 21 января 2011

Вероятно, вы могли бы обойтись путем объединения map и filter , что, по сути, было то, что мы делали в Python до понимания списка.

0 голосов
/ 21 января 2011

Использование map и grep вместе проходит по списку дважды.Создайте свой собственный:

sub fancy_filter {
  my ($map_block, $grep_block, @list) = @_;
  my @results;
  foreach my $item (@list) {
    local $_ = $item;
    if ($grep_block->()) {
      push @results, $map_block->();
    }
  }
  return @results;
}

my @ids = fancy_filter(
  sub { $_->{id} },       # map block
  sub { $_->{id} > 100 }, # grep block
  @id_list,
)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...