Как проверить, содержит ли массив Perl определенное значение?
Я пытаюсь выяснить способ проверки существования значения в массиве без итерации через массив.
Я читаю файл для параметра. У меня есть длинный список параметров, с которыми я не хочу иметь дело. Я разместил эти нежелательные параметры в массиве @badparams
.
Я хочу прочитать новый параметр, и если он не существует в @badparams
, обработайте его. Если он существует в @badparams
, перейдите к следующему чтению.
Ответы
Ответ 1
Просто превратите массив в хэш:
my %params = map { $_ => 1 } @badparams;
if(exists($params{$someparam})) { ... }
Вы также можете добавить в список дополнительные (уникальные) параметры:
$params{$newparam} = 1;
И позже получите список (уникальных) параметров назад:
@badparams = keys %params;
Ответ 2
Лучшее общее назначение - особенно короткие массивы (1000 единиц или менее) и кодеры, которые не уверены в том, какие оптимизации наилучшим образом соответствуют их потребностям.
# $value can be any regex. be safe
if ( grep( /^$value$/, @array ) ) {
print "found it";
}
Было упомянуто, что grep проходит через все значения, даже если первое значение в массиве совпадает. Это верно, однако grep по-прежнему чрезвычайно быстрый для большинства случаев. Если вы говорите о коротких массивах (менее 1000 элементов), то большинство алгоритмов будет довольно быстрым в любом случае. Если вы говорите о очень длинном массиве (1 000 000 элементов), grep является приемлемо быстрым, независимо от того, является ли элемент первым или средним или последним в массиве.
Примеры оптимизации для более длинных массивов:
Если ваш массив отсортирован, используйте "двоичный поиск".
Если тот же массив неоднократно просматривается многократно, сначала скопируйте его в хэш, а затем проверьте хэш. Если память является проблемой, переместите каждый элемент из массива в хэш. Больше памяти эффективнее, но уничтожает исходный массив.
Если одинаковые значения будут повторно выполняться в массиве, ленивно создайте кеш. (по мере поиска каждого элемента, сначала проверьте, сохранен ли результат поиска в сохраненном хэше. Если результат поиска не найден в хэше, тогда найдите массив и поместите результат в сохраненный хеш, чтобы в следующий раз найдите его в хеше и пропустите поиск).
Примечание. Эти оптимизации будут только быстрее при работе с длинными массивами. Не превышайте оптимизацию.
Ответ 3
Вы можете использовать функцию smartmatch в Perl 5.10 следующим образом:
Для выполнения литерала, выполняемого ниже, будет выполняться трюк.
if ( "value" ~~ @array )
Для скалярного поиска выполнение ниже будет работать, как указано выше.
if ($val ~~ @array)
Для встроенного массива, сделанного ниже, будет работать, как указано выше.
if ( $var ~~ ['bar', 'value', 'foo'] )
В Perl 5.18 smartmatch помечен как экспериментальный, поэтому вам нужно отключить предупреждения, включив experimental прагму, добавив ниже к вашему script/module
use experimental 'smartmatch';
Альтернативно, если вы хотите избежать использования smartmatch - тогда, как сказал Аарон, используйте:
if ( grep( /^$value$/, @array ) ) {
#TODO:
}
Ответ 4
В этом сообщении в блоге обсуждаются лучшие ответы на этот вопрос.
В качестве краткого резюме, если вы можете установить модули CPAN, наиболее читаемыми являются:
any(@ingredients) eq 'flour';
или
@ingredients->contains('flour');
Однако более распространенная идиома такова:
any { $_ eq 'flour' } @ingredients
Но, пожалуйста, не используйте функцию first()
! Он не отражает намерения вашего кода вообще. Не используйте оператор ~~
"Smart match": он сломан. И не используйте grep()
или решение с хешем: они перебирают весь список.
any()
остановится, как только он найдет ваше значение.
Подробнее читайте в блоге.
Ответ 5
Несмотря на то, что он удобен в использовании, похоже, что решение конвертирования в хеш файл стоит довольно много производительности, что было проблемой для меня.
#!/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)
Ответ 6
тест @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)
Ответ 7
Способ 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;
Ответ 8
Вы, конечно, хотите хэш здесь. Поместите плохие параметры в виде ключей в хеш, а затем определите, существует ли конкретный параметр в хеше.
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
Ответ 9
Есть два способа сделать это. Вы можете использовать броски значений в хеш для таблицы поиска, как это было предложено другими сообщениями. (Я добавлю еще один идиом.)
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.
Ответ 10
@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 здесь, вы можете поместить любое регулярное выражение
Ответ 11
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";
}
}
Возможно, вы захотите проверить согласованность конечных пробелов