Mảng có nhiều dữ liệu và tôi cần xóa hai phần tử.
Dưới đây là đoạn mã tôi đang sử dụng,
my @array = (1,2,3,4,5,5,6,5,4,9);
my $element_omitted = 5;
@array = grep { $_ != $element_omitted } @array;
Mảng có nhiều dữ liệu và tôi cần xóa hai phần tử.
Dưới đây là đoạn mã tôi đang sử dụng,
my @array = (1,2,3,4,5,5,6,5,4,9);
my $element_omitted = 5;
@array = grep { $_ != $element_omitted } @array;
Câu trả lời:
Sử dụng mối nối nếu bạn đã biết chỉ mục của phần tử bạn muốn xóa.
Grep hoạt động nếu bạn đang tìm kiếm.
Nếu bạn cần thực hiện nhiều điều này, bạn sẽ nhận được hiệu suất tốt hơn nhiều nếu bạn giữ mảng của mình theo thứ tự được sắp xếp, vì sau đó bạn có thể thực hiện tìm kiếm nhị phân để tìm chỉ mục cần thiết.
Nếu nó có ý nghĩa trong ngữ cảnh của bạn, bạn có thể muốn xem xét sử dụng một "giá trị ma thuật" cho các bản ghi đã xóa, thay vì xóa chúng, để tiết kiệm cho việc di chuyển dữ liệu - chẳng hạn như đặt các phần tử đã xóa thành undef. Đương nhiên, điều này có những vấn đề riêng (nếu bạn cần biết số lượng phần tử "sống", bạn cần theo dõi riêng, v.v.), nhưng có thể đáng gặp rắc rối tùy thuộc vào ứng dụng của bạn.
Chỉnh sửa Thực ra bây giờ tôi xem xét lại lần thứ hai - không sử dụng mã grep ở trên. Sẽ hiệu quả hơn nếu tìm chỉ mục của phần tử bạn muốn xóa, sau đó sử dụng mối nối để xóa nó (mã bạn đã tích lũy tất cả các kết quả không khớp ..)
my $index = 0;
$index++ until $arr[$index] eq 'foo';
splice(@arr, $index, 1);
Điều đó sẽ xóa lần xuất hiện đầu tiên. Việc xóa tất cả các lần xuất hiện rất giống nhau, ngoại trừ bạn sẽ muốn nhận tất cả các chỉ mục trong một lần vượt qua:
my @del_indexes = grep { $arr[$_] eq 'foo' } 0..$#arr;
Phần còn lại được để lại như một bài tập cho người đọc - hãy nhớ rằng mảng thay đổi khi bạn nối nó!
Edit2 John Siracusa đã chỉ ra một cách chính xác rằng tôi có một lỗi trong ví dụ của mình .. đã sửa, xin lỗi về điều đó.
my ($index) = grep { $arr[$_] eq 'foo' } 0..$#arr; if (defined $index) {splice(@arr, $index, 1); }
- cho trận đấu đầu tiên
Đây có phải là điều bạn sẽ làm nhiều không? Nếu vậy, bạn có thể muốn xem xét một cấu trúc dữ liệu khác. Grep sẽ tìm kiếm toàn bộ mảng mọi lúc và đối với một mảng lớn có thể khá tốn kém. Nếu tốc độ là một vấn đề thì bạn có thể cân nhắc sử dụng Hash thay thế.
Trong ví dụ của bạn, khóa sẽ là số và giá trị sẽ là số phần tử của số đó.
nếu bạn thay đổi
my @del_indexes = grep { $arr[$_] eq 'foo' } 0..$#arr;
đến
my @del_indexes = reverse(grep { $arr[$_] eq 'foo' } 0..$#arr);
Điều này tránh được vấn đề đánh số lại mảng bằng cách xóa các phần tử ở phía sau mảng trước. Đặt một splice () trong một vòng lặp foreach sẽ xóa @arr. Tương đối đơn giản và dễ đọc ...
foreach $item (@del_indexes) {
splice (@arr,$item,1);
}
Bạn có thể sử dụng phương pháp cắt mảng thay vì ghép nối. Grep để trả về các chỉ số bạn muốn giữ lại và sử dụng tính năng cắt:
my @arr = ...;
my @indicesToKeep = grep { $arr[$_] ne 'foo' } 0..$#arr;
@arr = @arr[@indiciesToKeep];
@arr = @arr[grep ...]
cái mà tôi đặc biệt thích. Tôi không chắc nó hiệu quả đến mức nào, nhưng tôi sẽ bắt đầu sử dụng nó vì nó không thể tệ hơn các giải pháp khác.
Phần còn lại của bài viết ghi lại sự khó khăn của việc biến các bài kiểm tra trên các phần tử thành splice
hiệu số. Do đó, làm cho nó trở thành một câu trả lời đầy đủ hơn .
Nhìn vào các vòng quay mà bạn phải trải qua để có một thuật toán hiệu quả (tức là một lần vượt qua) để biến các bài kiểm tra trên các mục danh sách thành chỉ mục. Và nó không trực quan chút nào.
sub array_remove ( \@& ) {
my ( $arr_ref, $test_block ) = @_;
my $sp_start = 0;
my $sp_len = 0;
for ( my $inx = 0; $inx <= $#$arr_ref; $inx++ ) {
local $_ = $arr_ref->[$inx];
next unless $test_block->( $_ );
if ( $sp_len > 0 && $inx > $sp_start + $sp_len ) {
splice( @$arr_ref, $sp_start, $sp_len );
$inx = $inx - $sp_len;
$sp_len = 0;
}
$sp_start = $inx if ++$sp_len == 1;
}
splice( @$arr_ref, $sp_start, $sp_len ) if $sp_len > 0;
return;
}
undef
. Bên cạnh đó, từ tài liệu được liên kết bởi ringø: "CẢNH BÁO: Việc gọi xóa trên các giá trị mảng không được khuyến khích. Khái niệm xóa hoặc kiểm tra sự tồn tại của các phần tử mảng Perl không nhất quán về mặt khái niệm và có thể dẫn đến hành vi đáng ngạc nhiên." (đoạn trước trong tài liệu có tất cả các chi tiết đẫm máu).
Xóa tất cả các lần xuất hiện của mảng if 'something'.
Dựa trên câu trả lời của SquareCog:
my @arr = ('1','2','3','4','3','2', '3','4','3');
my @dix = grep { $arr[$_] eq '4' } 0..$#arr;
my $o = 0;
for (@dix) {
splice(@arr, $_-$o, 1);
$o++;
}
print join("\n", @arr);
Mỗi lần chúng tôi xóa chỉ mục khỏi @arr
, chỉ mục chính xác tiếp theo để xóa sẽ là $_-current_loop_step
.
Điều tốt nhất tôi tìm thấy là sự kết hợp của "undef" và "grep":
foreach $index ( @list_of_indexes_to_be_skiped ) {
undef($array[$index]);
}
@array = grep { defined($_) } @array;
Đó là thủ thuật! Federico
grep
cuối sau đó xóa chúng.
Chỉ để chắc chắn rằng tôi đã đánh giá các giải pháp bản đồ và grep, trước tiên tìm kiếm chỉ mục của các phần tử phù hợp (những phần tử cần loại bỏ) và sau đó trực tiếp loại bỏ các phần tử bằng grep mà không cần tìm kiếm chỉ mục. Tôi có vẻ rằng giải pháp đầu tiên do Sam đề xuất khi hỏi câu hỏi của anh ấy đã là nhanh nhất.
use Benchmark;
my @A=qw(A B C A D E A F G H A I J K L A M N);
my @M1; my @G; my @M2;
my @Ashrunk;
timethese( 1000000, {
'map1' => sub {
my $i=0;
@M1 = map { $i++; $_ eq 'A' ? $i-1 : ();} @A;
},
'map2' => sub {
my $i=0;
@M2 = map { $A[$_] eq 'A' ? $_ : () ;} 0..$#A;
},
'grep' => sub {
@G = grep { $A[$_] eq 'A' } 0..$#A;
},
'grem' => sub {
@Ashrunk = grep { $_ ne 'A' } @A;
},
});
Kết quả là:
Benchmark: timing 1000000 iterations of grem, grep, map1, map2...
grem: 4 wallclock secs ( 3.37 usr + 0.00 sys = 3.37 CPU) @ 296823.98/s (n=1000000)
grep: 3 wallclock secs ( 2.95 usr + 0.00 sys = 2.95 CPU) @ 339213.03/s (n=1000000)
map1: 4 wallclock secs ( 4.01 usr + 0.00 sys = 4.01 CPU) @ 249438.76/s (n=1000000)
map2: 2 wallclock secs ( 3.67 usr + 0.00 sys = 3.67 CPU) @ 272702.48/s (n=1000000)
M1 = 0 3 6 10 15
M2 = 0 3 6 10 15
G = 0 3 6 10 15
Ashrunk = B C D E F G H I J K L M N
Như được hiển thị theo thời gian đã trôi qua, thật vô ích nếu cố gắng triển khai chức năng loại bỏ bằng cách sử dụng chỉ mục được xác định bởi bản đồ hoặc bản đồ. Chỉ cần loại bỏ trực tiếp grep.
Trước khi thử nghiệm, tôi đã nghĩ rằng "map1" sẽ là hiệu quả nhất ... Tôi đoán tôi nên thường xuyên dựa vào Điểm chuẩn hơn. ;-)
Nếu bạn biết chỉ số mảng, bạn có thể xóa () nó. Sự khác biệt giữa splice () và delete () là delete () không đánh số lại các phần tử còn lại của mảng.
Một mã tương tự mà tôi đã từng viết để loại bỏ các chuỗi không bắt đầu bằng SB.1 khỏi một mảng chuỗi
my @adoSymbols=('SB.1000','RT.10000','PC.10000');
##Remove items from an array from backward
for(my $i=$#adoSymbols;$i>=0;$i--) {
unless ($adoSymbols[$i] =~ m/^SB\.1/) {splice(@adoSymbols,$i,1);}
}