Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

"$_" vs. $_

by argv (Pilgrim)
on Apr 07, 2007 at 18:07 UTC ( #608844=perlquestion: print w/ replies, xml ) Need Help??
argv has asked for the wisdom of the Perl Monks concerning the following question:

This simple program illustrates the problem:

#!/usr/bin/perl sub foo { while (@_) { $_ = shift; $params->{$_} = shift; } print "$_: $params->{$_}\n", for keys %$params; } $_ = shift @ARGV || "world"; foo ( hello => $_ );

The line in question is the last one. As it is, the output is "hello: hello". But if I put the $_ in quotes:

foo ( hello => "$_" );

You get the expected behavior. why?

Comment on "$_" vs. $_
Select or Download Code
Re: "$_" vs. $_
by bart (Canon) on Apr 07, 2007 at 18:16 UTC
    Your problem is that you didn't localize $_ in the sub. As a result, the $_ from the outside world is changed. And since parameters are passed by reference, your parameter is changed after you passed it in. "$_" passes a copy, so that one doesn't change.

    As a fix, which IMO is much less interesting than the phenomenon we just witnessed, you can localize the change by

    sub foo { while (@_) { local $_ = shift; $params->{$_} = shift; } print "$_: $params->{$_}\n", for keys %$params; }
    or
    sub foo { local $_; while (@_) { $_ = shift; $params->{$_} = shift; } print "$_: $params->{$_}\n", for keys %$params; }
      Now that you pointed out what is, in retrospect, the obvious, it's easier to see when you do an identical thing:

      my $bar = "world"; sub foo { while (@_) { $bar = shift; $params->{$bar} = shift; } print "$_: $params->{$_}\n", for keys %$params; } foo ( hello => $bar );

      We're used to seeing things like this because we declare the variables and make that mental connection to it as "global variable", which you can see getting changed inside foo(). For some reason, that doesn't strike me as so obvious when using $_, since I sort of think of it more as an abstract placeholder for short-term uses of values when I don't want to make a whole new variable.

      anyway, thanks for the clarification

        Now that you pointed out what is, in retrospect, the obvious, it's easier to see when you do an identical thing:
        my $bar = "world"; sub foo { while (@_) { $bar = shift; $params->{$bar} = shift; }

        It's not, strictly speaking, the same thing: the big difference being that $_ is a package variable and your $bar above, a lexical one.

        It is perhaps interesting in this respect to notice that a lexical $_ is provided in blead; to quote from there:

        Lexical $_

        The default variable $_ can now be lexicalized, by declaring it like any other lexical variable, with a simple

        my $_;

        The operations that default on $_ will use the lexically-scoped version of $_ when it exists, instead of the global $_.

        In a map or a grep block, if $_ was previously my'ed, then the $_ inside the block is lexical as well (and scoped to the block).

        In a scope where $_ has been lexicalized, you can still have access to the global version of $_ by using $::_, or, more simply, by overriding the lexical declaration with our $_.


        Back to stable, you've been repeatedly pointed to local, in which case it's also worth pointing out that experienced Perl hackers often told me that

        local $_=whatever;

        can break because of a bug with Perl tied variables that may bite you in the neck first or later:

        #!/usr/bin/perl -l use strict; use warnings; use Tie::Array; my @q = my @q0 = qw/foo bar/; sub foo { local $_ = 'baz'; print "@q $_"; } foo for @q; tie @q, 'Tie::StdArray'; @q=@q0; print '-' x 11; foo for @q; __END__

        (Learnt in clpmisc and probably adapted from an example posted there by someone else.)

      The real WTF here is that
      while (@_) { local $_ = shift; $params->{$_} = shift; }
      is used instead of
      my %params = @_;


      holli, /regexed monk/
Re: "$_" vs. $_
by talexb (Canon) on Apr 08, 2007 at 03:34 UTC

    In my opinion, $_ springs into being when Perl feels it's appropriate -- it's an auto-vivified rvalue.

    On the other hand, $_ is *never* an lvalue unless you're using a regexp to modify the value, as in $_ =~ s/foo/bar/;, and even that's stretching a point, because of course uou can do the same thing with just s/foo/bar/;

    So I would say that any code that *sets* $_ is, by definition, wrong.

    Alex / talexb / Toronto

    "Groklaw is the open-source mentality applied to legal research" ~ Linus Torvalds

      So I would say that any code that *sets* $_ is, by definition, wrong.

      I feel this is not a Golden rule. Setting $_ has its uses, especially when one would like to do regex operations not wishing to declare intermediate variables. It is generally wise to localise $_ before setting it. A good example of this kind of usage can be found here.

        In the so-called good example, both local $_ could easily have been replaced with my. You gain the readability of a named variable at the tiny cost of having to specify the topic of two operations.

        my @a = map { s/\s*(#.*)?$//g; $_ } <DATA>; my @b; while (my $group = shift @a) { if ($group =~ m/\D(\d+)-(\d+)(?:\D|$)/) { my $range = "$1-$2"; foreach my $i ($1..$2) { (my $addr = $group) =~ s/(\D)$range(\D|$)/$1$i$2/; push @a, $addr; } } else { push @b, $group; } } print join("\n", @b);

        There are cases where using $_ helps, but I don't think this is one of them.

        Note: The localization of $1 and $2 is unnecessary. It's already being done by the for.

        Note: local $_ doesn't preserve $_ in all situations, unfortunately. for ($var) is a far better way of aliasing $_.

      So I would say that any code that *sets* $_ is, by definition, wrong.
      Unnecessary, perhaps, but not wrong. Setting $_ can be useful:
      1. As a topicalizer in a single-item for loop, to make a series of operations on a common value read more cleanly.

        Instead of this:

        $h->{foo}{bar} =~ s/[._]/ /g; Some::Custom::Function($h->{foo}{bar}); $h->{foo}{bar} =~ s/^\s+//; $h->{foo}{bar} =~ s/\s+$//; $h->{foo}{bar} =~ s/`/'/g;
        You can write this:
        for ($h->{foo}{bar}) { s/[._]/ /g; Some::Custom::Function($_); s/^\s+//; s/\s+$//; s/`/'/g; };

      2. And the occasional weird circumstance, like conveniently modifying the values of a hash:
        map { $_ = foo( $bar, $_ ) } values %somehash;
      On the other hand, where real for loops are concerned, I find it reduces confusion to always provide your own lexical topic (e.g.  for my $key (@keys) { ... } ). A bit backwards from the way the language is designed, I suppose.
        Um, in both examples you are not setting $_---you are only modifying it.

        Small difference, but with it, I'd also say "never set $_".


        Search, Ask, Know
Re: "$_" vs. $_
by Brovnik (Hermit) on Apr 11, 2007 at 11:44 UTC
    I think this is what you wanted :
    #!/usr/bin/perl use strict; use warnings; sub foo { my %params = @_; print "$_: $params{$_}\n", for keys %params; } my $word = shift @ARGV || "world"; foo ( hello => $word );
    Do not use $_ as a temporary variable unless essential.

    Declaring and naming the variable catches a lot of issues and solves (for example) the problem you came across as well as improving readability.

implicit local $_ happens sometimes
by doom (Deacon) on Apr 11, 2007 at 19:12 UTC

    I think that one of the things that's mildly confusing in this area is that perl does an implicit "local" for you whenever it sets $_ for you (in foreach, map and grep loops), but if you do the setting of $_ yourself, then you've got a new problem you don't see otherwise.

    (I still don't understand why our "standard practice" doesn't include doing a "local $_" at the beginning of every sub... but then on the other hand, I guess it isn't all that common to get burned by $_ problems.)

    Anyway, here's a script that demos what I'm talking about:

    use warnings; use strict; use Test::More qw(no_plan); # we're going to try doing various things to $_, # and we want to see if it changes the initial value my $initial_value = 'Some Value'; my @initial_array = qw( wun tew thuree foah fahv sex sevhun ); {#1 map perlfunc: "locally setting $_ to each element"? my $testcase = "map"; my @array = @initial_array; $_ = $initial_value; my @whateva = map{ s/^f/F/ } @array; is( $_, $initial_value, $testcase); } {#2 foreach my $testcase = "foreach"; my @array = @initial_array; $_ = $initial_value; my @whateva = (); foreach (@array) { s/^f/F/; push @whateva, $_ }; is( $_, $initial_value, $testcase); } {#3 while - this one fails my $testcase = "while"; my @array = @initial_array; $_ = $initial_value; my @whateva = (); while (@array) { $_ = pop @array; s/^f/F/; push @whateva, $_}; is( $_, $initial_value, $testcase); } {#4 while with local my $testcase = "while with explicit local"; my @array = @initial_array; $_ = $initial_value; my @whateva = (); while (@array) { local $_ = pop @array; s/^f/F/; push @whateva, $_}; is( $_, $initial_value, $testcase); } {#5 grep my $testcase = "grep"; my @array = @initial_array; $_ = $initial_value; my @whateva = grep{ m/^f/ } @array; is( $_, $initial_value, $testcase); }

      I still don't understand why our "standard practice" doesn't include doing a "local $_" at the beginning of every sub... but then on the other hand, I guess it isn't all that common to get burned by $_ problems.

      while (<FILE>) is the most common offender by far, but it is usually only used in the main body where it does no damage.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://608844]
Approved by bart
Front-paged by bart
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others surveying the Monastery: (5)
As of 2014-12-27 10:14 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    Is guessing a good strategy for surviving in the IT business?





    Results (176 votes), past polls