Normally when you run a Perl script, the computer moves through each line of your script in turn, executing each line as it goes. However, often you’ll want to alter this flow through the script.
For example, you might want to run different pieces of code depending on whether a visitor has filled out a form correctly or not. Also, you often want to run the same piece of code lots of times – for example, when reading through each line of a file.
Perl features a number of useful ways to alter the path of execution through your script. In this tutorial I’ll show you the most common techniques, and how you can use them.
Making decisions: the if
statement
The if
statement tells Perl to choose whether to run a particular piece of code or not depending on a certain condition (or set of conditions).
For example, the following code will display the message “x is less than 10” if the value of the variable $x
is less than 10. (If it’s 10 or greater, the code within the braces ({ }) will be skipped.)
if ( $x < 10 )
{
print "x is less than 10";
}
You can use the else
statement to run a different block of code if the condition in the if
statement is not met. For example:
if ( $x < 10 )
{
print "x is less than 10";
}
else
{
print "x is 10 or greater";
}
If the condition $x < 10
fails, control passes to the block of code after the else
statement.
You can take the else
concept one stage further with the elsif
statement. elsif
is short for "else if"
, and allows you to check as many conditions as you like. For example:
if ( $x < 10 )
{
print "x is less than 10";
}
elsif ( $x < 100 )
{
print "x is between 10 and 99";
}
This will return an appropriate message depending on whether the variable $x
is less than 10, or between 10 and 99.
You’ve probably already spotted that this code will do nothing if $x
is greater than or equal to 100! To fix this, we can modify the code as follows:
if ( $x < 10 )
{
print "x is less than 10";
}
elsif ( $x < 100 )
{
print "x is between 10 and 99";
}
else
{
print "x is 100 or greater";
}
You can chain as many elsif
s together as you like, optionally followed by an else
at the end of the chain, as shown above.
The unless
statement
The unless
statement is simply the opposite of the if
statement. In other words, it is saying “unless this condition is true, run this block of code”. For example:
unless ( $x >= 10 )
{
print "x is less than 10";
}
Shorthand for if
and unless
If you only want to run one line of code depending on the outcome of the if/unless
condition, you can write code like this:
print "x is less than 10" if ( $x < 10 );
Or, with the unless
statement:
print "x is less than 10" unless ( $x >= 10 );
Looping with the while
statement
The while
statement is one of the looping statements available in Perl. A while
loop allows you to keep executing a piece of code over and over again, as long as a certain condition is still met.
A while
loop looks like this:
while ( condition )
{
(stuff to do inside the loop)
}
As long as condition
remains true, the code inside the loop will execute. After the code has executed once, the condition
is tested again, and so on.
For example, this code will count up from 1 to 3 using the variable $counter
. Each time through the loop, $counter
is incremented by 1 (with the code $counter++
).
The code in the loop will only run as long as $counter
is less than or equal to 3. Once $counter
reaches 4, the loop will exit.
$counter = 1;
while ( $counter <= 3 )
{
print ( "I've counted to: $counter\n" );
$counter++;
}
This will output the following:
I've counted to: 1 I've counted to: 2 I've counted to: 3
Looping with the until
statement
As with if
and unless
, until
is basically the opposite of the while
statement. So the above loop could be rewritten to use until
as follows:
$counter = 1;
until ( $counter > 3 )
{
print ( "I've counted to: $counter\n" );
$counter++;
}
The do
statement and loops
With while
and until
loops, it’s possible to check the condition after the block of code inside the loop has been executed, rather than before it. You can do this using the do
statement, as follows:
do
{
(stuff to do inside the loop)
} while ( condition );
do
{
(stuff to do inside the loop)
} until ( condition );
So we could rewrite our while
counting example above as follows:
$counter = 1;
do
{
print ( "I've counted to: $counter\n" );
$counter++;
} while ( $counter <= 3 );
Because the condition is checked after the code block has run, the code block always runs at least once. This can be very handy if, for example, the variable you are testing in the condition needs to be initialised inside the code block!
Compare with the standard while
and until
loops above, where the condition is tested before the code block is run. If the condition is false, the code block will never be run.
Looping with the for
statement
The for
statement is another way to build loops in Perl. You can think of it as a more specialised, shorthand version of the while
loop.
The for
loop allows you to specify the initial value of a looping variable (e.g. $i
), then a condition test, and finally a statement to update the loop variable, all in one statement!
This means that you can write loops that involve a counter much more easily and neatly than using a while
loop. In fact the examples above that use the while
statement could be more succinctly written as a for
loop.
Here’s an example that counts from 1 to 10, placing the numbers in a string as it goes (using the string concatenation operator, '.'
). It then outputs the final string.
$output_string = "";
for ( $i=1; $i<=10; $i++ )
{
$output_string .= "I've counted to: $i\n";
}
print $output_string;
Looping with the foreach
statement
foreach
gives you a quick and easy way to loop over a list of values. The general structure of a foreach
loop is:
foreach variable ( list )
{
( do stuff )
}
As each element of the list variable list
is processed, it is made available via the variable variable
to the code block inside the loop, allowing you to manipulate that list element.
For example, here is a simple piece of Perl code that counts up to ten in words:
# Build a list variable to store the words
@word_list = ( 'one', 'two', 'three', 'four', 'five', 'six',
'seven', 'eight', 'nine', 'ten' );
# Loop through the list, displaying each list item in turn
foreach $count_word ( @word_list )
{
print "I've counted to: $count_word\n";
}
Each time through the loop, the next list element ("one"
, "two"
, etc) is made available via the variable $count_word
, which we can then use inside the loop to display the output.
This script displays the following output when run:
I've counted to: one I've counted to: two I've counted to: three I've counted to: four I've counted to: five I've counted to: six I've counted to: seven I've counted to: eight I've counted to: nine I've counted to: ten
Controlling loops: the next
, last
and redo
statements
Sometimes it’s useful to be able to alter the normal flow of a loop. You can do this in Perl with the following statements:
last
breaks out of the current loop entirely, continuing execution with the first statement outside the loop block.next
skips the rest of the code block after the next statement, and starts the next iteration of the loop (or ends the loop if the loop condition is now false).redo
skips the rest of the code block after the redo statement, and starts executing the loop block from the top again, but without re-testing the loop condition.
Here’s an example that uses the last
statement to only count up to 5, even though the loop would normally count to 10:
$output_string = "";
for ( $i=1; $i<=10; $i++ )
{
$output_string .= "I've counted to: $i\n";
last if ( $i == 5 );
}
print $output_string;
This example uses the next
statement to count to 10, missing out the number 5:
$output_string = "";
for ( $i=1; $i<=10; $i++ )
{
next if ( $i == 5 );
$output_string .= "I've counted to: $i\n";
}
print $output_string;
Nested loops and labels
Often you want to run one loop inside another loop. This is known as loop nesting. Nesting is handy for looping through two lists where the second list should be looped through for each element in the first list. For example, the following code lists the abilities of 5 children (reading, walking and talking) by looping through all 3 abilities for each child:
# Build a list variable to store the children's names
@children = ( 'Anna', 'Bert', 'Charlie', 'David', 'Fiona' );
# Build a list variable to store the abilities
@abilities = ( 'read', 'walk', 'talk' );
# Display the abilities for each child
foreach $child ( @children )
{
foreach $ability ( @abilities )
{
print "$child can $ability\n";
}
}
This script prints the following:
Anna can read Anna can walk Anna can talk Bert can read Bert can walk Bert can talk Charlie can read Charlie can walk Charlie can talk David can read David can walk David can talk Fiona can read Fiona can walk Fiona can talk
However, what if Bert can’t walk yet and David can’t talk? We can use the next
statement along with a loop label
to skip these two cases:
# Build a list variable to store the children's names
@children = ( 'Anna', 'Bert', 'Charlie', 'David', 'Fiona' );
# Build a list variable to store the abilities
@abilities = ( 'read', 'walk', 'talk' );
# Display the abilities for each child
foreach $child ( @children )
{
ABILITY: foreach $ability ( @abilities )
{
next ABILITY if ( $child eq "Bert" && $ability eq "walk" );
next ABILITY if ( $child eq "David" && $ability eq "talk" );
print "$child can $ability\n";
}
}
The ABILITY:
text placed before the inner foreach
loop assigns the label “ABILITY” to this loop. We then place the label after the next
statements in the loop block to tell Perl that we want to skip straight to the next iteration of the “ABILITY” loop (missing out the print
statement).
In fact, if we missed off the ABILITY
after the next
statements, the script would still work because Perl assumes you mean the innermost loop that encloses the next
statements if you don’t specify otherwise. However, what if Charlie couldn’t read, walk or talk yet? We could skip Charlie out altogether by using next
on the outer loop, as follows:
# Build a list variable to store the children's names
@children = ( 'Anna', 'Bert', 'Charlie', 'David', 'Fiona' );
# Build a list variable to store the abilities
@abilities = ( 'read', 'walk', 'talk' );
# Display the abilities for each child
CHILD: foreach $child ( @children )
{
ABILITY: foreach $ability ( @abilities )
{
next ABILITY if ( $child eq "Bert" && $ability eq "walk" );
next ABILITY if ( $child eq "David" && $ability eq "talk" );
next CHILD if ( $child eq "Charlie" );
print "$child can $ability\n";
}
}
With this script, we tell Perl to miss out Charlie altogether with next CHILD if ( $child eq "Charlie" )
. Here’s the output from our script:
Anna can read Anna can walk Anna can talk Bert can read Bert can talk David can read David can walk Fiona can read Fiona can walk Fiona can talk
Happy looping! 🙂
Leave a Reply