http://qs321.pair.com?node_id=608846


in reply to "$_" vs. $_

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; }

Replies are listed 'Best First'.
Re^2: "$_" vs. $_
by holli (Abbot) on Apr 07, 2007 at 21:05 UTC
    The real WTF here is that
    while (@_) { local $_ = shift; $params->{$_} = shift; }
    is used instead of
    my %params = @_;


    holli, /regexed monk/
Re^2: "$_" vs. $_
by argv (Pilgrim) on Apr 07, 2007 at 19:56 UTC
    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.)