Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there an easy way to localise (preserve) all "magic variables" like $1, $& etc.?

Tags:

perl

I know that in a subroutine in Perl, it's a very good idea to preserve the "default variable" $_ with local before doing anything with it, in case the caller is using it, e.g.:

sub f() {
    local $_;              # Ensure $_ is restored on dynamic scope exit
    while (<$somefile>) {  # Clobbers $_, but that's OK -- it will be restored
        ...
    }
}

Now, often the reason you use $_ in the first place is because you want to use regexes, which may put results in handy "magic" variables like $1, $2 etc. I'd like to preserve those variables too, but I haven't been able to find a way to do that.

All perlvar says is that @+ and @-, which $1 etc. seem to depend on internally, refer to the "last successful submatches in the currently active dynamic scope". But even that seems at odds with my experiments. Empirically, the following code prints "aXaa" as I had hoped:

$_ = 'a';
/(.)/;          # Sets $1 to 'a'
print $1;       # Prints 'a'
{
    local $_;   # Preserve $_
    $_ = 'X';
    /(.)/;      # Sets $1 to 'X'
    print $1;   # Prints 'X'
}
print $_;       # Prints 'a' ('local' restored the earlier value of $_)
print $1;       # Prints 'a', suggesting localising $_ does localise $1 etc. too

But what I find truly surprising is that, in my ActivePerl 5.10.0 at least, commenting out the local line still preserves $1 -- that is, the answer "aXXa" is produced! It appears that the lexical (not dynamic) scope of the brace-enclosed block is somehow preserving the value of $1.

So I find this situation confusing at best and would love to hear a definitive explanation. Mind you, I'd actually settle for a bulletproof way to preserve all regex-related magic variables without having to enumerate them all as in:

local @+, @-, $&, $1, $2, $3, $4, ...

which is clearly a disgusting hack. Until then, I will worry that any regex I touch will clobber something the caller was not expecting to be clobbered.

Thanks!

like image 757
j_random_hacker Avatar asked Feb 03 '23 10:02

j_random_hacker


1 Answers

Maybe you can suggest a better wording for the documentation. Dynamic scope means everything up to the start of the enclosing block or subroutine, plus everything up to the start of that block or subroutine call, etc. except that any closed blocks are excluded.

Another way to say it: "last successful submatches in the currently active dynamic scope" means there is implicitly a local $x=$x; at the start of each block for each variable.

Most of the mentions of dynamic scope (for instance, http://perldoc.perl.org/perlglossary.html#scope or http://perldoc.perl.org/perlglossary.html#dynamic-scoping) are approaching it from the other way around. They apply if you think of a successful regex as implicitly doing a local $1, etc.

like image 172
ysth Avatar answered May 22 '23 08:05

ysth