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;
}
$@ 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.
=~ 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/) {
Strictly speaking, the default scoping of variables in Perl is the package global.
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>)
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.)
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With