| Search perlmeme.org | |
| Home » Howtos » Syntax » Foreach |
A foreach loop is used to iterate over each element of a list.
The basic syntax is: foreach VAR (LIST) BLOCK
Where the variable VAR is set to each value of the LIST in turn and the code in BLOCK is executed. (By BLOCK we mean code enclosed in braces).
Or, to put this another way, consider the following code sample:
foreach my $var (@list) { ...some code...}
Each element in @list is assigned to $var in turn, and the code in the curly braces is executed.
In Perl, a list can be a simple list of values:
(0,5,3,2)
or produced by an expression like:
(0 .. 9) or ('a' .. 'z')
or from an array like:
@orders
or a function that returns a list:
split(':', $etc_passwd_line)
or
keys %hash
or any combination of these.
So you can use foreach to do something with every element of an array:
foreach my $name (@to_delete)
{
CORE::delete $self->{$name};
$to_delete{$name}++;
}
or every element of a list:
foreach (0..$records) {
my $q = new CGI;
$q->param(-name=>'counter',-value=>$_);
$q->save(\*OUT);
}
or the keys (and values) of a hash:
foreach (keys %ENV) {
push(@p,$_) if /^HTTP/;
}
or combinations of the above:
foreach my $item ('a' .. 'z',0,5,3,2) {
print "item: $item\n";
}
So with that introduction out of the way, let's look at the foreach
mechanism in a little more detail, because there are some subtleties
here that you will want to be aware of.
If the variable after the foreach keyword is omitted, the Perl
'topic' variable $_ is set to each value.
#!/usr/bin/perl
use strict;
use warnings;
my @weekdays = qw(monday tuesday wednesday thursday friday saturday sunday);
foreach (@weekdays) {
print $_ . "\n";
}
This produces the following output:
monday
tuesday
wednesday
thursday
friday
saturday
sunday
If a variable is supplied after the foreach keyword, it is used to
receive each value of the list in turn. If it is preceded with the "my"
keyword, it is visible only within the loop. This is more formally called
'lexically scoped'.
#!/usr/bin/perl
use strict;
use warnings;
my @weekdays = qw(monday tuesday wednesday thursday friday saturday sunday);
foreach my $day (@weekdays) {
print $day . "\n";
}
This produces the same output as the example above.
This code fragment shows more clearly that the scope of the $day variable
afer the foreach keyword is limited to the block of the foreach loop,
(the code inside the braces).
#!/usr/bin/perl
use strict;
use warnings;
my @weekdays = qw(monday tuesday wednesday thursday friday saturday sunday);
my $day = 'excellent';
print 'Value of $day before the loop: ' . $day . "\n";
foreach my $day (@weekdays) {
print $day . "\n";
}
print 'Value of $day after the loop: ' . $day . "\n";
This code produces:
Value of $day before the loop: excellent
monday
tuesday
wednesday
thursday
friday
saturday
sunday
Value of $day after the loop: excellent
If you don't qualify the control variable with a 'my', the variable is
nonetheless localized within the foreach loop block.
In this example $day is declared outside the foreach loop, but when
it is used in the loop, it's value is implicitly localized to the loop.
This behavior is unique to foreach loops.
#!/usr/bin/perl
use strict;
use warnings;
my @weekdays = qw(monday tuesday wednesday thursday friday saturday sunday);
my $day = 'excellent';
print 'Value of $day before the loop: ' . $day . "\n";
foreach $day (@weekdays) {
print $day . "\n";
}
print 'Value of $day after the loop: ' . $day . "\n";
As this output shows, $day regains it's previous value after the loop. It
does not, (as you might expect), retain the last value from the loop.
Value of $day before the loop: excellent
monday
tuesday
wednesday
thursday
friday
saturday
sunday
Value of $day after the loop: excellent
If the control variable is on the left-side of an '=' (i.e. in formal
language, it is an 'lvalue'), it will modify the corresponding element in the
LIST. This is because the control variable is 'aliased' to the element
in the list, and changes to the alias propogate to the real element.
#!/usr/bin/perl
use strict;
use warnings;
my @weekdays = qw(monday tuesday wednesday thursday friday saturday sunday);
foreach (@weekdays) {
$_ = ucfirst($_);
}
# and later...
foreach (@weekdays) {
print "Week day: $_\n";
}
Because we assigned the return value of the ucfirst function to the
topic variable $_, we also assign that value to the list element
itself. In other words, the first element of the @weekdays array was
monday, but after the loop, it has been changed to Monday, (and
tuesday has become Tuesday, and so on).
Week day: Monday
Week day: Tuesday
Week day: Wednesday
Week day: Thursday
Week day: Friday
Week day: Saturday
Week day: Sunday
And of course, this behavior is the same if we use an explicit control
variable, such as $day, rather than the topic variable $_:
foreach my $day (@weekdays) {
$day = ucfirst($day);
}
You can't modify the alias if the thing it is aliasing isn't capable of being modified.
#!/usr/bin/perl
use strict;
use warnings;
# WRONG - don't do this.
# If any element of LIST is NOT an lvalue, any attempt to modify
# that element will fail.
foreach my $day (qw(monday tuesday wednesday thursday friday saturday sunday)) {
$day = ucfirst($day);
}
This code will result in the warning:
Modification of a read-only value attempted
Whilst a c-style for loop is possible, it's rarely necessary.
#!/usr/bin/perl
use strict;
use warnings;
my @sorted_appointments = qw(9AM 10AM 2PM 2PM 4PM);
for (my $index = 0; $index <= $#sorted_appointments; $index++) {
if ($sorted_appointments[$index - 1] eq $sorted_appointments[$index]) {
print "You have two appointments at the same time "
. "(at $sorted_appointments[$index])\n"
}
}
The Perl range operator .. is a much more compact way of doing this:
#!/usr/bin/perl
use strict;
use warnings;
my @sorted_appointments = qw(9AM 10AM 2PM 2PM 4PM);
foreach my $index (1 .. $#sorted_appointments) {
if ($sorted_appointments[$index - 1] eq $sorted_appointments[$index]) {
print "You have two appointments at the same time "
. "(at $sorted_appointments[$index])\n"
}
}
You have two appointments at the same time (at 2PM)
Perl supports the idea of modifiers that can be appended to a simple statement.
You may have seen simple statements modified in this way, such as:
print "Some message\n" if ($some_condition_is_met);
In this case, print "Some message\n" is the simple statement and if ($some_condition_is_met)
is the modifier.
The foreach modifier executes the statement that precedes it once for each item in the list.
print length($_) . "\n" foreach qw(There's more than one way to do it);
which outputs:
7
4
4
3
3
2
2
2
In the case above, print length($_) . "\n" is the simple statement and
foreach qw(There's more than one way to do it) is the modifier.
The full syntax for a foreach loop is more formally:
LABEL: foreach VAR (LIST) BLOCK
Where LABEL is a literal value , like OUTER or TEACHERS for example.
Labels are traditionally typed in upper case to highlight their
presence. They act like the labels you see in a goto or a here document.
So, how do we use a label in a foreach block? Like this:
TEACHER: foreach my $teacher (@teachers) {
STUDENT: foreach my $student ($database->getStudents($teacher)) {
next TEACHER if $student->canTeach(); # student can do
# it for the teacher
next STUDENT if $student->unteachable(); # don't waste
# teachers time...
# do stuff with $teacher and $student
}
}
Basically, we can use labels to short circuit to the next element in the current loop, or an outer loop. Well chosen label names can also be self-documenting to some extent.
perldoc perlsyn
Look for the sections on:
Statement Modifiers
Compound Statements
For Loops
Foreach Loops
perldoc -f map
perldoc -f grep
perldoc -q "difference between a list and an array"
Discussions about statement modifiers in
Perl Best Practices