带有函数闭包规则的perl foreach循环

Sna*_*ark 8 perl foreach closures

以下代码

#!/usr/bin/env perl

use strict;
use warnings;

my @foo = (0,1,2,3,4);

foreach my $i (@foo) {
    sub printer {
        my $blah = shift @_;
        print "$blah-$i\n";
    }

    printer("test");
}
Run Code Online (Sandbox Code Playgroud)

没有做我所期望的.

究竟发生了什么?(我希望它打印出来"test-0 \ntest-1 \ntest-2 \ntest-3 \ntest-4 \n")

Eri*_*rom 19

问题是sub name {...}构造不能像for循环中那样嵌套.

原因是因为一旦解析它们就会执行sub name {...}真正的手段BEGIN {*name = sub {...}}和开始块.所以子程序的编译和变量绑定发生在编译时,在for循环有机会运行之前.

你想要做的是创建一个匿名子程序,它将在运行时绑定它的变量:

#!/usr/bin/env perl

use strict;
use warnings;

my @foo = (0,1,2,3,4);

foreach my $i (@foo) {
    my $printer = sub {
        my $blah = shift @_;
        print "$blah-$i\n";
    };

    $printer->("test");
}
Run Code Online (Sandbox Code Playgroud)

打印

test-0
test-1
test-2
test-3
test-4
Run Code Online (Sandbox Code Playgroud)

据推测,在您的实际用例中,这些闭包将被加载到数组或散列中,以便以后可以访问它们.

您仍然可以使用带闭包的裸字标识符,但是您需要做一些额外的工作以确保在编译时名称可见:

BEGIN {
    for my $color (qw(red blue green)) {
        no strict 'refs';
        *$color = sub {"<font color='$color'>@_</font>"}
    }
}

print "Throw the ", red 'ball';  # "Throw the <font color='red'>ball</font>"
Run Code Online (Sandbox Code Playgroud)


yst*_*sth 7

埃里克斯特罗姆的回答是正确的,也许你想看到的,但没有详细介绍绑定.

关于词汇生命周期的简短说明:词汇在编译时创建,甚至在输入范围之前实际可用,如此示例所示:

my $i;
BEGIN { $i = 42 }
print $i;
Run Code Online (Sandbox Code Playgroud)

此后,当它们超出范围时,它们将在下一次进入范围之前变为不可用:

print i();
{
    my $i;
    BEGIN { $i = 42 }
    # in the scope of `my $i`, but doesn't actually
    # refer to $i, so not a closure over it:
    sub i { eval '$i' }
}
print i();
Run Code Online (Sandbox Code Playgroud)

在您的代码中,闭包$i在编译时绑定到初始词法.然而,foreach循环有点奇怪; 虽然my $i实际创建了一个词法,但foreach循环并没有使用它; 相反,它在每次迭代时将其别名为循环值之一,然后在循环之后将其恢复到其原始状态.因此,你的封闭是唯一引用原始词汇的东西$i.

略有变化表明更复杂:

foreach (@foo) {
    my $i = $_;
    sub printer {
        my $blah = shift @_;
        print "$blah-$i\n";
    }

    printer("test");
}
Run Code Online (Sandbox Code Playgroud)

这里,原始文件$i是在编译时创建的,并且闭包绑定到那个; 循环的第一次迭代设置它,但循环的第二次迭代创建一个$i与闭包无关的新关联.