Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl default variable $_

Tags:

perl

I thought I understood map however the following has results that I don't understand. I know why it's happening I just don't know how it is happening.

The problem is that the contents of @array are changing because $_ is being reset during _do_stuff_to_file call. so what is printed is here: \nhere:\n when I expect it to be here: donkie\nhere: kong\n.

Note: This is not tested code. It's just what I remember seeing from lab. Why are the contents of @array changing?

If I set $_ to $f before returning 1 from _some_func. Then the array is still intact.

Here is an example program to illustrate what I am seeing:

my @array = ("donkie", "kong");
map { push @junk, _some_func('blah', $_); } @array;

if (join ('', @junk) !~ /0/)
{   # for example sake this is always true since return 1 from _some_func.
    print map { "here: $_\n"; } @array;
}

sub _some_func
{   # for example sake, lets say $f always exists as a file.
    my $j = shift;
    my $f = shift;
    return 0 if !open(FILE, "< $f");
    close FILE;
    _do_stuff_to_file($f);

    return 1;
}


sub _do_stuff_to_file
{
    my $f = shift;
    open(IN, "< $f");
    open(OUT, "> $f.new");

    while (<IN>)
    {
        print OUT;
    }

    close IN;
    close OUT;
}
like image 937
user318747 Avatar asked Aug 21 '10 21:08

user318747


People also ask

What is $@ in Perl?

$@ The Perl syntax error or routine error message from the last eval, do-FILE, or require command. If set, either the compilation failed, or the die function was executed within the code of the eval.

What does =~ mean in Perl?

=~ is the Perl binding operator. It's generally used to apply a regular expression to a string; for instance, to test if a string matches a pattern: if ($string =~ m/pattern/) {

What is the default scope of Perl variable?

Strictly speaking, the default scoping of variables in Perl is the package global.


3 Answers

Many functions in Perl use the default variable $_. Among these are map and the readline operator <>. Like foreach, map makes the loop variable an alias for each element of the list it processes. What's happening is that this line:

while (<IN>)

is assigning to $_ while the aliasing of the map is in effect. This is one of the problems with using $_ (or any other global variable) -- strange action at a distance. If you're going to use $_, localize it first:

local $_;
while (<IN>)
...

Alternately, use a lexical variable instead:

while (my $line = <IN>)
like image 50
Michael Carman Avatar answered Sep 19 '22 07:09

Michael Carman


Most things that set $_ implicitly alias it, so won't cause this problem; the exception is while (<filehandle>). While you can localize $_ (ideally with my $_;), it's better just to never let while implicitly set $_. Do while ( my $line = <filehandle> ) instead. (The special implicit defined() still happens.)

like image 34
ysth Avatar answered Sep 21 '22 07:09

ysth


modifying $_ will change your initial array, because $_ is an alias to current element. Your code should look like this:

my @array = ("donkie", "kong");
my @junk=map {_some_func('blah', $_) } @array;

if (join ('', @junk) !~ /0/)
{   # for example sake this is always true since return 1 from _some_func.
    print map { "here: $_\n"; } @array;
}

sub _some_func
{   # for example sake, lets say $f always exists as a file.
    my $j = shift;
    my $f = shift;
    return 0 if !-e $f;
    _do_stuff_to_file($f);
    return 1;
}


sub _do_stuff_to_file
{
    my $f = shift;
    local $_;
    open(IN, "<",$f);
    open(OUT, ">", "$f.new");

    while (<IN>)
    {
        print OUT;
    }

    close IN;
    close OUT;
}

P.S. map returns array with same number of elements (if scalar is returned from block). grep returns only elements for which block is true.

like image 38
Alexandr Ciornii Avatar answered Sep 23 '22 07:09

Alexandr Ciornii