Почему Perl оценивает код в ${...} во время интерполяции строк?

StackOverflow https://stackoverflow.com/questions/2332815

  •  22-09-2019
  •  | 
  •  

Вопрос

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

use strict;
no strict 'refs';

our $message = "Hello world!";
print "${ lc 'MESSAGE' }\n";
Это было полезно?

Решение

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

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

По общему признанию, немного глупо использовать завитушки в этом случае, но БЛОК может содержать любое произвольное выражение, в частности, выражения с подписью:

&{ $dispatch{$index} }(1,2,3);    # call correct routine

Из-за возможности опустить завитушки для простого случая $$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.

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top