Подтвердить что ты не робот

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

У меня есть список возможных значений:

@a = qw(foo bar baz);

Как я могу проверить кратким образом, что значение $val присутствует или отсутствует в @a?

Очевидная реализация - это цикл над списком, но я уверен, что TMTOWTDI.


Спасибо всем, кто ответил! Три ответа, которые я хотел бы выделить:

  • Принятый ответ - самый "встроенный" и обратный способ.

  • Ответ RET является самым чистым, но только хорошим для Perl 5.10 и более поздних версий.

  • ответ draegtun (возможно) немного быстрее, но требует использования дополнительного модуля. Мне не нравится добавлять зависимости, если я могу их избежать, и в этом случае вам не нужна разница в производительности, но если у вас есть список из 1000 000 элементов, вы можете попробовать этот ответ.

4b9b3361

Ответ 1

Perl bulit в функции grep() предназначен для этого.

@matches = grep( /^MyItem$/, @someArray ); 

или вы можете вставить любое выражение в матчи

@matches = grep( $_ == $val, @a ); 

Ответ 2

Если у вас есть perl 5.10, используйте оператор smart-match ~~

print "Exist\n" if $var ~~ @array;

Это почти волшебство.

Ответ 3

Ответ на perlfaq4 отвечает на "Как я могу указать, содержится ли какой-либо элемент в списке или массиве?" .

Чтобы выполнить поиск perlfaq, вы можете найти список всех вопросов в perlfaq, используя ваш любимый браузер.

В командной строке вы можете использовать ключ -q для perldoc для поиска ключевых слов. Вы бы нашли свой ответ, выполнив поиск "списка":

perldoc -q list

(часть этого ответа была предоставлена ​​Анно Зигелем и Брайан ди фой)

Слушание слова "in" означает указание, что вы, вероятно, должны использовать хэш, а не список или массив, для хранения ваших данных. Хеши предназначены для быстрого и эффективного ответа на этот вопрос. Массивы нет.

Таким образом, есть несколько способов приблизиться к этому. В Perl 5.10 и более поздних версиях вы можете использовать оператор smart match для проверки того, что элемент содержится в массиве или хеше:

use 5.010;

if( $item ~~ @array )
    {
    say "The array contains $item"
    }

if( $item ~~ %hash )
    {
    say "The hash contains $item"
    }

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

@blues = qw/azure cerulean teal turquoise lapis-lazuli/;
%is_blue = ();
for (@blues) { $is_blue{$_} = 1 }

Теперь вы можете проверить, $is_blue {$ some_color}. Возможно, было бы хорошей идеей держать блюз в хэше в первую очередь.

Если значения представляют собой все малые целые числа, вы можете использовать простой индексированный массив. Этот вид массива займет меньше места:

@primes = (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31);
@is_tiny_prime = ();
for (@primes) { $is_tiny_prime[$_] = 1 }
# or simply  @istiny_prime[@primes] = (1) x @primes;

Теперь вы проверяете, $is_tiny_prime [$ some_number].

Если значения, о которых идет речь, являются целыми числами вместо строк, вы можете сэкономить довольно много места, используя вместо этого битовые строки:

@articles = ( 1..10, 150..2000, 2017 );
undef $read;
for (@articles) { vec($read,$_,1) = 1 }

Теперь проверьте, является ли vec ($ read, $n, 1) истинным для некоторого $n.

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

Если вы тестируете только один раз, стандартный модуль List:: Util сначала экспортирует функцию для этой цели. Он работает, останавливаясь, когда находит элемент. Он написан на языке C для скорости, а его эквивалент Perl выглядит как эта подпрограмма:

sub first (&@) {
    my $code = shift;
    foreach (@_) {
        return $_ if &{$code}();
    }
    undef;
}

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

my $is_there = grep $_ eq $whatever, @array;

Если вы хотите извлечь нужные элементы, просто используйте grep в контексте списка.

my @matches = grep $_ eq $whatever, @array;

Ответ 4

Используйте первую функцию из List:: Util, которая входит в стандартную комплектацию с Perl....

use List::Util qw/first/;

my @a = qw(foo bar baz);
if ( first { $_ eq 'bar' } @a ) { say "Found bar!" }

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

Ответ 5

Одним из возможных подходов является использование функции List:: MoreUtils "any".

use List::MoreUtils qw/any/;

my @array = qw(foo bar baz);

print "Exist\n" if any {($_ eq "foo")} @array;

Обновить: исправлено на основе комментария zoul.

Ответ 6

Интересное решение, особенно для повторного поиска:

my %hash;
map { $hash{$_}++ } @a;
print $hash{$val};

Ответ 7

$ perl -e '@a = qw(foo bar baz);$val="bar";
if (grep{$_ eq $val} @a) {
  print "found"
} else {
  print "not found"
}'

найдено

$val='baq';

не найден

Ответ 8

Если вам не нравится ненужная зависимость, выполните any или first самостоятельно

sub first (&@) {
  my $code = shift;
  $code->() and return $_ foreach @_;
  undef
}

sub any (&@) {
  my $code = shift;
  $code->() and return 1 foreach @_;
  undef
}