Understanding Loops

Perl has a fairly standard selection of loops including

			while loop
			do while loop
			for loop
			C-style for loop
			foreach loop

One point of interest here is that foreach is actually an alias of for so you may see this type of loop written with for. However, foreach emphasises the fact that you are iterating through a list of items, so it is considered to be best practice to use it in those circumstances.

Loops with while and until

A while loop in Perl follows a fairly standard pattern as shown in figure 39.

		1.	#!/usr/bin/perl
		2.	# while.pl by Bill Weinman <http://bw.org/contact/>
		3.	 
		4.	use 5.16.0;
		5.	use warnings;
		6.	 
		7.	my @array = qw( one two three four five );
		8.	 
		9.	while (@array) {
		10.	    say shift @array;
		11.	}

Figure 39 - demonstrating the use of a while loop in Perl

The while loop continues as long as the loop is not empty. On each iteration, one element is removed using the shift operator and displayed. This continues until the loop is empty so that on the next iteration, the array is empty, so the condition returns a false value and the loop terminates.

Perl also has a similar type of loop which works with a negative condition. The loop iterates until the condition returns a true value. We can convert the while loop to an until loop by changing line 9 to

until (!@array) { …

so that the condition return a false value as long as the loop is not empty.

Here, we are simply negating the value returned by the condition so while the loop is not empty, a true value is returned and this becomes a false value. When the loop is empty, a false value is returned which becomes a true value which terminates the loop.

In this case, both versions of the loop work in exactly the same way. For the until loop, we have used a negation, but this is not a requirement. We could also have written this as

until (scalar @array == 0) { …

This is testing the size of the arrays (by taking the scalar value) so the loop will continue until the array is empty so again this has exactly the same effect.

Like the unless version of if, until is not commonly used and probably best used only if you have a compelling reason to such as where it makes the logic of a loop more understandable.

There is also a postfix version of the while loop. So we could write our loop as

say shift @array while @array;

This is quite common and you will see it a lot. There is also a postfix version of until.

say shift @array until scalar @array == 0;

The while loop is probably the simplest and most common in Perl so it is important to have a good understanding of it.

Iterating with for

Remember that Perl has two types of for loop, the traditional (sometimes referred to as C-type) for loop and an iterative for loop. Here, we will look at the traditional version which is demonstrated with for.pl shown in figure 40.

		1.	#!/usr/bin/perl
		2.	# for.pl by Bill Weinman <http://bw.org/contact/>
		3.	 
		4.	use 5.16.0;
		5.	use warnings;
		6.	 
		7.	my @array = qw( one two three four five );
		8.	 
		9.	for ( my $i = 0; $array[$i]; ++$i ) {
		10.	    say $array[$i];
		11.	}

Figure 40 - for.pl which demontrates the use of a traditional or C-style for loop

This works in exactly the same way as it does in C (and also other languages such as Java or Kotlin) and it has no postfix version. It is reasonably common in Perl and you will see it used, but the iterative version we will look at next is more common.

Iterating with foreach

Figure 41 shows the code for foreach.pl which demonstrates the use of this type of loop. In Perl, this is the more common type of for loop.

		1.	#!/usr/bin/perl
		2.	# foreach.pl by Bill Weinman <http://bw.org/contact/>
		3.	 
		4.	use 5.16.0;
		5.	use warnings;
		6.	 
		7.	my @array = qw( one two three four five );
		8.	 
		9.	foreach my $s ( @array ) {
		10.	    say $s;
		11.	}

Figure 41 - foreach.pl which demonstrates the use of a foreach loop

Remember that foreach is an alias of for, so if we change foreach to for in the code shown in figure 41, it would still work exactly the same. The use of foreach is preferred where possible because the meaning (iterating through a list) is clearer. However, this can become confusing if not used consistently so it is recommended that whichever version you tend to use, you stick to but you certainly will see foreach used a lot in Perl code.

In this example, we have used a scalar variable, $s, and this represents each element of the array as the loop iterates over them. This variable is optional but probably helps to make the code a little clearer.

If we omit the variable, a special variable, $_ is used. So, we could amend line 9 to

foreach ( @array ) {

We need to then take $s out of the loop so we can amend line 10 to

say $_;

This variable, $_, is what Perl uses when an optional variable is omitted so we could also amend line 10 to

say;

The change to line 9 combined with either version of the change to line 10 will still work exactly as before. You may notice that we have seen this type of usage previously in the section on Assignments and several others.

Actually, if we look at the previous example which was

say foreach @array;

this demonstrates something else and that is the postfix version of the foreach loop. Note that in the postfix version, the parentheses are optional so you could write this as

say foreach ( @array; )

This is not necessary, but if it makes the code a little bit easier to understand, it is worth adding them.

This type of syntax is very compact and straight forward so it is very common in Perl.

Of course, you can also amend the value of each element within the loop. To do this, we will switch back to block code and amend the loop as follows:

		1.	foreach my $s ( @array ) {
		2.	    $s .= '-foo';
		3.	    say $s;
		4.	}

So here, on each iteration through the array (or you might prefer to say, for each element of the array) we are using the concatenation operator to add ‘-foo’ to the end of the array element and then using say to output this amended element.

Bear in mind that this works on arrays, but would not work on a list of string literals.

Loop Control Statements

Perl provides several control statements that can change what happens inside the loop. The first of these is next, as demonstrated in figure 42.

		1.	#!/usr/bin/perl
		2.	# control.pl by Bill Weinman <http://bw.org/contact/>
		3.	 
		4.	use 5.16.0;
		5.	use warnings;
		6.	 
		7.	my $x = 'three';
		8.	my @array = qw( one two three four five );
		9.	 
		10.	while (@array) {
		11.	    my $y = shift @array;
		12.	    next if $y eq $x;
		13.	    say $y;
		14.	}	

Figure 42 - control.pl demonstrating the next control statement

The code in figure 42 uses the kind of while loop we have seen before which simply prints out the elements of the array. We also added a variable called $x with a value of ‘three’ which is the same as the third element of the array.

Inside the loop we have a postfix if statement which executes the next statement if the value of $x is equal to the value of $y. In other words, if the element we have taken from the array matches the value of $x, next is executed.

This simply means that the loop will terminate the current iteration and go on to the next one (obviously, if it is the last element of the array, the loop will simply terminate). In this loop, this means that we will exit the loop before the say statement.

When we run this, we will see that each element of the array being output except the third element because of the fact that the if statement evaluates to true and the loop goes onto the next iteration without executing say.

The effect of next is therefore to terminate the current iteration without executing any other code in the loop. For this reason, next cannot be the last statement in the loop because this would have the effect of skipping onto the next iteration when the loop has finished, which it would do anyway.

Another control statement is last which us similar to next but it breaks out of the loop entirely. So, if there are further elements to be read from the array, for instance, these would not be read.

If replaced next with last in the code in figure 42, we would see the value of the first two array elements being output. The third element would be skipped and the loop would terminate so the fourth and fifth elements would be ignored.

There is another loop control statement in Perl and that is continue. This is demonstrated in figure 43.

		1.	#!/usr/bin/perl
		2.	# control.pl by Bill Weinman <http://bw.org/contact/>
		3.	 
		4.	use 5.16.0;
		5.	use warnings;
		6.	 
		7.	my $x = 'three';
		8.	my @array = qw( one two three four five );
		9.	 
		10.	my $count = 0;
		11.	while ($array[$count]) {
		12.	    say "$count: $array[$count]";
		13.	} continue {
		14.	    ++$count;
		15.	}

Figure 43 - control.pl demontrating the use of the continue statement

This looks a lot like a for loop. You have the initial value for the control variable and the terminating condition (this is the while condition). The continue statement is equivalent to the final part of the for loop which changes the value of the control variable.

Here, we have it in the block of code which has been tacked onto the end of the continue statement. In this case, we are using a statement which would be typical of the sort of thing you would see in a for loop, but it is a block of code so you may see some additional code there as well.

This type of syntax is rarely used in Perl, simply because there are normally better ways to do things. For example, we could have simply used a for loop to avoid the need for continue or we could simply have included any code we put in this block in the main loop.

Similarly, we can usually avoid the use of next or continue. For instance, in the example where we used the next statement, we could instead have used an unless statement such as

say $y unless $y eq $x;

This might be a bit clearer since we are not making a special case for any element. We are just saying for every element, output as long as it’s not equal to $x so whatever the value of the array element, we still execute the same code.

Similarly with last, if we want to output the value of each element in the array until we get a value that matched a predefined value, in our example this was $x, we can simply use an until loop rather than while.

However, it’s a good idea to know about these statements and how the work so let’s quickly recap. The three loop control statements are

		next - this terminates the current iteration of the loop and moves onto the next one
		last - this terminates the loop completely so there will be no further iterations
		continue - this is tacked onto the end of a while loop with its own code block and it is used to control the flow of the loop in the same way that a statement such as $i++ is used to control a for loop (written in the traditional C-style)
	

In most cases, if you think that you need to use one of these loop control statements, it is probably better to re-check your logic because there is probably a better and more intuitive way to do the same thing.