【问题标题】:tell perl compiler to cache values inside loop告诉 perl 编译器在循环内缓存值
【发布时间】:2012-08-13 18:26:25
【问题描述】:

有没有办法告诉/强制 perl 编译器在循环中缓存值?

当然我可以在循环之外创建我的值,但是随着代码复杂性的增加,我发现在循环内创建值更具可读性,尽管它们不会改变

例如:

my $key = shift;
my @input = @_;
my %output;
foreach(@input) {
    my $output_tmp = specialOperation($_);
    ...
    my $key_tmp = constantOperation($key);
    my $specialKey = specialOperation2($_,$key_tmp);
    ...
    $output{$specialKey} = $tmp;
}

$keyTmp 在每次迭代中都有相同的值,我希望编译器缓存它

在正则表达式中,您可以使用o-flag
有没有类似的东西,例如一个关键字,来完成这个?

【问题讨论】:

  • 只需在 foreach 循环之前调用my $key_tmp = constantOperation($key);
  • 如前所述,我尽量避免这种可能性

标签: perl optimization loops keyword


【解决方案1】:

如下修改代码并不会带来任何额外的复杂性:

my $key_tmp = constantOperation($key);
my %output
foreach ( @input ) {
    my $output_tmp = special_operation($_);
    my $specialKey = specialOperation2($_,$key_tmp);
    $output{$specialKey} = $tmp;
}

但是暂时接受你有一个合理的未公开论据来解释你为什么不想这样做,另一种可能性(如果 constantOperation 是一个纯函数)是使用 Memoize。

use Memoize;
memoize 'constantOperation';

my $key = shift;
my @input = @_;
my %output;
foreach( @input ) {
    my $output_tmp = special_operation;
    my $key_tmp = constantOperation($key);
    my $specialKey = specialOperation2($_,$key_tmp);
    $output{$specialKey} = $tmp;
}

后者并不能避免重复的函数调用,但确实会导致在名为 constantOperation 的函数内进行缓存,因此使用先前使用的参数的后续调用将提供缓存的结果。

回到第一个示例,您可以将“my”声明移出循环,然后在循环内使用如下内容:$key_tmp //= constantOperation($key);。这可能是这样的:

my $key = shift;
my @input = @_;
my %output;
my $key_tmp;
foreach( @input ) {
    my $output_tmp = special_operation;
    $key_tmp //= constantOperation($key);
    my $specialKey = specialOperation2($_,$key_tmp);
    $output{$specialKey} = $tmp;
}

这就是 Perl,总有另一种方法可以做到这一点。启用“状态”功能,并将my $key_tmp 更改为state $key_tmp。这可能是这样的:

use feature 'state';
my $key = shift;
my @input = @_;
my %output;
foreach( @input ) {
    my $output_tmp = special_operation;
    state $key_tmp = constantOperation($key);
    my $specialKey = specialOperation2($_,$key_tmp);
    $output{$specialKey} = $tmp;
}

【讨论】:

  • 是的 state 正是我正在寻找的。我不知道,也可以循环使用。
【解决方案2】:

在循环外声明$key_tmp,然后在循环内检查它是否已经被赋值,然后再重新计算:

my ($key, @input) = @_;
my %output;
my $key_tmp;

foreach(@input) {
    my $output_tmp = specialOperation($_);
    ...
    $key_tmp = constantOperation($key) unless $key_tmp;
    my $specialKey = specialOperation2($_,$key_tmp);
    ...
    $output{$specialKey} = $tmp;
}

【讨论】:

  • 这与 DavidO 使用 //= 编写的方式相同
  • @Hachi 不太一样;如果constantOperation 返回的可能合法值评估为布尔值“假”,$key_tmp = constantOpearation($key) unless $key_tmp; 将失败。 unless defined $key_tmp; 可能会更健壮(并且在功能上更接近我的建议)。他在这里的解决方案更像是$key_tmp ||= ....,而不是$key_tmp //= ....。我选择后者是因为我认为它不那么脆弱。
  • 你所做的被翻译成unless defined $key_tmp,我知道。我只是想说这是相同的方法,而不是相同的方法