7. Looping

Notice that the postfix form can be used with all looping constructs. One must consider if this form results in better code.

7.1 For

Perl has the C-style for loop with its three arguments:
for ($i = 0; $i < 5; $i++) {
print ("$i\n"); # prints the numbers 0 till 4 on separate lines.
}
$i++ is a shortcut for $i = $i + 1. If you want the printed numbers in a reverse order, do this:
for ($i = 5; $i > 0; $i--) {
print ("$i\n"); # prints the numbers 5 till 1 on separate lines.
}
The count modifier $i++ / $i-- can defined in several ways, e.g. $i += 2, a shortcut for $i = $i + 2 .
for ($i = 2; $i < 10; $i += 2) {
print ("$i\n"); # prints the even numbers 2, 4, 6, 8 on separate lines.
}
For loops can be nested:
for ($i = 0; $i < 2; $i++) {
for ($j = 0; $j < 2; $j++) {
print ($i . "-" . $j . "\n"); # output: 0-0 0-1 1-0 1-1 on separate lines
}
}
Notice that each of the the three parts of the for statement are optional:
for ( ; $i > -1; ) {
print ($i . "\n");
$i--;
}
7.1.1 Practical example
How to remove duplicates in a string?
$abc = "aaaabbcccccddef";
$uniques = "";
for ($i = 0; $i < length($abc); $i++) {
$substring = substr($abc, $i, 1);
if (index($uniques, $substring) == -1) {
$uniques = $uniques . $substring;
}
}

print "Unique values: $uniques\n";# output: abcdef
An alternative and faster way can be found under 12. Transliteration (12.4 Modifiers 'c', 'd' and 's')

7.2 While

The C style for loop has a flexible nature. Most of the time you could also use while, but more code lines are necessary.
$i = 0;
while ($i < 5) {
print ("$i\n");# prints the numbers 0 till 4 on separate lines.
$i++;
}
Of course, the while loop statement modifier is more concise.
$i = 0;
print ($i++ . "\n") while ($i < 5);# prints the numbers 0 till 4 on separate lines.
Notice that ++$i would print the numbers 1 till 5! Notice also that print ("$i++\n") would result in an endless loop, printing 0++ ... So, maybe always better to write print ($i++ . "\n"), using the concatenation operator.
7.2.1 Practical example
How to handle input from the terminal:
print "Enter some alphabetic characters (to quit, press just ENTER):\n";
while (chomp($input = <STDIN>)) {

last if ($input eq "");
print($input . "-" . uc($input) . "\n");
}

print "\nDone";

7.3 Foreach

The C-style for loop can also used for arrays:
@abc = qw(a b c);
for ($i = 0; $i <= $#abc; $i++) {
print ("$abc[$i]\n");# prints the letters a, b and c on separate lines.
}
The value of $#abc is the last index, here: 2. As you saw earlier, 'foreach' (1) is the default alternative for iterating an array (and a list).
@abc = qw(a b c);
foreach $item (@abc) {
print ("$item\n");
}
and its shorter alternative with the special variable $_
@abc = qw(a b c);
foreach (@abc) {
print ($_ . "\n");
}
Of course, all these examples can be done with 'while'. Pars pro toto:
@abc = qw(a b c);
while ($#abc >= 0) {
$item = shift(@abc);# shift(@abc) removes the first element.
print ($item . "\n");
}
...which is the same as:
@abc = qw(a b c);
while (0 <= $#abc) {
print (shift(@abc) . "\n");
}
You now have met the Perl motto: 'There's more than one way to do it (TMTOWTDI or TIMTOWTDI, pronounced Tim Toady)!

7.4 For or foreach loop with ranges

The for loop with ranges is an easy one. To generate a password of 8 integers between 0 and 9:
$password = "";
for (1..8) { # or 'foreach'
$password = $password . (int(rand(10)));
}
print("$password\n");
Alternatively:
$password = "";
$password = $password . (int(rand(10))) for (1..8);
print ("$password\n");
Another example:
foreach (7..20) { 
  $str = $_; 
  $str = "fizzbuzz" if ($_ % 3 == 0 && $_ % 5 == 0); # shorter: if ($_ % 15 == 0) 	
  $str = "fizz" if ($_ % 3 == 0);
  $str = "buzz" if ($_ % 5 == 0); 
  print("$str\n"); # on separate lines: 7 8 fizz buzz 11 fizz 13 14 fizzbuzz
};

7.5 Escape routes with 'last' and 'next' (2)

The 'last' function breaks, exits the loop:
@abc = qw(a a b b b c c);
$is_found;
$element = 'a';
foreach (@abc) {
if( /$element/ ) {
$is_found = $_;
last;
}
}
($is_found) ? print "The element $element was found" : print "The element $element was not found!";
With a label:
@abc = qw(a a b b b c c);
$is_found;
$element = 'a';
mcLoop: foreach (@abc) {
if( /$element/ ) {
$is_found = $_;
last mcLoop;
}
}
($is_found) ? print "The element $element was found" : print "The element $element was not found!";
The 'next' function starts the next iteration of the loop immediately:
@no = (11, 10, 2, 4, 6, 9, 13);
foreach (@no) {
next if ($_ * $_) > 100;# do not print anything if the square of a number is greater than 100
print ($_ * $_ . " ");
}
The output is 100 4 16 36 81

7.6 Ways to iterate on a string

Two ways with split:
$str = "abcdefgh";
foreach $char (split(//, $str)) {
print "$char\n";
}

foreach $char (split('', $str)) {
print "$char\n";
}
The third way with a for loop:
$str = "abcdefgh";
for $i (0..length($str)-1) {
$char = substr($str, $i, 1);
print "index: $i, char: $char \n";
}

Footnotes
(1) In the next examples, 'foreach' can be replaced by 'for', but I do not recommend it.
(2) I skip the function 'redo', while not having found an useful application...