Рассмотрите следующий код Perl.
#!/usr/bin/perl
use strict;
use warnings;
$b="1";
my $a="${b}";
$b="2";
print $a;
Сценарий, очевидно, выводы 1
. Я хотел бы, чтобы это было безотносительно текущего значения $b
.
Каков был бы самый умный путь в Perl для достижения отложенных вычислений как это? Я хотел бы ${b}
оставаться "незамененным" до $a
необходим.
Мне больше интересно знать, почему вы хотите это сделать. Вы можете использовать различные подходы в зависимости от того, что вам действительно нужно сделать.
Вы можете обернуть код в coderef и оценивать его только тогда, когда вам это нужно:
use strict; use warnings;
my $b = '1';
my $a = sub { $b };
$b = '2';
print $a->();
Вариантом этого было бы использование именоваемой функции в качестве closure (это, вероятно, лучший подход, в более широком контексте вызывающего кода):
my $b = '1';
sub print_b
{
print $b;
}
$b = '2';
print_b();
Вы можете использовать ссылку на исходную переменную и разыменовать ее по мере необходимости:
my $b = '1';
my $a = \$b;
$b = '2';
print $$a;
Как другие упоминали, Perl будет оценивать только строки в том виде, в котором вы их написали, используя eval
для вызова компилятора во время выполнения. Вы можете использовать ссылки, как указано в некоторых других ответах, но это меняет способ внешнего вида кода ( $$ a
vs $ a
). Однако, поскольку это Perl, есть способ скрыть расширенные функции за простой переменной, используя tie
.
{package Lazy;
sub TIESCALAR {bless \$_[1]} # store a reference to $b
sub FETCH {${$_[0]}} # dereference $b
sub STORE {${$_[0]} = $_[1]} # dereference $b and assign to it
sub new {tie $_[1] => $_[0], $_[2]} # syntactic sugar
}
my $b = 1;
Lazy->new( my $a => $b ); # '=>' or ',' but not '='
print "$a\n"; # prints 1
$b = 2;
print "$a\n"; # prints 2
Вы можете поискать в документации для tie
, но в двух словах он позволяет вам определять вашу собственную реализацию переменной (для скаляров, массивов, хэшей или дескрипторов файлов). Таким образом, этот код создает новую переменную $ a
с реализацией, которая получает или устанавливает текущее значение $ b
(путем сохранения внутренней ссылки на $ b
) . Метод new
не является строго необходимым (на самом деле конструктором является TIESCALAR
), но предоставляется как синтаксический сахар, чтобы избежать использования tie
непосредственно в вызывающем коде.
(что будет связать мой $ a, 'Lazy', $ b;
)
Perl будет интерполировать строку при запуске кода, и я не знаю способа заставить его этого не делать, за исключением форматов (которые уродливы IMO). Что вы могли бы сделать, так это изменить «когда код запускается» на что-то более удобное, заключив строку в подпрограмму и вызвав ее, когда вам понадобится интерполированная строка ...
$b = "1";
my $a = sub { "\$b is $b" };
$b = "2";
print &$a;
Или вы можете сделать некоторую магию eval , но это немного более навязчиво (вам нужно будет немного манипулировать строкой, чтобы добиться этого).
Вы хотите сделать вид, что $a относится к чему-то, что оценивается, когда используется $a... Вы можете сделать это только в том случае, если $a не является действительно скалярным, это может быть функция (как ответ cHao) или, в этом простом случае, ссылка на другую переменную
my $b="1";
my $a= \$b;
$b="2";
print $$a;
То, что вам нужно, это не ленивая оценка, а позднее связывание. Чтобы получить это в Perl, вам нужно использовать eval
.
my $number = 3;
my $val = "";
my $x = '$val="${number}"';
$number = 42;
eval $x;
print "val is now $val\n";
Имейте в виду, что eval
обычно неэффективен, а также методически ужасен. Почти наверняка вам лучше использовать решение из одного из других ответов.
Я бы хотел, чтобы ${b} оставался "незамещенным", пока не понадобится $a.
Тогда я бы рекомендовал отказаться от интерполяции строк, а использовать sprintf
, чтобы "интерполировать", когда это необходимо.
Конечно, на этой основе вы можете связать
что-нибудь быстрое и грязное:
use strict;
use warnings;
package LazySprintf;
# oh, yuck
sub TIESCALAR { my $class = shift; bless \@_, $class; }
sub FETCH { my $self = shift; sprintf $self->[0], @$self[1..$#$self]; }
package main;
my $var = "foo";
tie my $lazy, 'LazySprintf', '%s', $var;
print "$lazy\n"; # prints "foo\n"
$var = "bar";
print "$lazy\n"; # prints "bar\n";
Работает и с более экзотическими спецификаторами формата. Фу.