http://qs1969.pair.com?node_id=274112

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

I'm currently writing a program which passes some user information around in a hash. In one of the subroutines of one of my files, the address of $_ is the same as the address of some of my data

\$_ = SCALAR(0x401ba594)
\$u->{loc} = SCALAR(0x401ba594)

I traced the problem down to this line of code in another file (which calls the above sub)

foreach ($u->{loc}) { ... }

So my question, how does the scoping apply to $_? I know that it's in package 'main', so it's global throughout my program (even across separate files). Can I make it local temporarily, or is there some other trick I can use? I'd like to avoid something like (foreach $a ($u->{loc})) because I actually have that foreach line in many spots, with lots of pattern matching, so it keeps my code cleaner to use $_. Is copying $u->{loc} into a temp variable prior to the foreach perhaps the best solution?

Thanks for any help,

Replies are listed 'Best First'.
Re: $_ scoping issues
by tilly (Archbishop) on Jul 14, 2003 at 19:39 UTC
    See $_ haters anonymou for a discussion of the scoping of $_.

    If you wish to use it widely in loops, then it is up to you to either localize it before calling anything that manipulates it, or to localize it before assigning to it.

    It is, of course, better to never assign to $_ without localizing it, but you can't always control other people's code. Therefore the paranoid call local on $_ early and often.

      All very correct. To specifically state it: $_ is a global variable, and is shared between all packages.

      This means that if you use it, you should use local() to be polite to your callers, and you should be careful to insulate yourself from code that may alter it.

      This can be a difficult proposition if you didn't write the code yourself (and sometimes even if you did!). Generally, you have to look at each appllication of $_ and judge on a case-by-case basis whether someone else may be modifying it while you're using it.

      Your best protection is to get into the habit of always localizing it yourself, so as not to step on someone else's toes, and to not trust that code you call has always been so polite.

        To more precisely state it, no matter what package has been declared, $_ means $main::_.

        As an amusing side effect, the declaration our $_; will (if you are in any package other than main) cause your explicit references to $_ to not be the $_ that you expect...

        thanks guys, these are the kind of things that a former C programmer needs to learn about programming in perl =)

        these forums are terrific, I've learned a lot over the last day browsing through the nodes. What a helpful community!
Re: $_ scoping issues
by DrHyde (Prior) on Jul 16, 2003 at 11:55 UTC
    When you do foreach (list_of_stuff), $_ is aliased to each entry in the list in turn. Hence, $_ *is* $u->{loc} and has the same address.

    If you want to copy each list element into another variable for processing (which is generally the wise thing to do) the usual solution would be foreach my $foo (list_of_stuff).

    On an unrelated issue, I betcha that foreach ($u->{loc}) is a bug. $u->{loc} is, and always will be, a scalar, so there's no point in the foreach.

      DrHyde,

      > When you do foreach (list_of_stuff), $_ is aliased to each entry in the list in turn. Hence, $_ *is* $u->{loc} and has the same address.

      This is exactly my mistake! I believed that each element was copied from the list_of_stuff into $_ one at a time. Good to know.

      > I betcha that foreach ($u->{loc}) is a bug

      You are write that it's a scalar, and I know it looks like I forgot to do something like @{($u->{loc})}, but it's actually my intended affect. Maybe I'm on the wrong track with this, so I'll post it in case people might have some useful suggestions for me.

      I'm using the foreach to do an implicit assignment into $_, and then pattern match on $_. In addition, the foreach loop has the added bonus of allowing flow control (without creating a block)

      foreach ($u->{loc}) { if(/n/) { print "option n" } elsif(/m/) { print "option m" } elsif(/w/) { print "option w" } ... }

      I'm not sure why I originally did it this way (maybe I read something in one of the O'Reilly books that used this technique?), so I adapted it. Is there a better way? If I don't need the flow control advantage, is it better to simply assign $_ = $u->{loc} prior to my conditional statements?