Есть ли в Perl оператор "не в"?

Предположим, что у меня есть массив чисел, и я хочу убедиться, что все они попадают в один из множества (x, y, z). В настоящее время я проверяю, что следующее оценивается как 0:

scalar ( grep { $_ ne x && $_ ne y && $_ ne z } @arr )

Было просто интересно, не будет ли проще, если бы у нас в perl тоже были "IN" и "NOT IN" sql-подобные операторы.

scalar ( grep { $_ NOT IN (x,y,z) } @arr )

Или есть один уже?

Спасибо, Троица

Ответы

Ответ 1

Типичным способом решения этой проблемы является использование хэша:

my %set = map {$_ => 1} qw( x y z ); # add x, y and z to the hash as keys
                                     # each with a value of 1

my @not_in_set = grep {not $set{$_}} @arr;

Ответ 2

Библиотеки List::Util или List::MoreUtils очень полезны здесь для тестирования членства в списке, где вы не заботитесь о самих значениях, а просто о существовании. Они более эффективны, чем grep, потому что они перестают перемещаться по списку, как только будет найдено совпадение, что может реально ускорить работу с длинными списками. Кроме того, эти модули написаны в C/XS, что быстрее, чем любая реализация pure-perl.

use List::MoreUtils 'any';

my @list = qw(foo bar baz);

my $exists = any { $_ eq 'foo' } @list;
print 'foo ', ($exists ? 'is' : 'is not'), " a member of the list\n";

$exists = any { $_ eq 'blah' } @list;
print 'blah ', ($exists ? 'is' : 'is not'), " a member of the list\n";

(Если вы ограничены только использованием модулей, которые поставляются с ядром Perl, вы можете использовать first в List:: Util - он сначала отправлен с perl в 5.7.3.)

Ответ 3

Если вы используете Perl 5.10 или выше (или хотите использовать экспериментальную функцию в perl 5.18 и выше), оператор Smart Match выполнит именно то, что вы ищете.

# if Perl 5.18 or higher; otherwise not needed
no warnings 'experimental::smartmatch';

my @filter = qw(X Y Z);
my $not_in_filter = scalar grep { ! ($_ ~~ @filter) } @array;

Если фильтр и/или @array являются большими, это может быть медленным, однако, из-за того, что он является O (N ^ 2). В этом случае вы все равно можете использовать интеллектуальное сопоставление и просто изменить свой фильтр:

my %filter = map { $_ => 1 } qw(X Y Z);
my $not_in_filter = scalar grep { ! ($_ ~~ %filter) } @array;

Подробнее см. "Умное соответствие в деталях" в perldoc perlsyn.

Кроме того, если вам нужно поддерживать версии perl между 5.10 и 5.18, подумайте об использовании модуля cpan experimental. Это проверяет версию и включает в себя "никаких предупреждений", если она находит версию Perl, которая ее требует.

use experimental 'smartmatch';

Смотрите: https://search.cpan.org/~leont/experimental-0.016/lib/experimental.pm

Ответ 4

Если в массиве имеется менее нескольких миллионов различных предметов, вы также можете использовать метод учебника с использованием хэша:

my %seen;
@seen{ @arr } = (); # Create a key for every distinct value in @arr
delete @seen{ qw(x y z) }; # And remove the ones for x, y, and z

if (keys %seen) {
    # There was something in @arr that not x, y, or z
} else {
    # There wasn't
}

Ответ 5

my $target = 'bar';
my @look_in = ('foo','baz','bar','etc');
if( $target ~~ @look_in) {
    print "target is in array ";
}

~~ находится в массиве

my $target = 'bar';
my @look_in = ('foo','baz','bar','etc');
if( not $target ~~ @look_in) {
    print "target is not in array";
}

это называется smartmatch, некоторые рекомендуют не использовать их, но работа очень приятна со списком строк.

Ответ 6

use List::Member;
my $target = 'bar';
my @look_in = ('foo','baz','bar','etc');

if( member($target, @look_in) + 1) {
 print "It a member of the array\n";
}

Это может сделать трюк