Perl中的子程序内部嵌套子程序

7
我想在另一个子程序中嵌套一个子程序。
sub a {
    sub b {
    }
}

我想在每次调用sub a时创建一个新的sub b实例。在Perl中有没有办法做到这一点?
当我运行上面的代码并在sub a中打印sub b的地址时,我总是得到相同的sub b地址。
sub a {
    print \&b;
    sub b{
    }
}

这个Perl Monks上的链接说我们可以做到,但我总是看到每次对sub a的调用都会返回相同的地址

有没有一种方法可以在每次调用sub a时创建sub b的新实例?


4
请解释一下您想通过多个相同子程序实例实现什么目的。我相信Perl有适合您的解决方案,但也许不是您选择的方式。 - Borodin
4个回答

12
sub a {
    sub b{
    }
}

基本上与以下代码相同:

sub a {

}
sub b{
}

因为命名子例程存在于符号表中,因此它们是全局的。 你需要返回一个对子例程的引用。


它们在变量作用域的意义上是否“相同”? - FNia

8

命名子例程只会被创建一次。您需要返回一个匿名子例程引用,类似于以下内容:

sub a {
    my $counter = 1;
    return sub {
        return $counter++;
    }
}

my $c1 = a();
my $c2 = a();

# different references
print "c1 = $c1, c2 = $c2\n";

# each has a different counter
print "c1 ", $c1->(), "\n";
print "c1 ", $c1->(), "\n";
print "c2 ", $c2->(), "\n";
print "c2 ", $c2->(), "\n";

这不会创建一个新的匿名子例程实例。只有闭包是不同的。 - Borodin
1
但这是一个比其他任何东西都更好的闭包示例。 - vol7ron
1
@Borodin:在正式的计算机科学中,“闭包”由“子程序”(或“过程”或“函数”)和环境组成。但由于很少有编程语言允许程序员在闭包和它的子程序之间,或者在闭包中关闭其词法环境的子程序和没有这样做的子程序之间做出任何严格区分,因此在某种语言中,程序员通常将整个闭包称为“子程序”(或“函数”或其他类似术语)。更重要的是,大多数语言的官方定义/标准也采用了这种方式。 - ruakh
@ruakh:我在某种程度上同意你的看法,但从未见过子程序及其上值被如此随意地引用。你能给出引用吗?@adrianh所说的和我希望纠正的是:“命名子程序只会创建一次。你需要返回一个匿名子程序”。 - Borodin
@vol7ron: 我不明白。 这个解决方案最适合作为闭包的示例,还是这个解决方案是闭包的最佳示例? - Borodin
@Borodin:针对Perl闭包(保持子例程实例化),这是一个更好的答案,而不是创建一个新实例,因为问题所问。虽然,正如你所写的,我也不清楚被问到的是什么,并且希望MojoUser能够提供更多解释。 - vol7ron

7
你可以创建一个匿名子例程的引用:
#!/usr/bin/env perl
use strict;
use warnings;

sub a
{
    my($b) = @_;
    my $subref = sub { my($a) = @_; print "a = $a; b = $b\n"; return $a + $b; };
    &$subref(3);
    return $subref;
}

my $sub1 = a(10);
my $a10  = &$sub1(19);
my $sub2 = a(20);
my $a20  = &$sub2(20);
print "a10 = $a10; a20 = $a20; sub1 = $sub1; sub2 = $sub2\n";

示例输出:

a = 3; b = 10
a = 19; b = 10
a = 3; b = 20
a = 20; b = 20
a10 = 29; a20 = 40; sub1 = CODE(0x7ffc3c002eb8); sub2 = CODE(0x7ffc3c032eb8)

1
-1 代表多个糟糕的 Perl 编码实践和令人震惊的标识符。 - Borodin
2
@Borodin:你能解释一下吗?我没有注意到你所说的不良实践;尽管如此,我同意变量命名可以改进。 - vol7ron
1
@vol7ron:你绝对不能使用$a$b作为标识符,因为它们是sort运算符使用的内置名称。而使用&调用子例程已经很长时间是错误的。 - Borodin
4
@Borodin:关于 $a$b 的使用确实如此,但是原帖使用了它们,所以为了保持与问题相关,我认为在这里使用它们是可以接受的。而且我从未听说过使用 & 是错误的。程序员应该知道 &foo 不同于 &foo() 或者 foo(),但是我认为他在上面的使用是正确的。 - vol7ron

3
sub a {
    my $b = sub {
    };
    print \&$b;
}

或者使用 glob 函数:
sub a {
    local *b = sub {
    };
    print \&b;
}

6
+1,虽然我认为\&$b是一个相当复杂的拼写$b的方式。:-P - ruakh
1
我同意,但不想让事情变得混乱。 - vol7ron

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接