Perl,懒洋洋地评估字符串

时间:2010-06-03 17:58:13

标签: perl string lazy-evaluation

考虑以下Perl代码。

#!/usr/bin/perl

use strict;
use warnings;

$b="1";

my $a="${b}";

$b="2";

print $a;

该脚本显然会输出1。我希望它是$b的当前值。

Perl中最聪明的方法是如何实现这样的懒惰评估?我希望${b}保持“未替代”,直到需要$a

6 个答案:

答案 0 :(得分:15)

我更想知道你为什么要这样做。根据您真正需要做的事情,您可以使用各种方法。

您可以将代码包装在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;

答案 1 :(得分:4)

Perl将在代码运行时插入一个字符串,我不知道如何使它不这样做,缺少格式(这是丑陋的IMO)。但是,你可以做的是将“当代码运行时”更改为更方便的东西,方法是将字符串包装在sub中并在需要插入字符串时调用它...

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

或者,你可以做一些eval魔法,但它更具侵入性(你需要对字符串进行一些操作才能实现它)。

答案 2 :(得分:3)

你想要的不是懒惰评估,而是后期绑定。要在Perl中获取它,您需要使用eval

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

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

$number = 42;

eval $x;

print "val is now $val\n";

请注意,eval通常效率低,而且有条不紊。使用其他答案之一的解决方案几乎肯定会更好。

答案 3 :(得分:3)

正如其他人所提到的,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

(这将是tie my $a, 'Lazy', $b;

答案 4 :(得分:1)

你希望假装$ a指的是在使用$ a时评估的东西...你只能在$ a不是真正的标量时才这样做,它可以是一个函数(如cHao的答案)或者,在这个简单的例子中,引用了另一个变量

my $b="1";
my $a= \$b;
$b="2";
print $$a;

答案 5 :(得分:1)

  

我希望$ {b}保持“未替代”,直到需要$ a。

然后我建议避免使用字符串插值,而是使用sprintf,以便在需要时“插入”。

当然,在此基础上你可以tie快速(ish)和肮脏的东西:

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

也适用于更具异国情调的格式说明符。呸。