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


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

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

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

11 202

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";
    }
}

вы можете проверить на числовую последовательность ведущих пространств