Как я могу проверить, содержит ли массив Perl определенное значение?
Я пытаюсь выяснить способ проверки существования значения в массиве без перебора массива.
Я читаю файл для параметра. У меня есть длинный список параметров, с которыми я не хочу иметь дело. Я поместил эти нежелательные параметры в массив @badparams
.
Я хочу прочитать новый параметр и если он не существует в @badparams
, обрабатывает его. Если он существует в @badparams
перейдите к следующей.
11 ответов:
просто превратите массив в хэш:
my %params = map { $_ => 1 } @badparams; if(exists($params{$someparam})) { ... }
вы также можете добавить больше (уникальных) параметров в список:
$params{$newparam} = 1;
и позже получить список (уникальных) параметров обратно:
@badparams = keys %params;
Best general purpose-особенно короткие массивы (1000 элементов или меньше) и кодеры, которые не уверены в том, какие оптимизации лучше всего подходят для их потребностей.
# $value can be any regex. be safe if ( grep( /^$value$/, @array ) ) { print "found it"; }
было упомянуто, что grep проходит через все значения, даже если первое значение в массиве совпадает. Это правда, однако grep по-прежнему очень быстро для большинства случаев. Если вы говорите о коротких массивах (менее 1000 элементов), то большинство алгоритмов будут довольно быстрыми в любом случае. Если вы говоря об очень длинных массивах (1 000 000 элементов) grep приемлемо быстр независимо от того, является ли элемент первым или средним или последним в массиве.
случаи оптимизации для более длинных массивов:
если Ваш массив отсортирован, используйте "двоичный поиск".
Если один и тот же массив повторно ищется много раз, скопируйте его в хэш, а затем проверьте хэш. Если память вызывает беспокойство, то переместите каждый элемент из массив в хэш. Более эффективная память, но уничтожает исходный массив.
Если одни и те же значения ищутся повторно внутри массива, лениво построить кэш. (при поиске каждого элемента сначала проверьте, сохранен ли результат поиска в сохраненном хэше. если результат поиска не найден в хэше, то найдите массив и поместите результат в сохраненный хэш, чтобы в следующий раз мы нашли его в хэше и пропустили поиск).
примечание: эти оптимизации будет быстрее при работе с длинными массивами. Не слишком оптимизируйте.
вы можете использовать функцию smartmatch в Perl 5.10 следующим образом:
для поиска литерального значения ниже будет делать трюк.
if ( "value" ~~ @array )
для скалярного поиска, делать ниже будет работать, как и выше.
if ($val ~~ @array)
для встроенного массива делать ниже, будет работать как выше.
if ( $var ~~ ['bar', 'value', 'foo'] )
на Perl 5.18 smartmatch помечен как экспериментальный, поэтому вам нужно отключить предупреждения, включив экспериментальные ПРАГМА, добавив в свой скрипт/модуль:
use experimental 'smartmatch';
альтернативно, если вы хотите избежать использования smartmatch-то, как сказал Аарон использовать:
if ( grep( /^$value$/, @array ) ) { #TODO: }
этот блог обсуждаются лучшие ответы на этот вопрос.
в качестве краткого резюме, если вы можете установить модули CPAN, то наиболее читаемые решения:
any(@ingredients) eq 'flour';
или
@ingredients->contains('flour');
однако более распространенной идиомой является:
any { $_ eq 'flour' } @ingredients
но, пожалуйста, не используйте ! Он вообще не выражает намерения вашего кода. Не используйте
~~
"умный матч" оператор: он сломан. И не используйтеgrep()
ни решение с хэшем:они перебирают весь список.
any()
остановится, как только он найдет вашу ценность.Проверьте сообщение в блоге для более подробной информации.
несмотря на то, что это удобно использовать, кажется, что решение для преобразования в хэш стоит довольно много производительности, что было проблемой для меня.
#!/usr/bin/perl use Benchmark; my @list; for (1..10_000) { push @list, $_; } timethese(10000, { 'grep' => sub { if ( grep(/^5000$/o, @list) ) { # code } }, 'hash' => sub { my %params = map { $_ => 1 } @list; if ( exists($params{5000}) ) { # code } }, });
выход теста:
Benchmark: timing 10000 iterations of grep, hash... grep: 8 wallclock secs ( 7.95 usr + 0.00 sys = 7.95 CPU) @ 1257.86/s (n=10000) hash: 50 wallclock secs (49.68 usr + 0.01 sys = 49.69 CPU) @ 201.25/s (n=10000)
@eakssjo это нарушается-меры, создающие хэши в цикле против создания регулярных выражений в цикле. Исправлена версия (плюс я добавил
List::Util::first
иList::MoreUtils::any
):use List::Util qw(first); use List::MoreUtils qw(any); use Benchmark; my @list = ( 1..10_000 ); my $hit = 5_000; my $hit_regex = qr/^$hit$/; # precompute regex my %params; $params{$_} = 1 for @list; # precompute hash timethese( 100_000, { 'any' => sub { die unless ( any { $hit_regex } @list ); }, 'first' => sub { die unless ( first { $hit_regex } @list ); }, 'grep' => sub { die unless ( grep { $hit_regex } @list ); }, 'hash' => sub { die unless ( $params{$hit} ); }, });
и результат (это для 100_000 итераций, в десять раз больше, чем в ответе @eakssjo):
Benchmark: timing 100000 iterations of any, first, grep, hash... any: 0 wallclock secs ( 0.67 usr + 0.00 sys = 0.67 CPU) @ 149253.73/s (n=100000) first: 1 wallclock secs ( 0.63 usr + 0.01 sys = 0.64 CPU) @ 156250.00/s (n=100000) grep: 42 wallclock secs (41.95 usr + 0.08 sys = 42.03 CPU) @ 2379.25/s (n=100000) hash: 0 wallclock secs ( 0.01 usr + 0.00 sys = 0.01 CPU) @ 10000000.00/s (n=100000) (warning: too few iterations for a reliable count)
метод 1: grep (может быть осторожным, пока значение, как ожидается, будет регулярным выражением).
старайтесь избегать использования
grep
, если смотреть на ресурсы.if ( grep( /^$value$/, @badparams ) ) { print "found"; }
Метод 2: Линейный Поиск
for (@badparams) { if ($_ eq $value) { print "found"; } }
Метод 3: Используйте хэш
my %hash = map {$_ => 1} @badparams; print "found" if (exists $hash{$value});
метод 4: smartmatch
(добавлено в Perl 5.10, отмечено экспериментально в Perl 5.18).
use experimental 'smartmatch'; # for perl 5.18 print "found" if ($value ~~ @badparams);
Метод 5: Используйте основной модуль
List::MoreUtils
use List::MoreUtils qw(any uniq);; @badparams = (1,2,3); $value = 1; print "found" if any {$_ eq $value} @badparams;
вы, конечно, хотите хэш здесь. Поместите плохие параметры в качестве ключей в хэш, а затем решите, существует ли конкретный параметр в хэше.
our %bad_params = map { $_ => 1 } qw(badparam1 badparam2 badparam3) if ($bad_params{$new_param}) { print "That is a bad parameter\n"; }
Если вы действительно заинтересованы в том, чтобы сделать это с массивом, посмотрите на
List::Util
илиList::MoreUtils
@files-это существующий массив
my @new_values = grep(/^2[\d].[\d][A-za-z]?/,@files); print join("\n", @new_values); print "\n";
/ ^2[\d].[\d][A-za-z]?/ = vaues начиная с 2 здесь можно поставить любое регулярное выражение
есть два способа сделать это. Вы можете использовать бросок значений в хэш для таблицы подстановки, как это предлагается в других сообщениях. ( Я добавлю еще одну идиому. )
my %bad_param_lookup; @bad_param_lookup{ @bad_params } = ( 1 ) x @bad_params;
но если это данные в основном буквы, а не слишком много мета, вы можете сбросить его в регулярное чередование:
use English qw<$LIST_SEPARATOR>; my $regex_str = do { local $LIST_SEPARATOR = '|'; "(?:@bad_params)"; }; # $front_delim and $back_delim being any characters that come before and after. my $regex = qr/$front_delim$regex_str$back_delim/;
это решение должно быть настроено для типов "плохих значений", которые вы ищете. И опять же, это может быть совершенно неуместно для некоторых типов строки, так предостережение emptor.
my @badparams = (1,2,5,7,'a','zzz'); my $badparams = join('|',@badparams); # '|' or any other character not present in params foreach my $par (4,5,6,7,'a','z','zzz') { if ($badparams =~ /\b$par\b/) { print "$par is present\n"; } else { print "$par is not present\n"; } }
вы можете проверить на числовую последовательность ведущих пространств