#regex #perl
#регулярное выражение #perl
Вопрос:
Обнаружено странное поведение моего скрипта, когда левая часть представляет собой массив:
#!/usr/bin/perl
sub say {
print @_, "n";
}
my @arr = ('I', 'am', 'Qiang');
if (@arr =~ /Qiang/) {
say("1: Match!!!");
} else {
say("1: No match found");
}
@arr = ('Qiang');
if (@arr =~ /Qiang/) {
say("2: Match!!!");
} else {
say("2: No match found");
}
if (('Qiang') =~ /Qiang/) {
say("3: Match!!!");
} else {
say("3: No match found");
}
qxu@xqiang-mac-0:~/test$ ./regex_array_match.pl
1: No match found
2: No match found
3: Match!!!
Код во 2-м и 3-м случаях выглядит эквивалентно для меня, но результаты разные.
Что Perl должен делать, когда в левой части соответствующего оператора отображается массив?
Ответ №1:
=~
Оператор вычисляет свой левый операнд в скалярном контексте (ему нужна строка).
Массив в скалярном контексте выдает количество содержащихся в нем элементов, которое в вашем случае равно 3
и 1
, соответственно.
Таким образом, условия становятся "3" =~ /Qiang/
и "1" =~ /Qiang/
, оба из которых являются ложными.
Если вы use strict; use warnings;
(что вы всегда должны), вы увидите следующее предупреждение:
Applying pattern match (m//) to @arr will act on scalar(@arr)
… что perldoc perldiag
объясняет как:
(W разное) Операторы сопоставления с образцом (
//
), подстановки (s///
) и транслитерации (tr///
) работают со скалярными значениями. Если вы примените одно из них к массиву или хэшу, оно преобразует массив или хэш в скалярное значение (длина массива или общая информация хэша), а затем обработает это скалярное значение. Вероятно, это не то, что вы хотели сделать. Смотрите «grep» в perlfunc и «map» в perlfunc для альтернатив.
Чтобы проверить, соответствует ли какой-либо элемент массива шаблону регулярных выражений, вы можете использовать grep
следующее:
my @arr = ('I', 'am', 'Qiang');
if ( grep { /Qiang/ } @arr ) {
say("At least one match!!!");
} else {
say("No match found");
}
grep
фактически возвращает количество совпадающих элементов при вычислении в скалярном контексте, поэтому это менее эффективно, чем могло бы быть (он всегда будет проверять каждый элемент массива). Чтобы остановить проверку после обнаружения первого совпадения, используйте any
from List::Util .
Комментарии:
1. Спасибо, но в 3-м случае, разве это не все еще массив? Почему в этом случае есть совпадение? Массив стал плоским?
2. @QiangXu В 3-м случае массива нет. У вас есть простая строка в (избыточных) круглых скобках. Это похоже на запись
$x = (2 2)
; скобки не нужны, но они также не наносят никакого вреда.3. @QiangXu «массив» — это тип переменной, а не тип значения; круглые скобки не создают массивы. (хотя
[]
вернет ссылку на анонимный массив, но это совсем другое)4. @QiangXu в
my @arr = ('I', 'am', 'Qiang');
скобках используются только для переопределения приоритета (по умолчанию=
имеет более высокий приоритет, чем,
). В@arr = ('Qiang');
скобках являются избыточными, и вы могли бы просто написать@arr = 'Qiang';
.5. @QiangXu, Parens (
(...)
) не создает списки, а тем более массивы. Массивы — это тип переменной, в котором переменные не отображаются('Qiang')
. Parens просто влияет на приоритет.('Qiang')
и'Qiang'
оба вычисляются до скаляра строкиQiang
независимо от контекста.