Массив аргументов @_
ведет себя не так, как вы думаете. Значения @_
в подпрограмме на самом деле псевдонимы для реальных аргументов :
Массив @_ является локальным массивом, но его элементы являются псевдонимами для фактических скалярных параметров.
Когда вы говорите это:
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, от которых я всегда держался подальше, потому что мне не нравятся манипуляции с бритвенными лезвиями.