Ответ 1
Perl bulit в функции grep() предназначен для этого.
@matches = grep( /^MyItem$/, @someArray );
или вы можете вставить любое выражение в матчи
@matches = grep( $_ == $val, @a );
У меня есть список возможных значений:
@a = qw(foo bar baz);
Как я могу проверить кратким образом, что значение $val
присутствует или отсутствует в @a
?
Очевидная реализация - это цикл над списком, но я уверен, что TMTOWTDI.
Спасибо всем, кто ответил! Три ответа, которые я хотел бы выделить:
Принятый ответ - самый "встроенный" и обратный способ.
Ответ RET является самым чистым, но только хорошим для Perl 5.10 и более поздних версий.
ответ draegtun (возможно) немного быстрее, но требует использования дополнительного модуля. Мне не нравится добавлять зависимости, если я могу их избежать, и в этом случае вам не нужна разница в производительности, но если у вас есть список из 1000 000 элементов, вы можете попробовать этот ответ.
Perl bulit в функции grep() предназначен для этого.
@matches = grep( /^MyItem$/, @someArray );
или вы можете вставить любое выражение в матчи
@matches = grep( $_ == $val, @a );
Если у вас есть perl 5.10, используйте оператор smart-match ~~
print "Exist\n" if $var ~~ @array;
Это почти волшебство.
Ответ на 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;
Используйте первую функцию из List:: Util, которая входит в стандартную комплектацию с Perl....
use List::Util qw/first/;
my @a = qw(foo bar baz);
if ( first { $_ eq 'bar' } @a ) { say "Found bar!" }
NB. сначала возвращает первый элемент, который он находит, и поэтому ему не нужно перебирать полный список (что будет делать grep).
Одним из возможных подходов является использование функции List:: MoreUtils "any".
use List::MoreUtils qw/any/;
my @array = qw(foo bar baz);
print "Exist\n" if any {($_ eq "foo")} @array;
Обновить: исправлено на основе комментария zoul.
Интересное решение, особенно для повторного поиска:
my %hash;
map { $hash{$_}++ } @a;
print $hash{$val};
$ perl -e '@a = qw(foo bar baz);$val="bar";
if (grep{$_ eq $val} @a) {
print "found"
} else {
print "not found"
}'
найдено
$val='baq';
не найден
Если вам не нравится ненужная зависимость, выполните any
или first
самостоятельно
sub first (&@) {
my $code = shift;
$code->() and return $_ foreach @_;
undef
}
sub any (&@) {
my $code = shift;
$code->() and return 1 foreach @_;
undef
}