2010-06-03 52 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