Perl 101 (part 3): Looping The Loop

Learn about Perl's control structures and array variables.

Shampoo And Perl

Last time, we introduced you to Perl's basic control structure - the "if-else" family of conditional statements - and also taught you a little more about scalar variables. This week, we're going to proceed a little further down that same road, with a look at the different types of loops you can use in Perl, an introduction to a new type of variable, and a tongue-in-cheek look at the things modern managers do in the interests of a fatter bottom line.

As always, we'll begin with a definition for those of you coming at this series from a non-programming background. In geek-talk, a "loop" is precisely what you would think - a programming construct that allows you to execute a set of statements over and over again, until a pre-defined condition is met.

Every programming language worth its salt uses loops - and, incidentally, so do quite a few shampoo manufacturers. Think lather-rinse-repeat, and you'll see what we mean...

While You Were Sleeping...

The most basic loop available in Perl is the "while" loop, and it looks like this:

while (condition)
{
do this!
}

Or, to make the concept clearer,

while (rich Uncle Ed's still breathing)
{
be nice to him
}

The "condition" here is a standard Perl conditional expression, which evaluates to either true or false. So, were we to write the above example in Perl, it would look like this:

while (rich_old_guy_lives == 1)
{
be_nice();
}

How about an example you could actually use in your real-life job? Well, here's one - be warned, however, that it's primarily meant for managers who work in the Dilbert Zone.

Are you satisfied with your salary? [y/n] n
Are you satisfied with your salary? [y/n] n
Are you satisfied with your salary? [y/n] n
Are you satisfied with your salary? [y/n] n
Are you satisfied with your salary? [y/n] y
Employee satisfaction has always been this company's goal. Thank you for making this experience an enriching one!

Let's take a closer look at this code. The first part of the program should be familiar to you by now - we're simply asking for input, assigning it to a variable, and then using the chomp() function to remove the trailing carriage return.

At this point, we begin checking the value of the variable - while this value is not equal to "y", or an affirmative reply, we continue printing the question over and over again, and stop only when the value becomes equal to "y". At this point, the lines following the loop are executed, and the appropriate output displayed.

The end result? A company full of "satisfied" employees [whoever says Perl isn't powerful should take a look at that HR manager's Christmas bonus...]

Here's another example, this one using a "while" loop and a conditional expression that contains a number instead of a string:

In case you flunked math class, the factorial of a number X is the product of all the numbers between 1 and X. And here's what the output looks like:

Gimme a number! 7
The factorial of 7 is 5040.

And if you have a calculator handy, you'll see that

7*6*5*4*3*2*1 = 5040

Once the user enters a number, a "while" loop is used to calculate the product of that number and the scalar variable $factorial [initialized to 1] - this value is stored in the variable $factorial. Next, the number is reduced by 1, and the process is repeated, until the number becomes equal to 1. At this stage, the value of $factorial is printed.

...Or Until You Wake Up

The not-so-distant cousin of Perl's "while" loop is its "until" loop, which looks like this:

until (condition)
{
do this!
}

To illustrate the relationship between the "while" and "until" loops, read these two sentences:

WHILE you're less than eighteen years of age, you can't drink!

UNTIL you're over eighteen years of age, you can't drink!

In other words, the conditional expression to be evaluated in a "while" loop will be exactly the opposite of the one to be evaluated in an "until" loop. So take another look at the WEEP, which we've rewritten using an "until" statement:

If you compare the "while" and "until" lines in the WEEP examples above, you'll see that the two conditional expressions are exactly the opposite of each other. And if you can't decide which one to use, try this little trick - translate your "until" or "while" statement into English, roll it around your tongue, and see if it sounds right to you...

Dos And Don'ts

The two control structures explained above test the conditional expression first, and only proceed to execute the statements within the loop if the expression evaluates to true. However, there are often occasions when you need to execute a particular set of statements at least once before you check for a valid conditional expression.

Perl has a solution to this problem too - it offers the "do-while" and "do-until" constructs, which allow you to execute a series of statements at least once before checking the conditional expression. Take a look:

do {
do this!
} while (condition)

or

do {
do this!
} until (condition)

In this case, Perl will only test for the condition after executing the loop once.

#!/usr/bin/perl
# weighted employee evaluation program version 2.0
# call me WEEP II!
# use DO to ensure that the statements
# within the loop are executed at least once
do {
print ("Can we put you in a cubicle, cancel all your benefits, and pay you less than minimum wage? [y/n] ");
$reply = <STDIN>;
chomp($reply);
} while($reply ne 'y');
print ("Heh heh! This company couldn't do without employees like you!\n");

As you can see, the "do" construct can help to make your code more compact - compare WEEP version 2.0 with the original above.

For Pete's Sake!

Both "while" and "until" are typically used when you don't know for certain how many times the program should loop - in the examples above, for example, the program continues to loop until the user enters the right answer. But Perl also comes with a mechanism for executing a set of statements a specific number of times - and it's called the "for" loop:

Doesn't make any sense? Well, the "counter" here refers to a scalar variable that is initialized to a specific numeric value [usually 0 or 1]; this counter is used to keep track of the number of times the loop has been executed.

Each time the loop is executed, the "condition" is tested for validity. If it's found to be valid, the loop continues to execute and the value of the counter is updated appropriately; if not, the loop is terminated and the statements following it are executed.

How does this work? We've begun by initializing the variable $a to 5. Each time the loop is executed, it checks whether or not $a is less than 12; if it is, a line of output is printed and the value of $a is increased by 1 - that's where the $a++ comes in. Once the value of $a reaches 12, the loop is terminated and the line following it is executed.

And, for something slightly more complex, take a look at our re-write of the factorial calculator above:

Grade School

Before we go on to our last - also known as final - control structure of the day, we're going to take a quick detour and introduce you to a different kind of variable.

As you already know, Perl comes with scalar variables, which can be used to store a single value. But what you may not know is that it also comes with a mechanism to store multiple values in a single variable. This variable is known as an "array variable", and it is a useful method of storing and representing related information.

All the standard rules which apply when naming scalar variables also apply in the case of array variables, with one important exception - where a scalar variable name is preceded by a dollar [$] sign, an array variable name is preceded by an @ symbol.

Note also that Perl does not place any restrictions on array and scalar variables sharing the same name in Perl - so the array @stuff is different from the scalar $stuff.

Now, let's say that you wanted to create an array containing the names of your friends:

Each element of the array is a scalar variable, and can be accessed using scalar notation, with a suffix denoting the element's position in the array. So, in order to extract the first element of the array @friends, you'd use

$friends[0]

while

$friends[5]

would give you the sixth element of the array, Ross.

Similarly, if you wanted to modify a particular element of the array, you could use the scalar notation above to accomplish your task, like this:

$friends[3] = "Janice";

Your array would now contain

("Rachel", "Monica", "Phoebe", "Janice", "Joey", "Ross");

Note that the first element of an array is always referred to by the index 0 - this concept, known as "zero-based indexing" often confuses novice programmers, and is just one more reason why geeks have so few friends.

An array can contain both string and numeric data - for example, this is perfectly valid:

@mix = ("hello", 34747, 3, "bon jour");

How about a quick example to illustrate how data can be stored in a single array variable:

What was your grade in Math? 10
What was your grade in Lit.? 20
What was your grade in Physics? 30
What was your grade in Biology? 40
SUBJECT GRADE
Math 10
Lit. 20
Physics 30
Biology 40
TOTAL: 100

Let's walk you through this: we've begun by initializing two array variables and one scalar variable. The array @subjectlist contains a list of the subjects to be graded, and the array @gradelist will be populated by the user with the actual grades. The scalar variable $total will be used to display a total figure at the end.

Next, we've used a "for" loop to display a question, and assign the user's input to the @gradelist array in the appropriate slot via the $x counter. Once all four subjects are taken care of [note the conditional expression in the "for" loop, which automatically stops looping when the counter reaches 4], we've simply used the print() function and a second "for" loop to display a neatly-tabulated row of grades and subjects.

The second loop also adds each grade to the variable $total, and this displays this total value at the end of the program.

A couple of other points to note:

The \t character used in the print() statements above is used to generate a single "tab" space.

The notation

$total += $gradelist[$y];

is simply a Perl shortcut for

$total = $total + $gradelist[$y];

Playing With Friends

Now that you've got the basics of array variables down, it's time for us to introduce the "foreach" loop, one of the most useful control structures in Perl for dealing with array variables. It looks like this:

Rachel is a true Friend!
Monica is a true Friend!
Phoebe is a true Friend!
Chandler is a true Friend!
Joey is a true Friend!
Ross is a true Friend!

The "foreach" loop works its way through the elements of an array, assigning each element to the defined scalar variable, and then executing the statements within the curly braces. In the example above, each subsequent iteration of the loop sees a new value being assigned to the scalar variable $item - this continues as many times as there are elements in the array. Once all the array elements are exhausted, the statements following the loop are executed.

An important point to note here is that the scalar variable used in the "foreach" loop is only assigned a value temporarily - once the loop has finished executing, the original value of the scalar variable, if any, is restored. Take a look: