Цитата - захватить - вопрос - PullRequest
5 голосов
/ 14 мая 2011

Может кто-нибудь объяснить, почему я могу использовать $1 два раза и получать разные результаты?

perl -wle '"ok" =~ /(.*)/; sub { "huh?" =~ /(.*)/; print for @_ }->( "$1", $1 )'

(Найдено в: Как исключить подсовпадения в Perl? )

Ответы [ 3 ]

7 голосов
/ 14 мая 2011

Массив аргументов @_ ведет себя не так, как вы думаете. Значения @_ в подпрограмме на самом деле псевдонимы для реальных аргументов :

Массив @_ является локальным массивом, но его элементы являются псевдонимами для фактических скалярных параметров.

Когда вы говорите это:

sub s {
    "huh?" =~ /(.*)/;
    print for @_;
}

"ok" =~ /(.*)/;   
s("$1", $1);

$1 в первом аргументе s немедленно оценивается с помощью интерполяции строк, но второй аргумент не оценивается, просто отмечается, что второе значение в версии @_ подпрограммы - $1 (фактическая переменная $1, а не ее значение). Затем внутри s значение $1 изменяется вашим регулярным выражением. И теперь ваш @_ имеет псевдоним для строки "ok", за которым следует псевдоним для $1, эти псевдонимы разрешаются print в вашем цикле.

Если вы измените функцию следующим образом:

sub s {
    my @a = @_;
    "huh?" =~ /(.*)/;
    print for @a;
}

или даже это:

sub s {
    local $1;
    "huh?" =~ /(.*)/;
    print for @_;
}

Тогда вы получите две строки "ОК", которые вы ожидаете. Забавно (странно, не смешно, ха-ха), что эти две версии s дают ожидаемый результат по разным причинам. Версия my @a = @_; извлекает текущие значения псевдонимов в @_, прежде чем регулярное выражение получит в свои руки $1; local $1; версия локализует переменную $1 для подпрограммы, оставляя псевдоним в @_, ссылаясь на версию $1 извне подпрограммы:

Локальный изменяет перечисленные переменные, чтобы быть локальным по отношению к вмещающему блоку, файлу или eval.

Странности, подобные этой, являются причиной того, что вы всегда должны копировать значения пронумерованных переменных захвата регулярных выражений в ваши переменные как можно скорее, и почему вы хотите распаковать @_ в самом начале ваших функций (если вы не знаете, почему вы не хочу этого делать).

Надеюсь, я не слишком обдумал терминологию, это один из тех странных уголков Perl, от которых я всегда держался подальше, потому что мне не нравятся манипуляции с бритвенными лезвиями.

2 голосов
/ 14 мая 2011

В примере кода используются два факта:

  • Элементы массива @_ являются псевдонимами для фактических скалярных параметров.В частности, если обновляется элемент $_[0], обновляется соответствующий аргумент (и наоборот).
  • $1 - это глобальная переменная (хотя и динамически ограниченная текущим BLOCK), которая автоматически содержитподшаблон из () из последнего успешного сопоставления с образцом.

Первый аргумент подпрограммы - обычная строка ("ok").Второй аргумент - глобальная переменная $1.Но оно изменяется в результате успешного сопоставления с шаблоном внутри подпрограммы перед выводом аргументов.

1 голос
/ 14 мая 2011

Это происходит потому, что perl передает параметры по ссылке.

То, что вы делаете, похоже на:

my $a = 'ok';

sub foo {
  $a = 'huh?';
  print for @_;
}

my $b = $a;
foo($b, $a)

Когда вызывается sub foo, $ _ [1] фактически является псевдонимом для $ a, поэтому его значение изменяется при изменении $ a.

...