expresspotato has asked for the wisdom of the Perl Monks concerning the following question:

Hi Guys & Gals (just kidding). The following code is able to localize a variable by its string name.
$var = "count"; local $$var;
However when going through a list of variable names to localize from an array, the following does not localize the variables as one (me) would expect.
foreach (@vars){ $_ =~ s/main:://g; if ($_ !~ m/::/){ if ($_ =~ m/^[a-z]/){ local $$_ = $$_; } } }
EDIT: Now I see why it was not working. The variable was then localized to the foreach loop. Hm. Any advice on how I can localize a list of variables in the current scope would be great.

Replies are listed 'Best First'.
Re: Local $$_?
by Corion (Patriarch) on Mar 15, 2010 at 08:48 UTC

    What is it that you actually want to do? It seems to me that you're passing around names of global variables and you want to change them locally, which strikes me as an overall bad idea, at least because I can't imagine a use case for that where it wouldn't be better to just pass around a lexical hash of the values.

    If you are bent on modifying the global variables, I would go for writing code to localize these variables and holding on to a ReleaseAction or Scope::Guard to trigger the restoral of the values. Potentially, you can do evil magic by using Sub::Uplevel or Scope::Upper to get at the scope surrounding your for loop (or higher), but before you do that, you should think hard and long whether you really want such action at a distance at all.

Re: Local $$_?
by shmem (Chancellor) on Mar 15, 2010 at 12:31 UTC
    The following code is able to localize a variable by its string name.

    Indeed. Are you trying to shoot yourself in the foot with a gatling gun? See Why it's stupid to `use a variable as a variable name'.

    Localizing global variables (which should be avoided anyways for dynamic data) isn't confined to the lexical scope, but propagated to any inner and called scope, i.e. subroutines (see my/local, space/time (was: Re: The difference between my and local)). Tracking down bugs related to localizing is always great fun.

    Any advice on how I can localize a list of variables in the current scope would be great.

    Don't - unless you have a very good reason to do so. Use my variables, or use a hash to pass data around.

Re: Local $$_?
by LanX (Saint) on Mar 15, 2010 at 10:10 UTC
    I really doubt the underlying concept of this question...

    Anyway I think you can solve it by passing a list to local.

    Since all package variables are organized in a "stash" (symbol table hash) you can try to localize a hash-slice from the stash your addressing.

    DB<1> $a=10;$b=1111 DB<2> @names=qw/a b/ DB<3> local @::{@names}; $b=666;print $b 666 DB<4> print $a,$b 101111

    (Every line in the debugger has it's own scope due to the evaling mechanism!)

    Please note: Like this your localizing the whole globes *a and *b meaning that variables like @a will be localized, too!!!

    Cheers Rolf

      This is weird. I tried that as well, but it doesn't work for me:

      Q:\>perl -le "@l=qw(a b c);$b=3;do{local @::{@l};$b=2;print $b};print +$b 2 2 Q:\>perl -v This is perl, v5.8.8 built for MSWin32-x86-multi-thread (with 50 registered patches, see perl -V for more detail) Copyright 1987-2006, Larry Wall Binary build 820 [274739] provided by ActiveState http://www.ActiveSta +te.com Built Jan 23 2007 15:57:46 Perl may be copied only under the terms of either the Artistic License + or the GNU General Public License, which may be found in the Perl 5 source ki +t. Complete documentation for Perl, including FAQ lists, should be found +on this system using "man perl" or "perldoc perl". If you have access to + the Internet, point your browser at http://www.perl.org/, the Perl Home Pa +ge.

      On 5.10.0, it starts working:

      G:\Aktivitaeten\IDV\wp2-XML-sst\OUTPUT>perl -le "@l=qw(a b c);$b=3;do{ +local @::{@l};$b=2;print $b};print $b" 2 3 G:\Aktivitaeten\IDV\wp2-XML-sst\OUTPUT>perl -v This is perl, v5.10.0 built for MSWin32-x86-multi-thread Copyright 1987-2007, Larry Wall Perl may be copied only under the terms of either the Artistic License + or the GNU General Public License, which may be found in the Perl 5 source ki +t. Complete documentation for Perl, including FAQ lists, should be found +on this system using "man perl" or "perldoc perl". If you have access to + the Internet, point your browser at http://www.perl.org/, the Perl Home Pa +ge.

      So, I should test my ideas not with the stock Perl but with my "most recent" Perl available :)

        Indeed weird ...

        For the records:

        lanx@nc10-ubuntu:~$ perl -version This is perl, v5.10.0 built for i486-linux-gnu-thread-multi

        But I thought I already saw similar things in old and productive code...

        Cheers Rolf

      Good morning. Thanks for your reply. local @::{@names}; Does infact work, however is there a way to also assign the previous value at the same time? Similar to local $$_ = $$_;?
        local @::{@names} = @::{@names}

        should carry over the values, but on this 5.10.0, it exhibits inconsitent behaviour:

        Saving the values keeps the localizing behaviour:

        >perl -le "@l=qw(a b c);$b=3;do{my@v=@::{@l};local @::{@l};print $b;$b +=2;print $b};print $b" 2 3

        "Resetting" the values destroys the localizing behaviour:

        >perl -le "@l=qw(a b c);$b=3;do{my@v=@::{@l};local @::{@l}=@v;print $b +;$b=2;print $b};print $b" 3 2 2

        The same failure with the direct assignment:

        >perl -le "@l=qw(a b c);$b=3;do{local @::{@l} = @::{@l};print $b;$b=2; +print $b};print $b" 3 2 2

        I would consider this inconsistency a bug, but maybe it's already fixed in 5.10.1 or 5.11.

        This is perl, v5.10.0 built for MSWin32-x86-multi-thread Copyright 1987-2007, Larry Wall Perl may be copied only under the terms of either the Artistic License + or the GNU General Public License, which may be found in the Perl 5 source ki +t. Complete documentation for Perl, including FAQ lists, should be found +on this system using "man perl" or "perldoc perl". If you have access to + the Internet, point your browser at http://www.perl.org/, the Perl Home Pa +ge. G:\Aktivitaeten\IDV\wp2-XML-sst\OUTPUT>perl -V Summary of my perl5 (revision 5 version 10 subversion 0) configuration +: Platform: osname=MSWin32, osvers=5.1, archname=MSWin32-x86-multi-thread uname='' config_args='undef' hint=recommended, useposix=true, d_sigaction=undef useithreads=define, usemultiplicity=define useperlio=define, d_sfio=undef, uselargefiles=define, usesocks=und +ef use64bitint=undef, use64bitall=undef, uselongdouble=undef usemymalloc=n, bincompat5005=undef Compiler: cc='gcc', ccflags =' -s -O2 -DWIN32 -DHAVE_DES_FCRYPT -DPERL_IMPL +ICIT_CONTEXT -DPERL_IMPLICIT_SYS -fno-strict-aliasing -DPERL_M SVCRT_READFIX', optimize='-s -O2', cppflags='-DWIN32' ccversion='', gccversion='3.4.5', gccosandvers='' intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234 d_longlong=undef, longlongsize=8, d_longdbl=define, longdblsize=12 ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='long lo +ng', lseeksize=8 alignbytes=8, prototype=define Linker and Libraries: ld='g++', ldflags ='-s -L"G:\Systeme\Tools\Perl-5.10.0\perl\lib\CO +RE" -L"G:\Systeme\Tools\Perl-5.10.0\c\lib"' libpth=C:\strawberry\c\lib libs= -lmsvcrt -lmoldname -lkernel32 -luser32 -lgdi32 -lwinspool - +lcomdlg32 -ladvapi32 -lshell32 -lole32 -loleaut32 -lnetapi32 - luuid -lws2_32 -lmpr -lwinmm -lversion -lodbc32 -lodbccp32 perllibs= -lmsvcrt -lmoldname -lkernel32 -luser32 -lgdi32 -lwinspo +ol -lcomdlg32 -ladvapi32 -lshell32 -lole32 -loleaut32 -lnetapi 32 -luuid -lws2_32 -lmpr -lwinmm -lversion -lodbc32 -lodbccp32 libc=-lmsvcrt, so=dll, useshrplib=true, libperl=libperl510.a gnulibc_version='' Dynamic Linking: dlsrc=dl_win32.xs, dlext=dll, d_dlsymun=undef, ccdlflags=' ' cccdlflags=' ', lddlflags='-mdll -s -L"G:\Systeme\Tools\Perl-5.10. +0\perl\lib\CORE" -L"G:\Systeme\Tools\Perl-5.10.0\c\lib"' Characteristics of this binary (from libperl): Compile-time options: MULTIPLICITY PERL_DONT_CREATE_GVSV PERL_IMPLICIT_CONTEXT PERL_IMPLICIT_SYS PERL_MALLOC_WRAP PL_OP_SLAB_ALLOC USE_ITHREADS USE_LARGE_FILES USE_PERLIO Built under MSWin32 Compiled at Dec 21 2007 14:11:41 @INC: g:/Systeme/tools/perl-5.10.0/perl/lib g:/Systeme/tools/perl-5.10.0/perl/site/lib .
        local @::{@names} = @::{@names};
        I fear your reasons for doing this
        Now you can use a loop or a temporary array to set whatever values you want!

        Cheers Rolf

Re: Local $$_?
by expresspotato (Beadle) on Mar 15, 2010 at 15:36 UTC
    If anyone is at all curious, or experiencing the same problem the code is below. Take what you will from it - a hacked job - but it does work and do what I need it to. For a page that is called directly by a user's browser. EG: index.pl
    use FCGI; do("./localizer.pl"); while (FCGI::accept >= 0) { local @global_locals; local @global_constants; &fcgi_localize('index'); local @::{@global_locals}; local @::{@global_constants} = @::{@global_constants}; print "Content-type: text/plain\r\n\r\n"; print "Come over! I'm lonely."; }
    Code for the localizer.pl - Some of the code in here will read the old lists from a file, as Symdump can consume a bunch of the time we were trying to get back!
    #!/usr/bin/perl -T $fcgi_fresh = 0; #Force freshness sub fcgi_localize(){ if (-e "./lc_cache/localizer_consts-$_[0].dat" && -e "./lc_cache/loc +alizer_locals-$_[0].dat" && $fcgi_fresh == 0){ #print "READ"; &localizer_read($_[0]); }else{ #print "WRITE"; &fcgi_localizer($_[0]); &localizer_write($_[0]); } } sub fcgi_localizer(){ require Devel::Symdump; $obj = Devel::Symdump->new(); @global_constants = ('nky','nkt','nfw','connectionInfo','db','host +','passwd','server_id','user','reqs'); my @global_s= $obj->scalars; my @global_a = $obj->arrays; my @global_h = $obj->hashes; my @functions = $obj->functions; my @globals = @global_s; push(@globals,@global_a); push(@globals,@global_h); undef %saw; @saw{@globals} = (); @globals = sort keys %saw; undef %saw; @saw{@functions} = (); @functions = sort keys %saw; foreach $lvar (@globals){ $valid = 1; foreach $fun (@functions){ if ($fun eq $lvar){ $valid = 0; } } if ($valid == 1){ $lvar =~ s/main:://g; if ($lvar !~ m/::/){ if ($lvar =~ m/^[a-z]/){ if ($lvar ne "stdout" && $lvar ne "stderr" && $lva +r ne "stdin"){ unless ($$lvar eq ""){ #push (@global_constants,$lvar); }else{ push (@global_locals,$lvar); } } } } } } #foreach (@global_locals){ # print "Local: ($_) <br>\n"; #} # #foreach (@global_constants){ # print "Constant: $_ <br>\n"; #} } sub localizer_write{ open(WRITER,">./lc_cache/localizer_consts-$_[0].dat") || print "Fail +ed to create Loczlizer Cache!"; foreach (@global_constants){ print WRITER "$_\n"; } close(WRITER); open(WRITER,">./lc_cache/localizer_locals-$_[0].dat") || print "Fail +ed to create Loczlizer Cache!"; foreach (@global_locals){ print WRITER "$_\n"; } close(WRITER); } sub localizer_read{ open(READER,"./lc_cache/localizer_consts-$_[0].dat") || print "Faile +d to create Loczlizer Cache!"; (@global_constants) = <READER>; close(READER); open(READER,"./lc_cache/localizer_locals-$_[0].dat") || print "Faile +d to create Loczlizer Cache!"; (@global_locals) = <READER>; close(READER); chomp(@global_constants); chomp(@global_locals); }
    Take what you will from it no bashing / reminders of the danger of globals / local. Any improvements will be accepted and integrated into the revision.