Почему Perl оценивает код в ${...} во время интерполяции строк?
Почему следующий фрагмент работает вообще? И какое зло может быть возможно, используя это? А если серьезно, есть ли причина, код в ${}
оценивается вообще, а затем используется как скалярная ссылка?
use strict;
no strict 'refs';
our $message = "Hello world!";
print "${ lc 'MESSAGE' }\n";
3 ответа
Это нормально, если вы не используете символические ссылки. Предположим, следующий код:
my %messages = (hello => "Hello world!", bye => "Bye-bye, world!");
sub get_message_ref { return \$messages{$_[0]} }; # returns scalarref
print "${ get_message_ref('bye') }\n";
Согласитесь, его полезность не очевидна для scalarrefs, но она очень полезна для arrayrefs.
print "keys: @{[keys %messages]}\n";
Мы объясним это подробно в Промежуточном Perl.
Общий синтаксис для поиска переменных:
SIGIL BLOCK INDEXY-THING
Для простого скаляра, который выглядит так:
print $ { foo };
Вы, вероятно, видели это, когда вам нужно отделить имя переменной от окружающих ее вещей:
print "abc${foo}def\n";
Если у вас есть только идентификатор Perl в блоке и нет окружающего беспорядка, вы можете оставить скобки, что является распространенным случаем:
print $foo;
Тем не менее, это то же самое для разыменования ссылки:
SIGIL BLOCK-RETURNING-REFERENCE INDEXY-THINGS
Если вещь, которую вы получаете в блоке, является ссылкой, Perl пытается разыменовать ее так, как вы это просили:
my $ref = \ '12345';
print $ { $ref };
Это настоящий блок, а не только сахар. Вы можете иметь столько утверждений, сколько захотите:
print $ { my $ref = \ '1234'; $ref };
Теперь вы не просто указываете идентификатор Perl, поэтому Perl не предполагает, что вы даете ему идентификатор, и он выполняет код и использует результат в качестве ссылки. Рассмотрим разницу между этими почти идентичными say
заявления:
use 5.010;
our $foo = "I'm the scalar";
sub foo { \ "I'm the sub" }
say ${foo};
say ${foo;};
В эту секунду say
Perl видит точку с запятой, понимает, что это не идентификатор, интерпретирует код внутри фигурных скобок как текст и возвращает результат. Поскольку результат является ссылкой, он использует ${...}
разыменовать это. Неважно, где вы это делаете, так что вы делаете это внутри строки в двойных кавычках не является особенным.
Также обратите внимание на our
там. Это важно сейчас, когда вы собираетесь рассмотреть что-то более сложное:
use 5.010;
our $foo = "I'm the scalar";
sub foo { \ "I'm the sub" }
sub baz { 'foo' }
say ${foo};
say ${foo;};
say ${baz;};
Perl интерпретирует это say
как код и видит результат не является ссылкой; это простая строка foo
, Perl видит, что это не ссылка, но теперь она находится в контексте разыменования, поэтому делает символическую ссылку (как описывает Грег Бэкон). Поскольку символьные ссылки работают с переменными в таблице символов, это $foo
должен был быть переменной пакета.
Так как это легко испортить, strict
есть удобная проверка для этого. Однако, когда вы выключаете его, не удивляйтесь, когда он вас кусает.:)
Из раздела "Использование ссылок" документации perlref:
Везде, где вы бы поместили идентификатор (или цепочку идентификаторов) как часть имени переменной или подпрограммы, вы можете заменить идентификатор на БЛОК, возвращающий ссылку правильного типа. Другими словами, предыдущие примеры можно записать так:
$bar = ${$scalarref}; push(@{$arrayref}, $filename); ${$arrayref}[0] = "January"; ${$hashref}{"KEY"} = "VALUE"; &{$coderef}(1,2,3); $globref->print("output\n"); # iff IO::Handle is loaded
Следует признать, что в этом случае немного глупо использовать curlies, но BLOCK может содержать любое произвольное выражение, в частности, выражения с индексами:
&{ $dispatch{$index} }(1,2,3); # call correct routine
Из-за возможности опустить curlies для простого случая
$$x
люди часто делают ошибку, рассматривая разыменовывающие символы как правильные операторы, и задаются вопросом об их приоритете. Если бы они были, вы могли бы использовать скобки вместо фигурных скобок. Это не тот случай. Рассмотрим разницу ниже; Случай 0 - сокращенная версия случая 1, а не случая 2:$$hashref{"KEY"} = "VALUE"; # CASE 0 ${$hashref}{"KEY"} = "VALUE"; # CASE 1 ${$hashref{"KEY"}} = "VALUE"; # CASE 2 ${$hashref->{"KEY"}} = "VALUE"; # CASE 3
Случай 2 также обманчив, поскольку вы обращаетесь к переменной
%hashref
, не разыскивая через$hashref
к хешу это предположительно ссылается. Это будет случай 3.
Далее в "Символических ссылках":
Мы сказали, что ссылки возникают по мере необходимости, если они не определены, но мы не говорили, что произойдет, если значение, используемое в качестве ссылки, уже определено, но не является жесткой ссылкой. Если вы используете его как ссылку, он будет считаться символической ссылкой. То есть значением скаляра считается имя переменной, а не прямая ссылка на (возможно) анонимное значение.