Как я могу проверить, содержит ли массив Perl определенное значение? - PullRequest
218 голосов
/ 18 мая 2010

Я пытаюсь выяснить способ проверки существования значения в массиве без итерации по массиву.

Я читаю файл для параметра. У меня длинный список параметров, с которыми я не хочу иметь дело. Я поместил эти нежелательные параметры в массив @badparams.

Я хочу прочитать новый параметр и, если он не существует в @badparams, обработать его. Если оно существует в @badparams, перейдите к следующему прочтению.

Ответы [ 11 ]

208 голосов
/ 25 июня 2011

Лучшее общее назначение - особенно короткие массивы (1000 элементов или менее) и кодеры, которые не уверены в том, какие оптимизации лучше всего соответствуют их потребностям.

# $value can be any regex. be safe
if ( grep( /^$value$/, @array ) ) {
  print "found it";
}

Было упомянуто, что grep проходит через все значения, даже еслипервое значение в массиве совпадает.Это правда, однако grep все еще чрезвычайно быстр для большинства случаев .Если вы говорите о коротких массивах (менее 1000 элементов), то большинство алгоритмов в любом случае будут довольно быстрыми.Если вы говорите об очень длинных массивах (1 000 000 элементов), то grep является приемлемо быстрым независимо от того, является ли элемент первым, средним или последним в массиве.

Варианты оптимизации для более длинных массивов:

Если ваш массив отсортирован , используйте «двоичный поиск».

Если один и тот же массив неоднократно ищется много раз, скопируйтесначала в хеш, а затем проверяем хеш.Если речь идет о памяти, переместите каждый элемент из массива в хеш.Более эффективно использует память, но уничтожает исходный массив.

Если в массиве выполняется неоднократный поиск одних и тех же значений , лениво создайте кэш.(так как каждый элемент ищется, сначала проверьте, был ли результат поиска сохранен в постоянном хэше. Если результат поиска не найден в хэше, то ищите массив и поместите результат в постоянный хеш, чтобы в следующий раз мынайдите его в хэше и пропустите поиск).

Примечание: эти оптимизации будут быстрее только при работе с длинными массивами.Не переусердствуйте.

177 голосов
/ 18 мая 2010

Просто превратите массив в хеш:

my %params = map { $_ => 1 } @badparams;

if(exists($params{$someparam})) { ... }

Вы также можете добавить больше (уникальных) параметров в список:

$params{$newparam} = 1;

А позже вернуть список (уникальных) параметров:

@badparams = keys %params;
116 голосов
/ 13 декабря 2013

Вы можете использовать функцию 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:
}
42 голосов
/ 22 мая 2013

В этом блоге обсуждаются лучшие ответы на этот вопрос.

В качестве краткого резюме, если вы можете установить модули CPAN, то наиболее читаемые решения:

any(@ingredients) eq 'flour';

или

@ingredients->contains('flour');

Тем не менее, более распространенная идиома:

any { $_ eq 'flour' } @ingredients

Но, пожалуйста, не используйте функцию first()! Он не выражает цели вашего кода вообще. Не используйте оператор ~~ "Smart match": он не работает. И не используйте grep() ни решение с хешем: они перебирают весь список.

any() остановится, как только найдет ваше значение.

Проверьте сообщение в блоге для получения более подробной информации.

11 голосов
/ 22 ноября 2012

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

#!/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)
10 голосов
/ 18 декабря 2012

Тест @ 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)
5 голосов
/ 09 мая 2017

Метод 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;
2 голосов
/ 28 февраля 2018

@ 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, здесь вы можете поместить любое регулярное выражение

2 голосов
/ 18 мая 2010

Вы, конечно, хотите хеш здесь. Поместите неверные параметры в качестве ключей в хеш, затем решите, существует ли конкретный параметр в хэше.

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

0 голосов
/ 21 мая 2014
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";
    }
}

Возможно, вы захотите проверить постоянство числовых ведущих пробелов

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...