Reputation: 522
I looked online and everyone seems to say they are used interchangeably. However, it gave me different results when I was iterating through my array.
I had an array of approx. 60 string elements and were filtering out whatever matched in another array. But when I used foreach
, it only saw an array of 45 elements so when I printed the final result, it did not filter everything out.
It was only when I used for
that it worked as I wanted. What is the reason behind this?
I managed to solve the problem, now I just want to understand the way this works. Thanks!
EDIT: So i didn't provide any examples so here's what I did:
my $i = 0;
foreach my $item (@array){
print "\n$i : $item";
foreach my $exclude_item (@exclude){
chomp $exclude_item;
if ($exclude_item eq $item){
print "\nDEBUG: Removing $item";
splice @array, $i, 1;
}
}
$i++;
}
The above wasn't giving me a complete list of @array
so when I printed the final result, there were elements inside that should've been filtered out.
So I tried this instead (which worked as I wanted):
for (my $i=0; $i < scalar @array; $i++){
print "\n$i : $array[$i]";
foreach my $exclude_item (@exclude){
chomp $exclude_item;
if ($exclude_item eq $array[$i]){
print "\nDEBUG: Removing $array[$i]";
splice @array, $i, 1;
}
}
}
Just additional info, the @array
was created by searching for list of subdirectories in multiple folders. It's possible that @array
contained duplicates.
Upvotes: 2
Views: 331
Reputation: 385655
# Foreach loop \
foreach my $item (@array) > same \
for my $item (@array) / \
> different
# Augmented while loop \ /
for (my $i=0; $i < scalar @array; $i++) > same /
foreach (my $i=0; $i < scalar @array; $i++) /
You must not modify an array over which you iterating. From perlsyn,
If any part of LIST is an array,
foreach
will get very confused if you add or remove elements within the loop body, for example withsplice
. So don't do that.
Therefore,
foreach (@array){
...
splice @array, $i, 1; # XXX Error
...
}
Note that you may safely modify the elements of an array over which are you iterating.
foreach (@array){
$_ = uc($_); # ok
}
Finally, a far better solution:
chomp(@exclude);
my %exclude = map { $_ => 1 } @exclude;
@array = grep { !$exclude{$_} } @array;
Aside from being much simpler, it scales much much better[1].
Upvotes: 14
Reputation: 14714
When people say they are used interchangably, they mean that the foreach
keyword is just a synonym for the for
keyword. That means the following two lines mean exactly the same thing:
foreach my $item (@array) {...}
for my $item (@array) {...}
For the avoidance of doubt I will ignore the foreach
keyword and use only for
below.
The following two lines, as you have seen, do not behave identically:
for my $item (@array) {...}
for (my $i = 0; $i < scalar @array; $i++) {...}
The manual advises against adding or removing elements in @array
in the loop body of the first line, and doesn't say what would be the result.
If the loop body of the second line removes an element in @array
at or before $i
, the result will be that the element at new position $i
will be skipped over. If it adds an element at or before $i
, the result will be that the element at old position $i
will be seen by the loop a second time.
Upvotes: 6