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
after 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, its 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 its 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 propagate 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