Perl中的闭包如何工作?

时间:2013-06-11 08:18:27

标签: perl closures

Perl中的新手再次来到这里,尝试理解Perl中的closure

所以这是一个我不理解的代码示例:

sub make_saying  {
    my $salute = shift;
    my $newfunc = sub {
        my $target = shift;
        print "$salute, $target!\n";
    };
    return $newfunc;            # Return a closure
}
$f = make_saying("Howdy");      # Create a closure
$g = make_saying("Greetings");  # Create another closure
# Time passes...
$f->("world");
$g->("earthlings");

所以我的问题是:

  1. 如果为某个函数分配了一个变量,它是否会自动引用该函数?
  2. 在上面的代码中,我可以改为编写$f = \make_saying("Howdy")吗?我什么时候可以使用&,因为我尝试使用它来传递参数(&$f("world")),但它不起作用。
  3. 最后,在上面的代码中,** worldearthlings中的单词如何附加到单词howdygreetings
  4. 注意:我知道$ f在某种程度上与参数howdy的函数绑定,因此我理解了world是如何附加的。我不明白的是里面的第二个功能。那个人如何运作它的魔力。对不起,我真的不知道怎么问这个。

3 个答案:

答案 0 :(得分:16)

在Perl中,标量变量不能直接保存子程序,它们只能保存引用。这非常类似于标量不能保存数组或哈希,只能包含arrayrefs或hashrefs。

sub { ... }评估为coderef,因此您可以直接将其分配给标量变量。如果要分配命名函数(例如foo),则必须获取\&foo之类的引用。

您可以调用$code->(@args)&$code(@args)等代码参数。

代码

$f = \make_saying("Howdy")

评估make_saying("Howdy"),并引用返回的值。所以你得到一个指向coderef的引用,而不是coderef本身。

因此,无法像&$f("world")那样调用它,您需要取消引用一个额外级别:&$$f("world")


闭包是绑定到特定环境的函数。

环境由所有当前可见的变量组成,因此闭包始终会记住此范围。在代码中

my $x;
sub foo {
  my $y;
  return sub { "$x, $y" };
}

foo$x的封闭,因为外部环境由$x组成。内部子是$x$y上的闭包。

每次执行foo时,我们都会得到一个新的$y,因此会有一个新的闭包。每次调用它时,都会返回不同的闭包。

执行make_saying("Howdy")时,$salute变量设置为Howdy。返回的闭包记住了这个范围。

当我们使用make_saying("Greetings")再次执行时,会再次评估make_saying的正文。 $salute现在设置为Greetings,内部子关闭此变量。此变量与先前存在的$salute不同,但除了通过第一个闭包之外无法访问。

这两个接待者已经关闭了单独的$salute个变量。当它们被执行时,它们各自的$salute仍然在范围内,并且它们可以访问和修改该值。

答案 1 :(得分:4)

  

如果变量与函数对齐,它是否自动为a   参考那个函数?

没有。在示例中,函数make_saying返回引用另一个函数。这样的闭包没有名称,可以从其范围之外捕获变量(在示例中为变量$salute)。

  

在上面的代码中,我可以写$ f = \ make_saying(“你好”)吗?   我什么时候可以使用&因为我尝试使用它来传递   参数(& $ f(“world”))但它不起作用。

没有。 $f = \make_saying("Howdy")不是您的想法(详情请参阅 amon 帖子)。您可以编写$f = \&make_saying;,这意味着“将$ f引用到函数make_saying”。您可以稍后使用它:

my $f = \&make_saying;
my $other_f = $f->("Howdy");
$other_f->("world");
  

最后,在上面的代码中,他怎么做了世界和   地球人被添加到了喜欢和问候的字样中。

make_saying 创建进入lamda(my $newfunc = sub)的 my 变量; lambda从 make_saying 返回。它通过“关闭”持有给定单词“你好”(?抱歉不知道用英语表示哪个单词)。

答案 2 :(得分:0)

每次调用子程序'make_saying'时,它: 1a - 创建一个不同的闭包 2a - 将接收到的参数分配给标量'$ salute' 3a - 声明(创建但不执行)内部匿名子例程:     这就是为什么此刻没有任何东西被分配给标量'$ target'也没有执行语句'print'$ salute,$ target!\ n“;' 4a - 最后子程序'make_saying'返回对内部匿名子程序的引用,这个引用成为调用(特定)匿名子程序的唯一方法。

有时你打电话给每个匿名子程序,它: 1b - 将接收到的参数分配给标量'$ target' 2b - 另请参阅标量'$ salute',它将在创建匿名子例程的时刻分配值(当被称为父子例程'make_saying'时 3b - 最后执行语句'print'$ salute,$ target!\ n“;'