2011-02-03 36 views
16

Khi tôi thử sauLàm thế nào để trừ một mảng khỏi mảng?

#!/usr/bin/perl 

use strict; 
use warnings; 
use Data::Dumper; 

my @bl = qw(red green blue); 
my @a = qw(green yellow purple blue pink); 

print Dumper [grep {not @bl} @a]; 

Tôi nhận được một mảng trống. Tôi đã mong đợi rằng @bl bị trừ từ @a, vì vậy đầu ra là yellow purple pink.

Có gì sai ở đây?

+2

“trừ” không phải là bên phải từ đây. Khi bạn ** làm ** tìm đúng từ, bạn sẽ phát hiện ra từ đó là từ kích hoạt tấn công băm Pavlovian. – tchrist

Trả lời

33

Bạn cần phải bật @bl thành một băm để thực hiện sự khác biệt thiết lập:

my %in_bl = map {$_ => 1} @bl; 
my @diff = grep {not $in_bl{$_}} @a; 
+7

Điều này tốt hơn câu trả lời faq cho câu hỏi này - faq chỉ cho bạn thấy cách tính toán "sự khác biệt đối xứng" giữa hai mảng – mob

+1

@mob: Vì vậy, thư brian có bản cập nhật được đề xuất. – tchrist

+6

Trong Perl 5.10 hoặc mới hơn, bạn có thể viết nó 'my @diff = grep {not $ _ ~~ @bl} @a;' –

4

Xem perlfaq4: How do I compute the difference of two arrays?

Trong code của bạn, not có lẽ không làm những gì bạn nghĩ rằng nó đang làm.

not @bl sẽ luôn là 1 nếu @bl là một mảng trống và undef nếu @bl không trống. Nó không có nghĩa là "các phần tử không có trong @bl" theo bất kỳ nghĩa nào.

4

@b1 để đánh giá đúng (nó là một mảng với một tổ chức phi zero số phần tử), vì vậy kiểm tra boolean trong grep của bạn xây dựng (not @b1) sẽ luôn trả về false. grep lọc một mảng chỉ trả về các phần tử mà phép thử boolean trả về true.

Bạn cần kiểm tra xem liệu $_ (yếu tố mảng hiện đang được xem xét) có đang ở trong @bl hay không. Một cách để làm điều này là để tạo ra một băm tạm thời sử dụng @bl như các phím, sau đó trong tầm kiểm soát tuyên bố grep của bạn cho sự hiện diện của $_ trong các phím băm:

#!/usr/bin/perl 

use strict; 
use warnings; 
use Data::Dumper; 

my @bl = qw(red green blue); 
my @a = qw(green yellow purple blue pink); 

# create a hash 
my %h; 

# nifty trick - use a hash slice to populate the 
# hash. The values are irrelevant so we'll use @bl 
# for those too 
@h{@bl} = @bl; 

print Dumper [grep {!exists $h{$_}} @a]; 
+1

Việc điền các giá trị '% h' là quá mức cần thiết. Nếu bạn sử dụng 'exist', điền vào với' @h {@bl} =() 'sẽ chỉ tốt và có thể nhanh hơn. –

1

Một cách khác, bằng cách sử dụng minus chức năng từ Acme::Tools CPAN module:

use strict; 
use warnings; 
use Data::Dumper; 
use Acme::Tools qw(minus); 

my @bl = qw(red green blue); 
my @a = qw(green yellow purple blue pink); 
my @diff = minus(\@a, \@bl); 
print Dumper(\@diff); 

__END__ 

$VAR1 = [ 
      'yellow', 
      'purple', 
      'pink' 
     ]; 
2

Một lựa chọn khác sử dụng perl5i:

use perl5i::2; 

my @bl = qw(red green blue); 
my @a = qw(green yellow purple blue pink); 
my @diff = @a->diff(\@bl); 

say @diff->mo->perl; 
4

Vì Perl 5.18.0 toán tử smartmatch được coi là thử nghiệm: The smartmatch family of features are now experimental. Do đó tôi sẽ không sử dụng giải pháp này dưới đây nữa.

Một cách khác với Smartmatch-điều hành (nếu bạn có perl-phiên bản 5,010 hoặc cao hơn):

#!/usr/bin/env perl 
use warnings; 
use 5.012; 

my @bl = qw(red green blue); 
my @a = qw(green yellow purple blue pink); 

my @s = grep{ not $_ ~~ @bl } @a; 
say "@s"; # yellow purple pink 
+0

Sẽ không dẫn đến hiệu suất 'O (n^2)', vì toán tử smartmatch khớp với từng phần tử '@ bl' cho mỗi phần tử trong' @ a'? – Floegipoky

+0

@Floegipoky: Nhận xét của bạn hợp lý với tôi. Với Perl 5, tôi đã ngừng sử dụng toán tử smartmatch. Tôi đã chỉnh sửa câu trả lời. –

-1

Một cách khác là sử dụng:

List::Compare CPAN module 
use List::Compare ; 
... 
my $compare_obj 
    = List::Compare->new(\@a , \@b1) ; 
@diff = $compare_obj->get_Lonly() ; 
... 
Các vấn đề liên quan