in reply to Side Effects

Unlike foreach loops, while does not localize $_. In your code, $_ must be an alias to $g::TaskList[0], like $array[0] is in the following code:

@array = ('a'); foreach (@array) { # $_ is an alias for $array[0]. while (<DATA>) { } # $_ and $array[0] are clobbered print("[$_]$/"); # Prints "[]" } print("[$array[0]]$/"); # Prints "[]"

The fix is to localize $_:

@array = ('a'); foreach (@array) { # $_ is an alias for $array[0]. local $_ = $_; # This $_ is not an alias for $array[0]. while (<DATA>) { } # $_ is clobbered, but not $array[0] print("[$_]$/"); # Prints "[]" } print("[$array[0]]$/"); # Prints "[a]"

If you want to preserve $_ within the loop, use:

@array = ('a'); foreach (@array) { # $_ is an alias for $array[0]. { local $_; # This $_ is not an alias for $array[0]. while (<DATA>) { } } # Neither $_ nor $array[0] are clobbered print("[$_]$/"); # Prints "[a]" } print("[$array[0]]$/"); # Prints "[a]"

or:

@array = ('a'); foreach (@array) { # $_ is an alias for $array[0]; while (my $line = <DATA>) { } # Neither $_ nor $array[0] are clobbered print("[$_]$/"); # Prints "[a]" } print("[$array[0]]$/"); # Prints "[a]"

Replies are listed 'Best First'.
Re^2: Side Effects
by goodgulf (Initiate) on Apr 07, 2005 at 19:50 UTC
    Actually, $g::TaskList is not used, named, or referred to in the entire module that the above code appears in (though it is global). (It is used by an upstream sub that calls down into this subroutine though, but wouldn't $_ at least be local to the subroutine?) Even if $_ somehow became an alias to a global variable that was not used in the module in question, how would using $_ as an r-value undef said global?
    $Body .= $_;
    How does that undef $_, or anything it might be aliasing to? Sorry if I'm not getting it (I understand the idea that making it local will fix it, but I'd like to understand what makes this not work in the first place, because I just cannot figure out how $_ became an alias to TaskList). Thanks for the continued attention. :)
      It doesn't, it's the while(<LOG>) that clears it. while(<LOG>) is short for while ($_ = <LOG>). When the end of the file is reached, <LOG> returns undef, undefining $_. My snippets demonstrate this behaviour.
      OK, here's what I'm thinking.
      $Body .= $_ while <LOG>;
      is equivalent to
      while ($_ = <LOG>) { $Body .= $_; }
      Therefore, $_ is both an L-value and an R-value. If we assume for a moment that $_ is somehow an alias for $g::TaskList, then it should be correct that for each iteration of the while loop, $_ (and thus $g::TaskList) will get assigned the next line from <LOG>. At the end of the while loop, what would happen to $_? Would it get undef'd? Would this undef any aliases? So, let's assume all this is true... Here is my code path:
      In Localize.pm: (SNIP) foreach my $lang (@g::LanguageList) { $g::Language = $lang; unless (isExcludedBuild($g::Product, $g::Architecture, $g::Flavor) + || $g::Architecture =~ /ia64/i || $g::Language =~ /usa/i) { (SNIP) &BuildBlah(); } } (SNIP) In Utilities.pm: sub BuildBlah { my $FlavorVariation = shift; (snip) if(FileExists("Blah")) { SendMail($g::MailPurpose{"BuildFailed"}, "Blah", $FlavorVariat +ion); } (snip) } In Reporting.pm: sub SendMail { my $Purpose = shift; my @OptionalParameters = @_; my $Body = ""; (snip) $Body = "<html> <body background=nothing bgproperties=\"fixed\">"; if($Purpose eq $g::MailPurpose{BuildNominated}) { (snip) } elsif($Purpose eq $g::MailPurpose{BuildFailed}) { (snip) open(LOG, "$LogDir\\$g::BuildErrFile"); while (my $line = <LOG>) { $Body .= "${line}<br>"; } close LOG; }
      Although I've snipped a lot of code, this follows the execution path, and you can see that $g::TaskList is never mentioned going way way back up through several loops, modules and subroutine calls. Do I need to backtrack further? I would think the foreach above would wipe out any alias of $_ to TaskList that might have been set higher up in the stack trace.