Как удалить элементы массива, равные некоторому элементу во втором массиве в perl
Просто интересно, если мне даны два массива, A и B, как удалить/удалить те элементы в A, которые также можно найти в B? Каков наиболее эффективный способ сделать это?
А также, как частный случай, если B - результирующий массив после grep
на A, как это сделать? Конечно, в этом случае мы можем сделать grep
в отрицательном условии. Но есть ли что-то вроде добавления дополнения к массиву по отношению к другому в perl?
Спасибо.
Ответы
Ответ 1
В любое время, когда вы думаете о found in
, вы, вероятно, ищете хэш. В этом случае вы создадите хэш своих значений B. Затем вы будете grep A, проверяя хэш для каждого элемента.
my @A = 1..9;
my @B = (2, 4, 6, 8);
my %B = map {$_ => 1} @B;
say join ' ' => grep {not $B{$_}} @A; # 1 3 5 7 9
Как вы можете видеть, perl обычно не поддерживает какую-либо таблицу found in
сам по себе,
поэтому вы должны предоставить его. Вышеприведенный код можно легко обернуть в функцию, но для эффективности лучше всего сделать inline.
Ответ 2
Посмотрите методы none
, all
, part
, notall
, доступные через List:: MoreUtils. Вы можете выполнять практически любую операцию с помощью методов, доступных в этом модуле.
Там есть хороший учебник по Perl Training Australia
Ответ 3
Если вы попросите наиболее эффективный способ:
my @A = 1..9;
my @B = (2, 4, 6, 8);
my %x;
@x{@B} = ();
my @AminusB = grep !exists $x{$_}, @A;
Но вы заметите разницу между моим и Eric Strom решение только для больших входов.
Вы можете найти этот функциональный подход:
sub complementer {
my %x;
@x{@_} = ();
return sub { grep !exists $x{$_}, @_ };
}
my $c = complementer(2, 4, 6, 8);
print join(',', $c->(@$_)), "\n" for [1..9], [2..10], ...;
# you can use it directly of course
print join(' ', complementer(qw(a c e g))->('a'..'h')), "\n";
Ответ 4
Вероятно, вам лучше с хешем, но вы также можете использовать умное сопоставление. Кража Пример Эрика Стром,
my @A = 1..9;
my @B = (2, 4, 6, 8);
say join ' ' => grep {not $_ ~~ @B } @A; # 1 3 5 7 9
Ответ 5
Опять же, вы, вероятно, лучше с хешем, но вы также можете использовать Perl6::Junction. Снова украл пример Эрика Стром,
use Perl6::Junction qw(none);
my @A = 1..9;
my @B = (2, 4, 6, 8);
say join ' ' => grep {none(@B) == $_} @A; # 1 3 5 7 9
Ответ 6
Как уже упоминалось Эрик Стром, всякий раз, когда вам нужно искать что-то конкретное, всегда легче, если у вас есть хэш.
Эрик имеет более приятное решение, но может быть трудно понять. Я надеюсь, что мое легче понять.
# Create a B Hash
my %BHash;
foreach my $element (@B) {
$BHash{$element} = 1;
}
# Go through @A element by element and delete duplicates
my $index = 0;
foreach my $element (@A) {
if (exists $BHash{$element}) {
splice @A, $index, 1; #Deletes $A[$index]
$index = $index + 1;
}
}
В первом цикле мы просто создаем хэш, который вводится элементами в @B
.
Во втором цикле мы проходим через каждый элемент в @A
, отслеживая индекс в @A
.