perl closures and $_

2019-03-23 14:55发布

One of the first things I try to learn in an unfamiliar programming language is how it handles closures. Their semantics are often intertwined with how the language handles scopes and various other tricky bits so understanding them reveals several other aspects of the language. Plus, closures are a really powerful construct and often times cut down on the amount of boilerplate I have to type. So I was messing around with perl closures and I stumbled upon a little gotcha:

my @closures;
foreach (1..3) {
  # create some closures
  push @closures, sub { say "I will remember $_"; };
}
foreach (@closures) {
  # call the closures to see what they remember
  # the result is not obvious
  &{$_}();
}

When I wrote the above code I was expecting to see

I will remember 1
I will remember 2
I will remember 3

but instead I got I will remember CODE(0x986c1f0).

The above experiment revealed that $_ is very context dependent and if it appears in a closure then it's value is not fixed at the point of the closure's creation. It behaves more like a reference. What other gotchas should I be aware of when creating closures in perl?

2条回答
够拽才男人
2楼-- · 2019-03-23 15:32

$_ is a global variable and should not be used in closure. Before using it assign this to a lexically scoped variable as shown bewlow. This will produce expected o/p.

#!/usr/bin/perl -w

use strict;
my @closures;


foreach (1..3) {
   my $var = $_;
   push @closures, sub { print "I will remember $var"; };
}

foreach (@closures) {
  $_->();
  print "\n";
}
查看更多
forever°为你锁心
3楼-- · 2019-03-23 15:51

Closures only close over lexical variables; $_ is normally a global variable. In 5.10 and above, you can say my $_; to have it be lexical in a given scope (though in 5.18 this was retroactively declared to be experimental and subject to change, so better to use some other variable name).

This produces the output you expected:

use strict;
use warnings;
use 5.010;
my @closures;
foreach my $_ (1..3) {
  # create some closures
  push @closures, sub { say "I will remember $_"; };
}
foreach (@closures) {
  # call the closures to see what they remember
  # the result is not obvious
  &{$_}();
}
查看更多
登录 后发表回答