Обнаружил странное поведение моего скрипта, когда левая часть представляет собой массив:
#!/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, когда в левой части оператора сопоставления появляется массив?
Оператор =~
оценивает свой левый операнд в скалярном контексте (ему нужна строка).
Массив в скалярном контексте дает количество содержащихся в нем элементов, что в вашем случае равно 3
и 1
соответственно.
Таким образом, условия становятся "3" =~ /Qiang/
и "1" =~ /Qiang/
, оба из которых ложны.
Если вы use strict; use warnings;
(что вы всегда должны делать), вы увидите следующее предупреждение:
Applying pattern match (m//) to @arr will act on scalar(@arr)
... который perldoc perldiag
объясняет как:
(W misc) The pattern match (
//
), substitution (s///
), and transliteration (tr///
) operators work on scalar values. If you apply one of them to an array or a hash, it will convert the array or hash to a scalar value (the length of an array, or the population info of a hash) and then work on that scalar value. This is probably not what you meant to do. See "grep" in perlfunc and "map" in perlfunc for alternatives.
Чтобы проверить, соответствует ли какой-либо элемент массива шаблону регулярного выражения, вы можете использовать grep
следующим образом:
my @arr = ('I', 'am', 'Qiang');
if ( grep { /Qiang/ } @arr ) {
say("At least one match!!!");
} else {
say("No match found");
}
grep
фактически вернет количество совпадающих элементов при оценке в скалярном контексте, поэтому это менее эффективно, чем могло бы быть (он всегда будет проверять каждый элемент массива). Чтобы прекратить проверку после того, как будет найдено первое совпадение, используйте any
из списка::Util.
@QiangXu В третьем случае массива нет. У вас есть простая строка в (лишних) круглых скобках. Это как писать $x = (2 + 2)
; скобки не нужны, но и вреда они не приносят.
@QiangXu «массив» — это тип переменной, а не тип значения; круглые скобки не создают массивы. (хотя []
вернет ссылку на анонимный массив, но это совсем другое)
@QiangXu В my @arr = ('I', 'am', 'Qiang');
скобки используются только для переопределения приоритета (по умолчанию =
имеет более высокий приоритет, чем ,
). В @arr = ('Qiang');
круглые скобки избыточны, и вы могли бы просто написать @arr = 'Qiang';
.
Добавил решение. Не стесняйтесь вернуться.
@QiangXu, Parens ((...)
) не создают списки, тем более массивы. Массивы — это тип переменной, и в ('Qiang')
нет переменных. Parens просто влияет на приоритет. ('Qiang')
и 'Qiang'
оба вычисляют строковый скаляр Qiang
независимо от контекста.
Спасибо, а в третьем случае разве это не массив? Почему в таком случае есть совпадение? Массив стал плоским?