user3002620
user3002620

Reputation: 69

What am I not getting about foreach loops?

It was always my understanding that

foreach (@arr)
{
    ....
}

and

for(my $i=0; $i<@arr; $i++)
{
    .....
}

were functionally equivalent. However, in all of my code, whenever I use a foreach loop I run into problems that get fixed when I change to a for loop. It always has to do with comparing the values of two things, usually with nested loops.

Here is an example:

for(my $i=0; $i<@files; $i++)
{
    my $sel;
    foreach (@selected)
    {
        if(files[$i] eq selected[$_])
        {
            $selected='selected';
        }
    }
    <option value=$Files[$i] $sel>$files[$i]</option>
}

The above code falls between select tags in a cgi program. Basically I am editing the contents of a select box according to user specifications. But after they add or delete choices I want the choices that were origionally selected to remain selected.

The above code is supposed to accomplish this when reassembling the select on the next form. However, with the foreach version it only gets the first choice that's selected and skips the rest. If I switch it to a 3 part for loop, without changing anything else, it will work as intended.

This is only a recent example, so clearly I am missing something here, can anyone help me out?

Upvotes: 2

Views: 94

Answers (1)

tobyink
tobyink

Reputation: 13674

Let's assume that @files is a list of filenames.

In the following code, $i is the array index (i.e. it's an integer):

for (my $i=0; $i<@files; $i++) { ... }

In the following code, $i is set to each array item in turn (i.e. it's a filename):

foreach my $i (@files) { ... }

So for example:

use strict;
use warnings;

my @files = (
   'foo.txt',
   'bar.txt',
   'baz.txt',
);

print "for...\n";
for (my $i=0; $i<@files; $i++) {
   print "\$i is $i.\n";
}

print "foreach...\n";
foreach my $i (@files) {
   print "\$i is $i.\n";
}

Produces the following output:

for...
$i is 0.
$i is 1.
$i is 2.
foreach...
$i is foo.txt.
$i is bar.txt.
$i is baz.txt.

foreach loops are generally preferred for looping through arrays to avoid accidental off-by-one errors caused by things like for (my $i=1;...;...) or for (my $i=0;$i<=@arr;...).

That said, for and foreach are actually implemented as synonyms in Perl, so the following script produces identical output to my previous example:

use strict;
use warnings;

my @files = (
   'foo.txt',
   'bar.txt',
   'baz.txt',
);

print "for...\n";
foreach (my $i=0; $i<@files; $i++) {
   print "\$i is $i.\n";
}

print "foreach...\n";
for my $i (@files) {
   print "\$i is $i.\n";
}

It it simply customary to refer to the second type of loop as a foreach loop, even if the source code uses the keyword for to perform the loop (as has become quite common).

Upvotes: 4

Related Questions