23

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

所以这是一个我不明白的代码示例:

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. 最后,在上面的代码中,他**如何执行单词world并附earthlings加到单词howdyand greetings

注意:我知道 $f 有点绑定到带有参数的函数,howdy所以这就是我理解如何world附加的。我不明白的是里面的第二个功能。那个人如何发挥它的魔力。对不起,我真的不知道怎么问这个。

4

3 回答 3

16

在 Perl 中,标量变量不能直接保存子程序,它们只能保存引用。这很像标量不能保存数组或散列,只能保存 arrayrefs 或 hashrefs。

评估sub { ... }为 coderef,因此您可以直接将其分配给标量变量。如果你想分配一个命名函数(例如foo),你必须获得像\&foo.

您可以调用 coderefs$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. 内部 sub 是$x和的闭包$y

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

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

当我们用 再次执行它时,会再次评估make_saying("Greetings")的主体。make_saying现在$salute设置为Greetings,并且内部子关闭此变量。这个变量与前一个变量是分开的,前一个变量$salute仍然存在,但除了通过第一个闭包外不可访问。

这两个迎宾员已经关闭了单独的$salute变量。当它们被执行时,它们各自$salute仍然在作用域内,它们可以访问和修改值。

于 2013-06-11T08:34:09.790 回答
4

如果将变量分配给函数,它是否会自动引用该函数?

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

在上面的代码中,我可以改写 $f = \make_saying("Howdy") 吗?我什么时候可以使用 & 因为我尝试使用它来传递参数 (&$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变量;该 lambda 是从make_saying返回的。它通过“关闭”保存给定的单词“你好”(?抱歉不知道英语中的哪个单词)。

于 2013-06-11T08:29:55.743 回答
0

每次调用子程序“make_saying”时,它:

  1. 创建一个不同的闭包

  2. 将接收到的参数分配给标量 '$salute'

  3. 定义(创建但不执行)一个内部匿名子例程:这就是为什么此时没有为标量分配任何内容, $target也没有执行语句的原因print "$salute, $target!\n";

  4. 最后,子例程“make_saying”返回对内部匿名子例程的引用,该引用成为调用(特定)匿名子例程的唯一方法。

每次调用每个匿名子例程时,它都会:

  1. 将接收到的参数分配给标量$target

  2. $salute还看到将具有在创建匿名子例程时分配的值的标量(当被称为其父子例程时make_saying

  3. finally 执行语句print "$salute, $target!\n";

于 2014-10-23T13:45:31.247 回答