Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

perl closures and regexp variables

Tags:

perl

I found a possible bug in perl's closures and $1 regexp variables. Simply, they don't mix together.

Let's take this code.

use warnings;

while ("1234567890"=~/(.)/sg) {
    push @subs, sub{print $1;};
}

for (@subs) {$_->()}

You would imagine that perl will now print all the numbers - instead, I got 10 warnings from undefined $1.

Is it really a bug, or did I just missed something in perl documentation? Is there some reason, why should $1 be undefined and NOT being part of the closure?

like image 999
Karel Bílek Avatar asked Dec 07 '22 23:12

Karel Bílek


2 Answers

Perl has two separate but largely compatible variable systems. Global variables, which are in the symbol table, and lexical variables which are in scope bound lexical pads.

Global variables can be the target of a symbolic dereference and are subject to dynamic scope with local. Lexical variables (defined with my) can be closed over.

The regex match variables (and all of Perl's other special variables) are global variables in the symbol table, thus there is no way to close over them.

To fix this, just copy the value into a lexical:

use warnings;

while ("1234567890"=~/(.)/sg) {
    my $x = $1;                # creates a new lexical that the sub closes over
    push @subs, sub{print $x;};
}

for (@subs) {$_->()}
like image 109
Eric Strom Avatar answered Jan 05 '23 14:01

Eric Strom


I think the answer is similar to the answer to perl closures and $_. $1 is a global variable, too.

What you need to do is:

my $x = $1;
push @subs, sub{print $x;};
like image 25
Nylon Smile Avatar answered Jan 05 '23 13:01

Nylon Smile