2

Here is a part of Perl code with a nested loop, plz ignore undefined variables. I was confused about this $_ work mechanism in out and in loop.

Hypothesis: @sfiles = A.c B.c C.c

After execution array @sfiles will be overridden by inner loop which match $symbol.

I am not sure that $_ is used just like a C pointer to these @sfile or open files?

foreach (@sfiles) {

  my $fpname = $sdir . '/' . $_;
  open SOURCEFILE, '<', $fpname or die "Failed to open file [$fpname].  $!";

  while (<SOURCEFILE>) {
    chomp;
    next if /\/\//;

    if (/\/\*/) {
      while (<SOURCEFILE>) {
        chomp;
        last if /\*\//;
      }
    }
    if (/$symbol/) {
      $reference = 'refered';
      last;
    }
  }

  close(SOURCEFILE);

  last if $reference;
}
1
  • 1
    $_ *=2 for my @r =1..5; print "@r" so foreach variable($_) is here aliased to @r array elements. Commented Apr 11, 2014 at 16:43

2 Answers 2

8

I recommend avoiding $_ for almost all situations. It was suppose to make your code more English like, but it really doesn't improve readability:

for ( @array ) {
    next unless /^foo/;
    print;
}

Is that really more readable than this?

for my $item ( @array ) {
    next unless $item =~ /^foo/;
    print "$item\n";
}

Nor, does it have any sort of magic properties (as Borodin hinted in his answer) that another variables would not have. Any variable, and not just $_ would be aliased to the element in a for loop. In this program, I change three to buzz in my array by simply changing the value of the variable $item:

use strict;
use warnings;

my @array = qw(one two three four five);

for my $item ( @array ) {
    if ( $item eq "three" ) {
        $item = "buzz";   # Will actually change $array[2]
    }
}

#
# Let's print out my array and see if $array[2] got changed.
#
for my $item ( @array ) {
    print "$item\n";
}

This prints out:

one
two
buzz
four
five

Even worse, $_ raises it's ugly head anyway in any loop longer than a few lines. In your example, you have:

my $fpname = $sdir . '/' . $_;

So much for being invisible.

In your example, you noticed that the value of $_ seems to keep working in both the inner and outer loop. That is, even though I'm using the same variable, somehow the two loops don't interfere with each other.

That's because $_ is localized for each loop. It's one of those Perl mishmash concepts that non-Perl developers rant and rave about.

$_ is the same variable in both loops. $_ is global in scope. However, in each loop, the value that this global variable contains is localized to the scope of the loop. Huh? See local and get ready to have your mind blown. It usually takes a few years for someone to learn the difference between a package (aka globally) scoped variable, a lexically (aka locally) scoped variable, and a package scoped (aka global) variable that's localized. No wonder why all the Python programmers make fun of me.

As ysth stated, although using $_ happens to work, your program is would be easier to understand if you used an actual variable in each loop. Besides, imagine what would happen if you had need the name of the file that the line came from in your while loop. (warnThe line $_ in file $_ is not in the right format";`).

You may want to use labels when you have loops within loops. A label can be used to tell which loop a next or last should be going to:

FILE:
for my $source_file ( @files ) {
   open my $source_fh, "<", $source_file;   # Use autodie...
   LINE:
   while ( my $line = <$file> ) {
      ...
      ...
      next LINE if ...   # Don't need this line
      ...
      ...
      next FILE if ...  # I'm done with this file
      ...
   }
}

Even if you don't use next FILE like I did, a label simply makes your program a bit cleaner. You're not merely going to the next iteration of the loop, you're going to the next LINE of the file.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks, I did not have a deep understanding of localized in perl, because my routine work always be C. Thanks again for your clearly and correct description. I would like to learn about this.
The information is a bit scattered. Take a look at the Perldoc on Perl Subroutines and read the parts of the Temporary Variables via "my" and Temporary Variables via "local". Also look at the Perldoc on Perl Modules for a discussion on namespace and the symbol table. In Perl before 5.x, all variables are symbol table variables and if you needed to localize the value, you used local. Perl 5 introduced my variables and use strict;
Finaly after 5 hours i found the bug.
5

Yes, foreach aliases the loop variable to the elements being looped over. For this reason, you are often better off not using the default variable but instead doing:

foreach my $file (@sfiles) {
    ...
    while ( my $line = <SOURCEFILE> ) {
       ...

1 Comment

And have the positive side effect that the code becomes readable, and you can follow the logic.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.