Perl, оцените строку лениво

Рассмотрите следующий код Perl.

#!/usr/bin/perl

use strict;
use warnings;

$b="1";

my $a="${b}";

$b="2";

print $a;

Сценарий, очевидно, выводы 1. Я хотел бы, чтобы это было безотносительно текущего значения $b .

Каков был бы самый умный путь в Perl для достижения отложенных вычислений как это? Я хотел бы ${b} оставаться "незамененным" до $a необходим.

7
задан Mike 3 June 2010 в 17:58
поделиться

6 ответов

Мне больше интересно знать, почему вы хотите это сделать. Вы можете использовать различные подходы в зависимости от того, что вам действительно нужно сделать.

Вы можете обернуть код в 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;
15
ответ дан 6 December 2019 в 06:35
поделиться

Как другие упоминали, 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; )

3
ответ дан 6 December 2019 в 06:35
поделиться

Perl будет интерполировать строку при запуске кода, и я не знаю способа заставить его этого не делать, за исключением форматов (которые уродливы IMO). Что вы могли бы сделать, так это изменить «когда код запускается» на что-то более удобное, заключив строку в подпрограмму и вызвав ее, когда вам понадобится интерполированная строка ...

$b = "1";
my $a = sub { "\$b is $b" };
$b = "2";
print &$a;

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

4
ответ дан 6 December 2019 в 06:35
поделиться

Вы хотите сделать вид, что $a относится к чему-то, что оценивается, когда используется $a... Вы можете сделать это только в том случае, если $a не является действительно скалярным, это может быть функция (как ответ cHao) или, в этом простом случае, ссылка на другую переменную

my $b="1";
my $a= \$b;
$b="2";
print $$a;
1
ответ дан 6 December 2019 в 06:35
поделиться

То, что вам нужно, это не ленивая оценка, а позднее связывание. Чтобы получить это в Perl, вам нужно использовать eval.

my $number = 3;
my $val = "";

my $x = '$val="${number}"';

$number = 42;

eval $x;

print "val is now $val\n";

Имейте в виду, что eval обычно неэффективен, а также методически ужасен. Почти наверняка вам лучше использовать решение из одного из других ответов.

3
ответ дан 6 December 2019 в 06:35
поделиться

Я бы хотел, чтобы ${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";

Работает и с более экзотическими спецификаторами формата. Фу.

1
ответ дан 6 December 2019 в 06:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: