How do closures in Perl work?

2019-04-03 16:02发布

Newbie in Perl again here, trying to understand closure in Perl.

So here's an example of code which I don't understand:

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");

So my questions are:

  1. If a variable is assigned to a function, is it automatically a reference to that function?
  2. In that above code, can I write $f = \make_saying("Howdy") instead? And when can I use the & because I tried using that in passing the parameters (&$f("world")) but it doesn't work.
  3. and lastly, In that code above how in he** did the words world and earthlings get appended to the words howdy and greetings.

Note: I understand that $f is somewhat bound to the function with the parameter howdy so that's my understanding how the world got appended. What I don't understand is the 2nd function inside. How that one operates its magic. Sorry I really don't know how to ask this one.

标签: perl closures
3条回答
干净又极端
2楼-- · 2019-04-03 16:36

Every time you call the subroutine 'make_saying', it: 1a - create a DIFFERENT closure 2a - assign the received parameter to the scalar '$salute' 3a - declare (create but not execute) the inner anonymous subroutine: that the reason why at this moment nothing is assigned to the scalar '$target' nor is executed the statement 'print "$salute, $target!\n";' 4a - finally the subroutine 'make_saying' return a reference to the inner anonymous subroutine, this reference become the only way to call the (specific) anonymous subroutine.

Ever time you call each anonymous subroutine, it: 1b - assign the received parameter to the scalar '$target' 2b - see also the scalar '$salute' that will have the value assigned at the moment in which was created the anonymous subroutine (when was called its parent subroutine 'make_saying' 3b - finally execute the statement 'print "$salute, $target!\n";'

查看更多
仙女界的扛把子
3楼-- · 2019-04-03 16:37

In Perl, scalar variables cannot hold subroutines directly, they can only hold references. This is very much like scalars cannot hold arrays or hashes, only arrayrefs or hashrefs.

The sub { ... } evaluates to a coderef, so you can directly assign it to a scalar variable. If you want to assign a named function (e.g. foo), you have to obtain the reference like \&foo.

You can call coderefs like $code->(@args) or &$code(@args).

The code

$f = \make_saying("Howdy")

evaluates make_saying("Howdy"), and takes a reference to the returned value. So you get a reference that points to a coderef, not a coderef itself.

Therefore, it can't be called like &$f("world"), you need to dereference one extra level: &$$f("world").


A closure is a function that is bound to a certain environment.

The environment consists of all currently visible variables, so a closure always remembers this scope. In the code

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

foo is a closure over $x, as the outer environment consists of $x. The inner sub is a closure over $x and $y.

Each time foo is executed, we get a new $y and therefore a new closure. Each time it is called, a different closure is returned.

When we execute make_saying("Howdy"), the $salute variable is set to Howdy. The returned closure remembers this scope.

When we execute it again with make_saying("Greetings"), the body of make_saying is evaluated again. The $salute is now set to Greetings, and the inner sub closes over this variable. This variable is separate from the previous $salute, which still exists, but isn't accessible except through the first closure.

The two greeters have closed over separate $salute variables. When they are executed, their respective $salute is still in scope, and they can access and modify the value.

查看更多
做个烂人
4楼-- · 2019-04-03 16:42

If a variable is asigned to a function, is it automatically a reference to that function?

No. In example the function make_saying return reference another function. Such closures do not have name and can catch a variable from outside its scope (variable $salute in your example).

In that above code, can i write $f = \make_saying("Howdy") instead? And when can i use the & cause i tried using that in passing the parameters (&$f("world")) but it doesnt work.

No. $f = \make_saying("Howdy") is not what you think (read amon post for details). You can write $f = \&make_saying; which means "put into $f reference to function make_saying". You can use it later like this:

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

and lastly, In that code above how in he** did the words world and earthlings got appended to the words howdy and greetings.

make_saying creating my variable which goes into lamda (my $newfunc = sub); that lambda is returned from make_saying. It holds the given word "Howdy" through "closing" (? sorry dont know which word in english).

查看更多
登录 后发表回答