массив в левой части соответствующего оператора

#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 независимо от контекста.