Как удалить элементы массива, равные некоторому элементу во втором массиве в 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";

Ответ 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.