Perl中的闭包机制是如何工作的?

enyaitl3  于 2022-11-15  发布在  Perl
关注(0)|答案(3)|浏览(147)

这里又是一个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.如果一个变量被赋值给一个函数,它是否自动成为对该函数的引用?
1.在上面的代码中,我可以写$f = \make_saying("Howdy")吗?什么时候可以使用&,因为我在传递参数(&$f("world"))时尝试使用它,但它不起作用。
1.最后,在上面代码中,字符串worldearthlings是如何附加到字符串HowdyGreetings上的。
注意:我知道$f在某种程度上绑定到了带参数"Howdy"的函数,所以这就是我对"world"是如何被附加的理解。我不明白的是里面的第二个函数。它是如何操作它的魔力的。抱歉,我真的不知道如何问这个。

e4yzc0pl

e4yzc0pl1#

在Perl中,标量变量不能直接保存子例程,它们只能保存引用。这很像标量不能保存数组或散列,只能保存arrayref或hashref。
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,从而得到一个新的闭包。最重要的是,当foo离开时,$y不会“超出作用域”,但它会“保持活动”,因为它仍然可以从返回的闭包访问。简而言之:$y是返回的闭包的“本地状态”。
当我们执行make_saying("Howdy")时,变量$salute被设置为Howdy,返回的闭包会记住这个作用域。
当我们用make_saying("Greetings")再次执行它时,make_saying的主体被再次求值。$salute现在被设置为Greetings,并且内部子函数在这个变量上结束。这个变量与之前的$salute是分开的,后者仍然存在,但只有通过第一个闭包才能访问。
两个greeter已经关闭了单独的$salute变量。当它们被执行时,它们各自的$salute仍然在作用域中,并且它们可以访问和修改值。

d4so4syb

d4so4syb2#

如果一个变量被赋值给一个函数,它是否自动成为对该函数的引用?
不可以。在示例中,函数make_saying返回引用了另一个函数。这种闭包没有名称,可以捕获作用域之外的变量(示例中的变量$salute)。
在上面的代码中,我可以写$f = \make_saying(“Howdy”)吗?什么时候可以使用&,因为我在传递参数(&$f(“world”))时尝试过使用它,但它不起作用。
不,$f = \make_saying("Howdy")不是你想的那样(阅读amonpost了解详情)。你可以写$f = \&make_saying;,意思是“放入$f对函数make_saying的引用”。你以后可以这样使用它:

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

最后,在上面的代码中,单词world和earthlings是如何附加到单词howdy和greatings上的。

make_saying创建my变量,该变量进入lambda(my $newfunc = sub);这个lambda是从make_saying返回的。它保存了从“关闭”到“你好”的给定单词(抱歉,不知道英语中的哪个单词)。

ego6inou

ego6inou3#

每次调用子例程“make_saying”时,它:
1.创建一个不同的闭包
1.将接收到的参数赋给标量“$alquet”
1.定义(创建但不执行)内部匿名子例程:这就是为什么在那个时刻没有给标量$target赋值,也没有执行语句print "$salute, $target!\n";的原因。
1.最后,子例程“make_saying”返回对内部匿名子例程的引用,该引用成为调用(特定的)匿名子例程的唯一方式。
每次调用每个匿名子例程时,它:
1.将接收到的参数分配给标量$target
1.还可以看到标量$salute,该标量将具有在创建匿名子例程时(调用其父子例程make_saying时)分配的值
1.最后执行语句print "$salute, $target!\n";

相关问题