2011-01-20 2 views
6

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

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

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

ответ

13

Блок 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; 
+4

'map + ($ _-> id) x ($ _-> id> 100), @ objs' – ysth

+4

@ysth: Yikes. Думаю, я бы сохранил этот код для гольфа. Тернарный оператор намного безопаснее, так как вам не нужно, чтобы ваше условие возвращало только 0 или 1. – cjm

+1

, если у вас есть сомнения, используйте оператор '() x !!' – ysth

2

Используйте 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"; 
} 
+0

Итак, мне нужно сопоставить это с 'map', чтобы получить то, что я хочу? Поскольку на самом деле я хочу, чтобы подполе каждого из объектов, которые я «grep» выбрал из списка? –

+0

Да, так я и сделал. – Mikel

1

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

+2

Perl 'filter' эквивалент называется' grep'. Не нужно идти в длину, на которую они пошли, в вашей связи perlmonks для этой проблемы. – Mikel

0

Используя карту и 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, 
) 
Смежные вопросы