Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.
 
PerlMonks  

Magical data modification using $_

by Sewi (Friar)
on Oct 11, 2009 at 10:36 UTC ( [id://800531]=perlmeditation: print w/replies, xml ) Need Help??

While working with and on Padre, the great Perl IDE, I discovered a possible bug: The shortcut F2 only worked once per Padre start. As I was busy working on something else, the issue went on the "verify and fix later" list. Today (the next morning), Gábor, the father of Padre, reported the same issue and this time I investigated it and found a common case causing very_hard_to_find errors.

Padre is using Actions for menu options, shortcuts, etc. The Action for showing the Perl help search (F2) is handled by the following piece of source:

for ( @{ $self->{event} } ) { next if ref($_) ne 'CODE'; &{$_}(@args); }
Once I verified that each F2 keypress reached this point by adding a debug print above the loop, the following debug prints got surprising results:
print "---START---\n"; for ( @{ $self->{event} } ) { print STDERR $self->{name}." => $_\n"; next if ref($_) ne 'CODE'; &{$_}(@args); }
This may trigger a "Use of uninitlized value" warning as "use warnings;" is active, but I don't care of them for debug prints which never reach the public. Anyway, here is the result:
---START--- help.search => CODE(0x9bcb9b0) help.search => CODE(0xa2f8618) ---START--- help.search => -f help.search => CODE(0xa2f8618)
We're walking through an array which isn't - believe me - touched anywhere, but the first value magically changes. Memory corruption? Human computer? Moon phase problems? No! It's the $_'s evil side. The fix was simple:
print "---START---\n"; for my $item ( @{ $self->{event} } ) { print STDERR $self->{name}." => $item\n"; next if ref($item) ne 'CODE'; &{$item}(@args); }
Here we go:
---START--- help.search => CODE(0x9bcb9b0) help.search => CODE(0xa2f8618) ---START--- help.search => CODE(0x9bcb9b0) help.search => CODE(0xa2f8618)
What happend?

$_ was used by the loop, but it was no copy of the first array item, it was referencing it. Because something in the called CODE(0x9bcb9b0) was using $_, the new value was stored into the referenced place - the first array item overriding it's own CODE-reference.
By using a private (my) variable for the loop, $_ isn't used any longer and the problem was gone: http://padre.perlide.org/trac/changeset/8727/trunk/Padre/lib/Padre/Action.pm

This is a common problem which triggers Perl newbies and experts and which is usually hard to find, because the errors occurs at a place somewhere later when using the modified data, but if you respect this rule, you should be safe:
Use a private my-variable for looping whenever you call a sub or method within your loop!

Replies are listed 'Best First'.
Re: Magical data modification using $_
by ikegami (Patriarch) on Oct 11, 2009 at 17:22 UTC

    $_ was used by the loop, but it was no copy of the first array item, it was referencing it.

    It was aliasing it.

    Because something in the called CODE(0x9bcb9b0) was using $_

    Perl has many global variables. If you're going to change them in a sub, you have to localize them.

    sub foo { ... while (<$fh>) { # XXX Changes $_ ... } ... }

    Fixed:

    sub foo { ... local $_; while (<$fh>) { ... } ... }
Re: Magical data modification using $_
by jakobi (Pilgrim) on Oct 11, 2009 at 17:58 UTC

    Footnote to ikegami's reply. I found this, when I was hacking in some code on auto-pilot and ended up with my($_) instead of the intended local($_):

    perl -e '@a=qw(a b c d e); for my $_ (@a){&f;print "$_\n"};sub f {$_=uc($_)}'

    This indeed protects the caller from falling victim to a careless $_ use in a subroutine. Works in 5.10, but not in 5.8.

Re: Magical data modification using $_
by Anonymous Monk on Oct 11, 2009 at 16:52 UTC
    They call it aliasing, its special
    #!/usr/bin/perl -- use strict; use warnings; my $one = "hi "; my $two = \$one; $_ .= "there " for $one; $$two .= "buddy "; $two = "bye "; print "one=$one\ntwo=$two\n"; __END__ one=hi there buddy two=bye
    The rule sounds familiar :)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://800531]
Approved by broomduster
Front-paged by Old_Gray_Bear
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (6)
As of 2024-03-28 14:58 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found