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

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

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

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

11 ответов


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

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

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

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

$params{$newparam} = 1;

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

@badparams = keys %params;

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

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

было упомянуто, что grep проходит через все значения, даже если первое значение в массиве совпадает. Однако это правда!--4-->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: Используйте core module 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";
    }
}

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