Почему Perl оценивает код в $ {...} во время интерполяции строк? - PullRequest
6 голосов
/ 25 февраля 2010

Почему следующий фрагмент работает вообще? И какое зло может быть возможно, используя это? А если серьезно, есть ли какая-то причина, код в ${} вычисляется вообще, а затем используется как скалярная ссылка?

use strict;
no strict 'refs';

our $message = "Hello world!";
print "${ lc 'MESSAGE' }\n";

Ответы [ 3 ]

13 голосов
/ 26 февраля 2010

Мы объясним это подробно в Промежуточный 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 есть удобная проверка. Однако, когда вы выключаете его, не удивляйтесь, когда он вас кусает. :)

5 голосов
/ 25 февраля 2010

Из раздела «Использование ссылок» документации perlref :

Везде, где вы бы поместили идентификатор (или цепочку идентификаторов) как часть имени переменной или подпрограммы, вы можете заменить идентификатор на BLOCK, возвращающий ссылку правильного типа. Другими словами, предыдущие примеры можно записать так:

$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.

Позже в "Символических ссылках":

Мы сказали, что ссылки возникают по мере необходимости, если они не определены, но мы не говорили, что произойдет, если значение, используемое в качестве ссылки, уже определено, но не является жесткой ссылкой. Если вы используете его как ссылку, он будет считаться символической ссылкой. То есть значением скаляра считается имя переменной, а не прямая ссылка на (возможно) анонимное значение.

3 голосов
/ 25 февраля 2010

Все в порядке, , если вы не используете символические ссылки . Предположим, следующий код:

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";
...