2010-06-03 48 views
7

考虑下面的Perl代码。的Perl,评估串懒洋洋地

#!/usr/bin/perl 

use strict; 
use warnings; 

$b="1"; 

my $a="${b}"; 

$b="2"; 

print $a; 

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

什么是Perl中最聪明的方式来达到延迟计算也是这样吗?我希望${b}保持“不更换”,直到需要$a

+0

我建议不要使用'$ a'和'$ b'来举例这样的例子,因为这些都是perl特性,* not * lexically作用域(如你在'$前面省略'my' b'在你的例子中)。 – pilcrow 2010-06-10 06:13:27

回答

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

+1是为了选择好的技术,但是对于你的第二个子程序来说,你需要在定义子程序之前定义'my $ b',否则它会绑定到全局$ b。 – 2010-06-03 18:51:10

+0

@安迪:好点! – Ether 2010-06-03 19:09:21

+0

我试图想出一种做快速和脏字符串替换的方法。你的coderef建议非常好,谢谢 – Mike 2010-06-03 23:07:45

4

的代码运行时Perl会插一个字符串,我不知道的方式,使之不这样做,短格式(这是丑陋的IMO)。你可以做什么,不过,是改变“当代码运行”的东西更方便,在副包裹串并调用它,当你需要插入串...

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

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

1

你想假装$ a是指当使用$ a时被评估的东西......如果$ a不是真正的标量,它可能是一个函数(如cHao的答案)或者,在这种简单的情况下,引用另一个变量

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

你想要的是不是懒惰的评价,但后期绑定。要使用Perl,你需要使用eval

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

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

$number = 42; 

eval $x; 

print "val is now $val\n"; 

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

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;

1

我想$ {B}保持 “未置换” 直到需要$一个。

然后,我建议避开字符串插值,而不是使用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"; 

厂采用较特殊的格式说明,太。呸。

+0

完全真棒'yuck':-) – 2010-06-10 06:34:26