簡體   English   中英

Perl,延遲計算字符串

[英]Perl, evaluate string lazily

考慮下面的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->();

這種方法的一種變體是使用命名函數作為閉包 (在調用代碼的較大上下文中,這可能是最好的方法):

my $b = '1';
sub print_b
{
    print $b;
}

$b = '2';
print_b();

您可以使用對原始變量的引用,並根據需要取消引用:

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

當代碼運行時,Perl會插值一個字符串,我不知道一種使其不這樣做的方法,缺少格式(這是丑陋的IMO)。 但是,您可以做的是將“代碼運行時”更改為更方便的方式,方法是將字符串包裝在子代碼中,並在需要插入字符串時調用它。

$b = "1";
my $a = sub { "\$b is $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通常效率低下,而且方法上的殘酷。 幾乎可以肯定,使用其他答案之一中的解決方案會更好。

正如其他人提到的那樣,Perl將僅在使用eval在運行時調用編譯器編寫字符串時評估字符串。 您可以使用其他答案中指出的引用,但這會更改代碼的外觀( $$a$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的文檔,但總而言之,它允許您定義自己的變量實現(用於標量,數組,哈希或文件句柄)。 因此,此代碼使用實現獲取或設置$b的當前值(通過在內部存儲對$b的引用)來創建新變量$a 並不是必須要使用這種new方法(構造函數實際上是TIESCALAR ),但是它作為語法糖提供,以避免必須在調用代碼中直接使用tie

(將tie my $a, 'Lazy', $b;

您想假裝$ a指的是使用$ a時要評估的東西...您只能這樣做,如果$ a不是真正的標量,它可能是一個函數(如cHao的回答),或者用這種簡單的方式情況下,對另一個變量的引用

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

我希望$ {b}在需要$ a之前保持“不變”。

然后,我建議避開字符串插值,而不要使用sprintf ,以便在需要時“插值”。

當然,在此基礎上,您可以tie一些快速(ish)和骯臟的東西tie在一起:

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

也可以與其他格式的說明符一起使用。 uck

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM