Introduction
Loops, also known as loop statements or iteration statements, are amongst those ideas in programming without which it's not just difficult but totally impossible to imagine this modern era of computing. Computers are the heart of automation and loops are the forerunner in enabling that.
Essentially a loop is merely a block of code together with some set of instructions on how long to keep that code running again and again. Back in the day when programming languages were invented, we didn't have the loops that we have today.
Over time as programming matured, people realized the need of such a neat programming construct that could fit in well with our intuitive reasoning of repetition. And thus came in the idea of loops as we use them today.
The two most conventional looping statements provided by almost all mainstream languages are for
and while
. Each has its own specific purpose but the idea is the same — keep repeating code until some condition becomes false
.
In this chapter, we start off by exploring all the nitty gritty details of the for
loop, followed by those of while
in the next chapter.
Let's begin.
What is for
meant for?
So what is for
meant for?
Well, precisely speaking:
for
loop is meant to repeatedly execute a piece of code a known number of times.For instance, if we want to print 'Hello'
a thousand times, or process as many <a>
elements in the HTML document as given by the variable anchorsLength
, then what we need is the for
loop.
In both of these cases, we know the limit to the iteration in one way or the other.
Compare this with, let's say, repeatedly asking the user to input a number until the input is -1
. Here clearly we don't know how many times would the code repeat before execution jumps out of it, hence this is not a situation meant for for
(however, technically, it could be used here as well — we'll shortly see how).
To restate it, for
is used to iterate a known number of times.
This is the reason why it's common to use for
to iterate over arrays using their length
property or, in general, over any other sequence.
Anyways, with the purpose of for
understood, it's time to see how to write a basic for
loop.
Syntax of for
The for
statement begins with the for
keyword, followed by a set of configurations enclosed in a pair of parentheses (()
), known as the loop's header, followed by the statement to repeatedly execute, known as the loop's body.
Here's the syntax:
<?php
for (initialization; condition; update)
statement;
The pair of parentheses following the for
keyword consists of three different configurations, each separated by a semicolon (;
).
$initialization
— here any variables to be used in the loop are initialized to given values.condition
— the condition that's evaluated before every iteration. If it evaluates totrue
, the loop's body is executed, or else execution moves out of the loop.update
— an expression that's evaluated after every iteration. Typically this updates a variable's value that's used in the loop's condition so that it eventually becomesfalse
at a certain point, and thus the loop comes to an end.
Note that only expressions are allowed in each of these three sections.
Moreover, it's invalid to include more or less than two semicolons in the for
loop's header. The individual expressions can be omitted but the semicolons have to be there.
We'll see numerous examples of for
loops shortly below to help us understand all this discussion.
Now, if we were to go with all this description of the syntax of for
, we'd arrive at the following syntax.
<?php
for ([initialization]; [condition]; [update])
statement;
Notice the [ ]
around each of the three configuration expressions — they signify the fact that all the three expressions are optional.
Simple examples
In this section, we aim to see a couple of examples of the usage of for
. With each example, we cover a lot of aspects of how to rewrite the for
loop.
Basic counting
Consider the problem of echoing 0 to 4 using a for
loop.
This could very easily be done as follows:
<?php
for ($i = 0; $i < 5; $i++) {
echo $i, "\n";
}
Let's understand how this loop works:
- First, in the statement
$i = 0
, we define a new variable$i
, set to0
. This variable will keep track of the iteration we are currently on, and obviously also be used in the output. Such a variable that's meant to drive a given loop is often known as a loop variable, or even as a loop counter. - Secondly, the condition
$i < 5
means that$i
should be less than5
for the given body to be executed. - Lastly, the update expression
$i++
means that after every iteration,$i
is incremented by1
.
Hence, when $i = 0
(initially), the loop executes; when $i = 1
, the loop executes; when $i = 2
the loop executes; this goes on until $i
becomes equal to 5
at which point the evaluation of $i < 5
yields false
and consequently the loop ends.
Simple, isn't this?
Note that the same output could've also been obtained by slightly modifying the condition. That is, instead of using $i < 5
, we could've also used $i <= 4
. This is because the condition $i <= 4
(akin to $i < 5
) yields true
for all the values 0
, 1
, 2
, 3
and 4
.
Here's an illustration of this fact:
<?php
for ($i = 0; $i <= 4; $i++) {
echo $i, "\n";
}
Iterating over an array
One of the most common uses of for
is to iterate over an array, or any other sequence, such as a string.
The main thing used in this case is the length of the sequence (i.e. the total number of elements in it). For arrays, we already know that the length is obtained via the count()
function.
Likewise, what we do is start at the index 0
and go as long as the index remains less than the length of the sequence. That's because the last element's index is one less than the length of the sequence, and therefore we don't have to end right at that very point.
Let's consider an example. In the code below, we have an array of numbers:
<?php
$nums = [1, 10, 5, -9, -1];
What we want to do is to output each number separately. This is accomplished as follows:
<?php
$nums = [1, 10, 5, -9, -1];
for ($i = 0; $i < count($nums); $i++) {
echo $nums[$i], "\n";
}
Once again, the loop's counter $i
begins at 0
and goes right up to count($nums)
(obviously excluding it), being incremented after each iteration. Inside the loop's body, the $i
th element of $nums
is echoed using $nums[$i]
.
Simply amazing!
If we want to, we can add a bit of spice in the code above. Instead of merely echoing the current item of the array, we can also label it, and thus specify which index does the item have.
Consider the code below:
<?php
$nums = [1, 10, 5, -9, -1];
for ($i = 0; $i < count($nums); $i++) {
echo 'nums[' . $i . ']: ' . $nums[$i], "\n";
}
As can be seen, this time the output is a bit more detailed, showcasing the index of each echoed item.
Alright, it's now time for one very quick and easy task...
Given the same $nums
array as shown above, write some code to output its elements in reverse-order using a for
loop.
The expected output is the following:
To go in reverse order, we ought to begin at the very last index, proceed backwards, and continue as long as the index remains greater than or equal to 0
.
Hence, we get to the following code:
<?php
$nums = [1, 10, 5, -9, -1];
for ($i = count($nums) - 1; $i >= 0; $i--) {
echo $nums[$i], "\n";
}
Moving on, one common thing you'll notice across code snippets out there is depicted as follows:
<?php
$nums = [1, 10, 5, -9, -1];
for ($i = 0, $len = count($nums); $i < $len; $i++) {
echo 'nums[' . $i . ']: ' . $nums[$i], "\n";
}
Can you spot the difference? Well, it's the expression $len = count($nums)
and then the condition's modification to $i < $len
from $i < count($nums)
.
So what's so special about this change?
Recall that the loop's condition, i.e. the second segment in the loop's header, is evaluated before every iteration. When the condition is $i < count($nums)
, the count()
function is called again and again and again in this evaluation.
To prevent this overhead of a function call, programmers typically cache (temporarily store) the length of the sequence in a variable and then use this variable instead.
In the code above, the $len
variable acts as a cache for the length of $nums
. Once stored in it, we can easily retrieve the array's length without having to reinvoke count($nums)
.
Getting numbers input
Suppose we want to obtain some numbers and then add them together. How many numbers we want to add is specified in an initial prompt input. Thereafter, as many additional prompts are made, asking for each of the numbers to be added.
The code below accomplishes this:
<?php
echo 'How many numbers?> ';
$n = (int) fgets(STDIN);
$sum = 0;
for ($i = 0; $i < $n; $i++) {
echo 'Enter number ', $i + 1, '> ';
$sum += (int) fgets(STDIN);
}
echo $sum;
Notice that we begin the loop at $i = 0
even though we could've begun at $i = 1
and obviously changed $i < $n
to $i <= $n
likewise.
The thing is that it's very conventional and natural to begin at 0
and iterate the given number of times using the <
operator, even though sometimes the 0
might not have any practical significance, just like in the code above.
Note that if the expression $i + 1
was repeatedly needed in the loop's body above, then we might've considered changing $i = 0
(in the loop's header) to $i = 1
and then obviously the conditional expression $i < $n
to $i <= $n
as well to ultimately replace $i + 1
with $i
.
However, the expression $i + 1
was used only once in the code above and likewise we didn't consider modifying the loop's header.
Nested loops
A for
loop's body is a piece of code just like any other piece of code in a program. This means that it could contain another for
loop as well.
We usually refer to this as a nested loop. The inner loop is said to be nested inside the outer loop.
Nested loops are extremely common in programming. The majority of algorithms and data structures that you'll implement in your programming career will require you to employ nested loops. So let's quickly see how to set up a nested for
loop.
First let's come up with a scenario. Suppose we have to print ordered pairs of the form (i, j)
where i
is 0
, 1
, 2
and $j
is 0
, 1
, 2
.
The way we'd do so is to first set $i = 0
in the outerloop and then iterate with $j
set to 0
, 1
and 2
; then increment $i
and repeat the iteration over $j
; then increment $i
again and repeat the iteration over $j
.
Here's the code:
<?php
for ($i = 0; $i < 3; $i++) {
for ($j = 0; $j < 3; $j++) {
echo "($i, $j)\n";
}
}
It's very intuitive to follow along. For each iteration of the outer loop, the inner loop iterates three times.
Conventional naming: $i
, $j
and $k
Note that using $i
and $j
as the loop counter variables in a nested loop is a convention. In fact, when there are a total of three loops, the variables used are $i
, $j
and $k
in that order, starting with the outermost loop.
Perhaps this has been influenced from the theory of vectors and matrices in mathematics, where the same names are used to denote given variables.
Whenever designing nested loops in this course, we'll follow this convention unless there is a valid reason to use some other naming.
Time to test you understanding of nested loops.
Write some code to achieve the following output:
Before writing the code, we have to focus on the output and see which element in the ordered pairs is changing quicker than the other one. That's clearly the first element.
Hence, this hints us that the inner loop (with $j
as its counter variable) will produce this first element of the ordered pair. The second element will be produced by the outer loop (with $i
as its counter variable).
Here's one way to accomplish this task, and the preferrable way:
<?php
for ($i = 0; $i < 3; $i++) {
for ($j = 0; $j < 3; $j++) {
echo "($j, $i)\n";
}
}
Another way could simply be to swap the variable names $i
and $j
:
<?php
for ($j = 0; $j < 3; $j++) {
for ($i = 0; $i < 3; $i++) {
echo "($i, $j)\n";
}
}
This isn't preferred since the variable $j
comes first in the outer loop followed by $i
in the inner loop — ideally it should be the case that $i
comes first and then $j
.
The break
and continue
keywords
JavaScript, like almost all mainstream languages, provides two common control flow commands that dictate the execution of a loop — break
and continue
.
break
As the name suggests,
break
serves to break execution out of the loop that it is placed withinIt's more or less like a shortcut to terminate a given loop. Anything following the break
command isn't executed in the loop — execution immediately jumps out.
Let's see a quick example.
In the code below, we have a basic loop, counting from 0
to 5
. The if
conditional inside the loop checks for $i === 2
. The moment that happens, execution jumps out of the loop:
<?php
for ($i = 0; $i < 5; $i++) {
if ($i === 2) {
break;
}
echo $i, "\n";
}
Here's the output produced by this code:
As is evident, the echo $i
statement executes just twice. In the third iteration, when $i === 2
is true
, leading to break
executing (on line 5), the echo $i
statement (on line 8) gets ignored.
Let's try to modify the position where break
is called:
<?php
for ($i = 0; $i < 5; $i++) {
echo $i, "\n";
if ($i === 2) {
break;
}
}
Here's the output produced by this code:
This time, since echo $i
is before the break
command, it executes for the third iteration as well (when $i = 2
), unlike previously, and hence we get the third output.
break
only breaks execution out of the loop where it is used — it won't break execution out of the parent loop (if there's any).break
is undoubtedly a useful command, but use it very carefully. It does reduce the readability of code. Whenever possible, try to avoid using break
in place of a simple and intuitive conditional expression in the header of the loop.
Let's see an instance of what does it actually mean for break
to reduce the readability of code.
Consider the following code:
<?php
$nums = [1, 11, 3, 2, 5];
for ($i = 0; $i < count($nums); $i++) {
if ($nums[$i] % 2 === 0) {
break;
}
echo $nums[$i], "\n";
}
The loop's header clearly shows that we want to iterate over the entire array. However, inside the loop, the additional if
conditional checks whether the current element is an even number. If it is, the loops exits.
In other words, we want to process the elements of the array until an even number is encountered.
As you may agree, this isn't immediately evident just by reading the loop's header. Ideally, we should try to be as informative as we can in laying out the loop's condition in order to make sure that anyone reading the code can pick up the purpose of the loop right away.
A much better approach would be to join both the given conditions together, as done below:
<?php
$nums = [1, 11, 3, 2, 5];
for ($i = 0; $i < count($nums) && $nums[$i] % 2 !== 0; $i++) {
echo $nums[$i], "\n";
}
The loop's condition simply means that the execution of the loop should continue if $i
is less than count($nums)
(i.e. we don't exceed the last element of the array) and if $nums[$i]
is not an even number (i.e. we don't want to process an even number).
See how break
has been removed from our code, yet the loop still works as it did before.
The point of this example is to emphasize on the fact that break
should be used sparingly in loops. This isn't because it degrades performance — it doesn't affect the performance of the script in any way. It's only because it costs the fluid readability of the given code.
That's it.
continue
Let's now move to continue
.
continue
keyword serves to signal to the JavaScript to the engine to skip the current iteration and move to the next one.Effectively this means that any code following the command isn't executed, and execution moves to the next iteration if there is any remaining. If no iteration is remaining, then obviously the loop terminates.
Let's consider a quick example.
In the code below, we iterate over the given array $nums
and skip the loop's body if the current element is an even number:
<?php
$nums = [1, 11, 3, 2, 5];
for ($i = 0; $i < count($nums); $i++) {
if ($nums[$i] % 2 === 0) {
continue;
}
echo $nums[$i], "\n";
}
Clearly, this code could be improved by removing the call to continue and instead using an if
conditional to check if a log ought to be made (i.e. the current number is odd).
This is accomplished as follows:
<?php
$nums = [1, 11, 3, 2, 5];
for ($i = 0; $i < count($nums); $i++) {
if ($nums[$i] % 2 !== 0) {
echo $nums[$i], "\n";
}
}
Without any doubt, this second code snippet is much more readable than the previous one that employed continue
.
Long story short: continue
, as with break
, is a very useful command to control the execution of a given loop. However, we should make sure to use it sparingly as well, just like break
, and rather try to use if...else
conditionals in a way that gets the same job done but in a more readable manner.
With this done, it's time to see the return
keyword as used inside a loop.
The return
keyword
As we saw in the chapter PHP Functions, the return
keyword is used inside a function to immediately terminate execution and return a given value to the calling context.
We also know that it's invalid to use return
outside a function. Therefore, if we want to use return
inside a loop, the loop has to be part of a function's body.
Using return
in a loop, which is obviously itself a part of function, is a very common idea. Why?
Simply because it does two things:
- It breaks execution out of the function which effectively means that no matter how many loops are nested together,
return
would exit them all and the containing function as well. - It returns a value to the calling context, i.e. the place where the function was called.
One quick example could be a function that searches for a given number in a matrix (represented as an array of arrays).
The search would use a double nested loop and, inside the inner loop, a mere conditional to check for the number. If it finds the number we were searching for, true
is immediately returned before bringing the entire execution of the function to a halt.
Following is this example brought to the glyphs of code:
<?php
$matrix = [
[0, 5, 6],
[1, 2, 10],
[0, 0, -1],
];
function search_matrix($matrix, $value) {
for ($i = 0, $n = count($matrix); $i < $n; $i++) {
for ($j = 0; $j < $n; $j++) {
if ($matrix[$i][$j] === $value) {
return true;
}
}
}
return false;
}
var_dump(search_matrix($matrix, 10));
var_dump(search_matrix($matrix, 2));
var_dump(search_matrix($matrix, -50));
Had this example been implemented without using return
, we would've had to make a lot of changes, and then the code won't remain as simple as it looks right now.
And this is it.