Предупреждение о переменных uninitailized несмотря на проверки фдоон


Я сегодня кажусь очень глупым, или у меня очень странная проблема. Пожалуйста, рассмотрите следующий код:

#!/usr/bin/perl

use strict;
use warnings;
use warnings::unused;
use warnings FATAL => 'uninitialized';

my ($String, $Pattern);

$Pattern = "pattern";
$String = "patternn";

if (($String =~ m/^([^ ]+).*$/s) &&
    defined($1) &&
    ($1 =~ m/^.*$Pattern$/s)) {

  print $1."n";
}

Этот код выдает следующее предупреждение (а затем останавливается из-за use warnings FATAL => 'uninitialized';):

Use of uninitialized value $1 in concatenation (.) or string at [...]
Я действительно не понимаю этого, потому что я проверяю, если $1 определен (инициализирован) перед его использованием. Виновником, по-видимому, является последняя строка условия (т. е. ($1 =~ m/^.*$Pattern$/s)). Если я оставлю это, предупреждение исчезнет.

Но почему? В соответствии с моим пониманием, эта линия должна просто тест $1, но не изменяйте его значение ($Pattern и остальная часть этого регулярного выражения не содержит скобок, поэтому $1 не следует повторно назначать возможное соответствие).

Хммм ... размышляя об этом, мне приходит в голову, что Perl может установить $1 и его коллеги в каждом случае, когда регулярное выражение совпадает, независимо от того, есть ли в нем скобки. Это объясняет его поведение. Это правда?

3 4

3 ответа:

Я создаю ответ из моих комментариев выше:

if (($String =~ m/^([^\ ]+).*$/s) && # If matching, this will set $1
    defined($1) &&                   # Here $1 is still defined
    ($1 =~ m/^.*$Pattern$/s)         # If matching, will reset all $1, .. vars
) {

Таким образом, всякий раз, когда регулярное выражение совпадает, оно сбрасывает все цифровые переменные (и другие переменные, связанные с регулярным выражением).

Единственное решение, которое я вижу, - это разбить условие и сохранить $1 в дополнительной переменной. Обычно хорошая вещь, во всяком случае.
if ($String =~ m/^([^\ ]+).*$/s and defined $1) {
    my $match = $1;
    if ($match =~ m/^.*$Pattern$/s) { ... }
}
Обратите внимание, что в этом случае вам даже не нужно проверять наличие defined $1, потому что если этот шаблон соответствует, $1 будет всегда определен. Это зависит от шаблон.

Perldoc perlvar :

$<digits> ($1, $2, ...)
        Contains the subpattern from the corresponding set of capturing
        parentheses from the last successful pattern match [...]

Perldoc perlvar :

Содержит подшаблон из соответствующего набора скобок захвата из последнего успешного сопоставления шаблонов, не считая шаблонов, сопоставленных во вложенных блоках,которые уже были выведены.

Поскольку второе совпадение является последним успешным совпадением шаблона, внутри блока if $1 находится undef.

Вы могли бы сделать:

my ($match) = ($String =~ m/^([^\ ]+).*$/s);
if (defined $match && $match =~ m/^.*$Pattern$/s) {
    print $match."\n";
}

Если вы не знаете, что делает сложное утверждение, разбейте его на части.

if( $String =~ m/^([^\ ]+).*$/s ){
    if( defined($1) ){
        if( $1 =~ m/^.*$Pattern$/s ){