2011-01-20 27 views
6

Trong python, tôi có thể làm như sau để có được tất cả các đối tượng trong một danh sách với một thuộc tính cụ thể. Trong ví dụ này, tôi lấy danh sách các id lĩnh vực mỗi obj trong danh sách objs nơi obj.id lớn hơn 100:Perl tương đương với sự hiểu biết danh sách của Python với câu lệnh if nếu được nhúng?

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

Làm thế nào tôi sẽ làm điều tương tự trong perl? Tôi nghĩ rằng tôi muốn sử dụng map, nhưng tôi không biết làm thế nào để có điều kiện bản đồ các mục từ nguồn gốc được đặt thành bộ đích.

Trả lời

13

Khối map có thể trả về 0 hoặc nhiều phần tử cho mỗi phần tử trong danh sách gốc. Để bỏ qua một yếu tố, chỉ cần trả lại danh sách trống ():

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

này giả định rằng các đối tượng trong @objs có một id thuộc tính và accessor liên quan. Nếu bạn muốn truy cập băm trực tiếp, bạn có thể làm điều đó quá:

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

Hoặc, bạn chỉ có thể kết hợp mapgrep:

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

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

Tôi không chắc chắn mà những người sẽ hiệu quả hơn, nhưng trừ khi @objs thực sự lớn, điều đó không quan trọng lắm.

Nếu giá trị bạn đang chiết xuất từ ​​các đối tượng là tốn kém để tính toán, sau đó bạn có thể cache giá trị cho kỳ thi này và trở lại giá trị:

my @vals = map { my $v = $_->expensive_method; $v > 100 ? $v :() } @objs; 
+4

'bản đồ + ($ _-> id) x ($ _-> id> 100), @ objs' – ysth

+4

@ysth: Yikes. Tôi nghĩ rằng tôi muốn tiết kiệm một cho chơi golf mã. Toán tử bậc ba an toàn hơn nhiều, vì bạn không phải đảm bảo điều kiện của bạn chỉ trả về 0 hoặc 1. – cjm

+1

nếu nghi ngờ, hãy sử dụng toán tử '() x !!' – ysth

2

Sử dụng grep để chỉ trả về các mục phù hợp với điều kiện. Nó giống như filter bằng các ngôn ngữ khác.

grep{ condition }@array

Ví dụ:

my @nums = (1, 50, 7, 105, 200, 3, 1000); 
my @numsover100 = grep { $_ > 100 } @nums; 
foreach my $num (@numsover100) { 
    print $num . "\n"; 
} 
+0

Vì vậy, tôi phải ghép nối với 'bản đồ' để có được những gì tôi muốn? Vì tôi thực sự muốn các trường con của mỗi đối tượng mà tôi đã 'grep''d ra khỏi danh sách? –

+0

Đúng, đó là cách tôi làm. – Mikel

1

Bạn có thể có thể nhận được bởi với cách kết hợp mapfilter, mà chủ yếu là những gì chúng ta đã làm trong Python trước comprehensions danh sách.

+2

Tương đương 'filter' của Perl được gọi là' grep'. Không cần phải đi đến độ dài họ đã đi vào liên kết perlmonks của bạn cho vấn đề này. – Mikel

0

Sử dụng bản đồ và grep cùng nhau đi qua danh sách hai lần. Xây dựng của riêng bạn:

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, 
) 
Các vấn đề liên quan