Skip to content
Image with logo, providing a link to the home page
  • United Stated of America flag, representing the option for the English language.
  • Bandeira do Brasil, simbolizando a opção pelo idioma Português do Brasil.

Learn Programming: Repetition Structures (Loops)

Examples of repetition structures in the Lua, Python, JavaScript and GDScript programming languages.

Image credits: Image created by the author using the program Spectacle.

Requirements

In the introduction to development environments, I have mentioned Python, Lua and JavaScript as good choices of programming languages for beginners. Later, I have commented about GDScript as an option for people who want to program digital games or simulations. For the introductory programming activities, you will need, at least, a development environment configured for one of the previous languages.

If you wish to try programming without configuring an environment, you can use of the online editors that I have created:

However, they do not provide all features offered by interpreters for the languages. Thus, sooner or later, you will need to set up a development environment. If you need to configure one, you can refer to the following resources.

Thus, if you have an Integrated Development Environment (IDE), or a combination of text editor and an interpreter, you are ready to start. The following example assumes that you know how to run code in your chosen language, as presented in the configuration pages.

If you want to use another language, the introduction provides links for configure development environments for the C, C++, Java, LISP, Prolog, and SQL (with SQLite) languages. In many languages, it suffices to follow the models from the experimentation section to modify syntax, commands and functions from the code blocks. C and C++ are exceptions, for they require pointers access the memory.

Do It Once and Make the Computer Repeat It Multiple Times

There are problems that require few inputs or that analyze a few objects, beings, samples or entities. There are problems that require processing and analyzing tens, thousands, billions of entities. For instance, image that you want to sum a thousand numbers, write a message a thousand times, or examine a million of samples. Although it is possible to duplicate code to perform a very same processing multiple times, it certainly is not recommended nor practical.

The use of subroutines softens the problem. Instead of duplicating the required code to perform the processing, a parameterized function or procedure is defined to perform the task. However, it is still necessary to duplicate function calls. For one, two, five entities, the required effort is low. For hundreds, thousands or millions, the effort becomes increasingly greater. It is certainly possible to replicate the call, although there exists a better way.

Computers are excellent machines to automate tasks. Instead of manually repeating instructions or calls, the ideal is making the computer work for you. In the ideal scenario, you solve a problem once and instruct the computer to repeat the solution as many times as you need.

In the instruction to subroutines, the use of recursion, the allowed creating code that repeated itself, by means of a call that called itself.

The use of recursion for repetition is typical in programming using the functional paradigm, although less usual in the imperative paradigm. In the imperative paradigm, it is more common to use repetition structures (or loops) for the same purpose.

Repetition Structures (Loops)

In books, cartoons and movies, there are punishments that require writing a message multiple times. For instance, it could be necessary to write Hello, my name is Franco five times. It certainly would be possible to write the code as follows.

console.log("1. Hello, my name is Franco!")
console.log("2. Hello, my name is Franco!")
console.log("3. Hello, my name is Franco!")
console.log("4. Hello, my name is Franco!")
console.log("5. Hello, my name is Franco!")
print("1. Hello, my name is Franco!")
print("2. Hello, my name is Franco!")
print("3. Hello, my name is Franco!")
print("4. Hello, my name is Franco!")
print("5. Hello, my name is Franco!")
print("1. Hello, my name is Franco!")
print("2. Hello, my name is Franco!")
print("3. Hello, my name is Franco!")
print("4. Hello, my name is Franco!")
print("5. Hello, my name is Franco!")
extends Node

func _ready():
    print("1. Hello, my name is Franco!")
    print("2. Hello, my name is Franco!")
    print("3. Hello, my name is Franco!")
    print("4. Hello, my name is Franco!")
    print("5. Hello, my name is Franco!")

Five times is a small number. For thousands of times, the task would be more tiresome.

Every message has a same pattern: write(counter, ". Hello, my name is Franco!"). It would be extremetly convenient to define code such as:

let counter = 1
console.log(counter, ". Hello, my name is Franco!")
++counter
counter = 1
print(counter, ". Hello, my name is Franco!")
counter += 1
local counter = 1
print(counter, ". Hello, my name is Franco!")
counter = counter + 1
extends Node

func _ready():
    var counter = 1
    print(counter, ". Hello, my name is Franco!")
    counter += 1

Then, it would be convenient to instruct the computer to repeat the line with the command or function to write the message. Each repetition could also be called iteration.

By combining branches and jumps, it is possible to create code that repeats itself and performs arbitrary processing. The repetition can be infinite, if there is no condition to determine a halt, or, (preferably) finite, when combined with such condition. In other words, a way to define repetitions in computers is to perform a jump to a previous region of the source code of a program.

Repetition structures abstract the jump and the verification of the condition using some reserved words as a command. With them, it is possible to define code that repeat itself zero or more times, or one or more times.

The most common repetition commands are:

  1. With test in the beginning, for zero or more repetitions: while and for;
  2. With test at the end, for one or more repetitions: repeat until and do while.

Some programming languages provide command for both cases; other languages, for only one. In practice, that is not a limitation, because it is possible to write equivalent code to any structure using any other structure.

While (while)

The while command is a direct translation from the scenario described as a combination of jump and branch. It has the following structure as pseudocode:

while (CONDITION)
begin
    // Code to be repeated.
    // Code the potentially changes the result of the contition.
end

The while command is one of the most versatile for repetitions, because it does not impose limitations regarding data types and modifications of values. It only analyzes an expression that must result into a logic value. Thus, CONDITION can be a boolean variable, or a relation and/or logical expression.

While CONDITION results in True, the code repeats itself. This mean that, instead of continuing the normal flow after end, the program returns to the line 1 (definition of while) to verify once again the condition. While CONDITION is evaluated as True, the program will keep executing the block comprehended between begin and end.

When CONDITION results False, the block will finally end (in this case, advancing to line 6). If CONDITION starts with a False result, the block will be ignored (the program will advance to line 6 without executing the block between begin and end at all).

Flowcharts: Flowgorithm

Flowcharts are a good resource to visualize how repetitions work, especially is the program is executed step by step.

To create a while block in Flowgorithm, click in an arrow to add a new block and choose While. Click in the new block to define the condition, that must result in a logic value. To define the code to be repeated, add new blocks using the arrow inside the block.

Example of using `while` in Flowgorithm.

The following code snippet provides the transcription of the text in the image.

Main

Integer counter
counter = 1

While counter <= 5
    False
    True
        Output counter & "Hello, my name is Franco."
        counter = counter + 1

Output "End"

End

It can be noted that the block to repeat is contained inside True. The side with False returns to the normal flow of execution, without adding instructions.

Visual Programming Languages: Scratch

Scratch provide three blocks for repetitions, available in Control: repeat 10, forever and repeat until _. To increment counters with greater ease, the block change my variable by 1, which is available in Variables can be used.

Scratch does not provide a block for while. The closes block is repeat until _, which works with the negation of the condition of a while statement. The block repeat until _ is also different from repeat until in other programming languages, because it performs the verification at the beginning instead of at the end of the structure.

Example of using `repeat until` in Scratch with a condition equivalent to `while`, defined as a negation using `not`.

To avoid using not, the condition can be reversed.

Example of using `repeat until` in Scratch with a conditional equivalent to `while`, using the reverse operation.

The solution probably will be easier to read.

Text Programming Languages: JavaScript, Python, Lua and GDScript

Repetition structures in programming are normally defined in blocks. Some languages, such as JavaScript, can allow omitting an explicit block for a single line, although I do not recommend it.

let counter = 1
while (counter <= 5) {
    console.log(counter, ". Hello, my name is Franco!")
    ++counter
}

console.log("End")
counter = 1
while (counter <= 5):
    print(counter, ". Hello, my name is Franco!")
    counter += 1

print("End")
local counter = 1
while (counter <= 5) do
    print(counter, ". Hello, my name is Franco!")
    counter = counter + 1
end

print("End")
extends Node

func _ready():
    var counter = 1
    while (counter <= 5):
        print(counter, ". Hello, my name is Franco!")
        counter += 1

    print("End")

The message written after the code block only serves the goal of adding an output after the end of the loop, to show the end of the last repetition. Thus, it is inclusion is not necessary to write programs.

Trace Table (or Trace)

To understand how a computer run the previous programs, it is interesting to learn about a technique called trace table (or simply trace). In a trace table, a person executes the code of a program as she/he was a computer.

A way to schematize a trace table consists of using a table reuniting the current line code, the variables defined in the scope, information about input and output (and/or other side effects), and description of processing.

As the code used as example does not receive input, the column will be omitted in the following table. To keep a consistent numbering, the lines assumes the blocks for JavaScript, Python and Lua. Although the tests work the same way for GDScript, the numbering will be offset by 3 lines (instead of starting at line 1, the code will start at line 4).

LinecounterOutputDescription
0?Before starting the program
11Declaration and inicialiation of counter
211 <= 5 is True; the program advances to line 3
311. Hello, my name is Franco!Data output
42Increment of counter
222 <= 5 is True; the program advances to line 3
322. Hello, my name is Franco!Data output
43Increment of counter
233 <= 5 is True; the program advances to line 3
333. Hello, my name is Franco!Data output
44Increment of counter
244 <= 5 is True; the program advances to line 3
344. Hello, my name is Franco!Data output
45Increment of counter
255 <= 5 is True; the program advances to line 3
355. Hello, my name is Franco!Data output
46Increment of counter
266 <= is é False; the program advances to line 7
76EndLast instruction of the program

The second line of the table (the first with data, that is, the one with value 0 for the Line of code) only serves to illustrate that values of variables can be unknown before the declaration and initialization. After the declaration, the value can still be unknown until the first assignment (initialization), or the language can set a default value (although languages often do not do this).

To understand the table, one should follow the code as well as the table. Starting from the third line of the table (Line 1 of code), each program in JavaScript, Python and Lua declares a variable called counter. The value assigned to it is 0, making it the initialization.

In the sequence, the program advances to the next line of code (Line 2). Line 2 contains a while repetition structure. The condition defined is counter <= 5. As counter has, at this time, value 2, the condition is 2 <= 5 that results True. Thus, the next line of code will be Line 3, that will write a message (1. Hello, my name is Franco!) and will advance to Line 4, which increments the counter.

Instead of following the next line outside the repetition, the program performs a jump back to Line 2. This time, counter has the value 2. As 2 <= 5, the condition results, once again, True. Therefore, the next instruction will be the one in Line 3.

The block repeats itself once more. counter assumes value 3, which is less than or equal 5. Ou seja, nova repetição.

The block repeats itself once more. counter assumes value 4, which is also less than or equal 5.

The block repeats itself once more. counter assumes value 5, which still is less than or equal 5.

At this increment, counter will assume the value 6. As 6 >= 5 results False, the next line of code will be Line 7, which is the first line of code after the repetition structure. As Line 7 is also the last line of the program, it writes End and finishes.

To perform traces in a digital way, it is possible to use a debugger. Debugger is a tool used to inspect how a program works, normally with the purpose of helping to remove existing problems (bugs).

The Condition Determines the Number of Repetitions

If you wanted to write the message 100 times, what would you do?

The answer is simple: it is enough to change the condition. Instead of counter <= 5, it would become counter <= 100. You can modify the code to observe the result. The computer will write the 100 messages quickly. If you choose a larger number (such as 5000), perhaps that the execution will take a few seconds.

It is also worth thinking about what you would do to write the message a number of times defined by the end-user. What would you do?

A possibility is storing the number of repetitions into a variable (such as last_value, number_times, or repetitions) and use it in the condition.

Besides, it is very common defining repetitions using integer counters which value belongs to the interval instead of , with as the counter and the number of repetitions. The convention depends on programming language, usually corresponding to the indices used for arrays in the language. Thus, the numeration would start at 0 for JavaScript, Python e GDScript (with the condition counter < ultimo_valor), though at 0 for Lua (with the condition counter <= ultimo_valor).

Variables used for counting are frequently called counters. It is also very common to use the names i, j and k' for counters, which is also common in Math (besides, i` is the first letter of integer).

Code that considers the best practices and follow the conventions of a language is called idiomatic.

The next examples synthesize the discussions of this subsection as examples. The examples for JavaScript, Python and GDScript start the counting at zero. The stop condition and the writing of the value of the counter were adjusted accordinly. The example for Lua starts counting at one. The example for GDScript adopts an arbitrary number of repetitions, as Godot Engine does not provide resources for console (terminal) input.

let counter = 0
let repetitions_count = parseInt(prompt("Number of repetitions: "))
while (counter < repetitions_count) {
    console.log(counter + 1, ". Hello, my name is Franco!")
    ++counter
}
counter = 0
repetitions_count = int(input("Number of repetitions: "))
while (counter < repetitions_count):
    print(counter + 1, ". Hello, my name is Franco!")
    counter += 1
local counter = 1
print("Number of repetitions: ")
local repetitions_count = io.read("*number")
while (counter <= repetitions_count) do
    print(counter, ". Hello, my name is Franco!")
    counter = counter + 1
end
extends Node

func _ready():
    var counter = 0
    var repetitions_count = 5
    while (counter < repetitions_count):
        print(counter + 1, ". Hello, my name is Franco!")
        counter += 1

You can choose the number schema that you prefer. However, it is convenient to get familiarized with numeration starting from zero, because it is very common in programming. In the next examples, the counting in Lua will usually start from zero, due to the author's personal preference and to keep a standard with the other examples.

Verification of the Condition at the Beginning

The while command checks the condition at the beginning of the structure. Therefore, it is possible to completely ignore the block to be repeated if the initial condition results False.

let counter = 5
while (counter < 5) {
    console.log(counter, ". Hello, my name is Franco!")
}

console.log("End")
counter = 5
while (counter < 5):
    print(counter, ". Hello, my name is Franco!")

print("End")
local counter = 5
while (counter < 5) do
    print(counter, ". Hello, my name is Franco!")
end

print("End")
extends Node

func _ready():
    var counter = 5
    while (counter < 5):
        print(counter, ". Hello, my name is Franco!")

    print("End")

In all previous blocks, the initial condition is 5 < 5, which results False. Consequently, the execution of the program advances to the line that writes End.

Infinite Loops and Processes That Never End

It is important to note that, if the expression use as the condition does never change or if it never results False, the repetitions will never end. This is called an infinite loop. There are cases that a program must repeat itself infinitely. However, in general, the occurrence of an infinite loop results from an error of logic during the program development.

All following code blocks repeat infinitely, because the value of counter is never changed during the repetition. Thus, the condition counter < 5 will be eternally 0 < 5, which always result True.

let counter = 0
while (counter < 5) {
    console.log(counter + 1, ". Hello, my name is Franco!")
}

console.log("End")
counter = 0
while (counter < 5):
    print(counter + 1, ". Hello, my name is Franco!")

print("End")
local counter = 0
while (counter < 5) do
    print(counter + 1, ". Hello, my name is Franco!")
end

print("End")
extends Node

func _ready():
    var counter = 0
    while (counter < 5):
        print(counter + 1, ". Hello, my name is Franco!")

    print("End")

As the condition does never change, the previous codes are equivalent to while (True).

while (true) {
    console.log(counter, ". Hello, my name is Franco!")
}

console.log("End")
while (True):
    print(counter, ". Hello, my name is Franco!")

print("End")
while (true) do
    print(counter, ". Hello, my name is Franco!")
end

print("End")
extends Node

func _ready():
    while (true):
        print(counter, ". Hello, my name is Franco!")

    print("End")

Sooner or later, you will write a program that will be (inadvertently) in an infinite loop. To end the program, you will need to end the process (also called kill the process). In command line interpreters, it is common that the shortcuts Ctrl D, Ctrl Z, or Ctrl C (although Ctrl C may leave the program running in the background, which is not always desirable) end processes. In IDEs, there is usually an stop icon () to stop the process.

Another possibility is using the task manager of the system to end the process. In Linux, this can be performed as a combination of the commands ps aux | grep PROCESS_NAME to find the process identifier (process ID ou PID), followed by kill PID_NUMBER. If the process does not end, it is possible to force the end using kill -9 PID_NUMBER. Another alternative is using the command killall, that ends processes by name (por exemplo, killall PROCESS_NAME). In graphical environments, it is also possible to use managers. For instance, in KDE, the shortcut Ctrl Esc opens the monitor with system activities. After choosing a process in the list, it is possible to use a button to end it.

In Windows, there are the shortcuts Ctrl Alt Delete (followed by the option Task Manager) or Ctrl Shift Esc to start the Task Manager. Next, you should access the tab Processes, choose the name of the process in the list, select it and use the option End Task.

Omission of Curly Brackets for Blocks

As it happens for conditional structures, in languages such as C, C++ and JavaScript, it is possible to omit the use of curly brackets to define a block if there is interest in repeating a single line of code.

let counter = 0
while (counter < 5) {
    console.log(counter + 1, ". Hello, my name is Franco!")
    ++counter
}

counter = 0
while (counter < 5)
    // Increment performed here, to avoid an infinite loop.
    console.log(++counter, ". Hello, my name is Franco!")

counter = 0
while (counter++ < 5)
    console.log(counter, ". Hello, my name is Franco!")

console.log("End")

For the same reasons presented in conditional structures, I always prefer to explicit define blocks using curly brackets.

For (for)

A repetition structure such as while can be summarized in four parts:

  1. The initialization of the variable(s) that will be used as condition. In particular, it is quite common that the variable used as a condition is defined as a counter or other variable of the integer type;
  2. The verification of the condition;
  3. The block of code to be repeated;
  4. The modification of the variable(s) used as condition.

Many programming languages provide a repetition structured called for, which combines in a single line the declaration (and/or initialization) of the variable used as condition, modification of the value of the variable and verification of the defined condition. The pseudocode of the structure is similar to:

for VARIABLE from INITIAL_VALUE to FINAL_VALUE by INCREMENT_VALUE
begin
    // Code to be repeated.
end

The previous structure is common in programming languages that define for for integer or numeric types. The structure is equivalent to the following while command:

VARIABLE = INITIAL_VALUE
while (VARIABLE < FINAL_VALUE)
         // or VARIABLE <= FINAL_VALUE, depending on the language
begin
    // Code to be repeated.

    VARIABLE += INCREMENT_VALUE
end

Some programming languages define the structure a slightly different and generic way, allowing its use with other data types:

for VARIABLE from INITIALIZATION while CONDITION modified by UPDATE
begin
    // Code to be repeated.
end

In this case, the structure is equivalent to the following while statement:

VARIABLE = INITIALIZATION
while (CONDITION)
begin
    // Code to be repeated.

    // UPDATE normally uses VARIABLE in some way.
    VARIABLE = ...
    // Change of result of logic value for CONDITION.
    // CONDITION = ...
end

The use of the for command allows defining repetition in a very succinct way. Besides, as declaration, comparison and increment are grouped in a single line, it is harder to forget performing them, potentially avoiding mistakes.

Flowcharts: Flowgorithm

To use the for command in Flowgorithm, choose For after click an arrow. In the definition of the block, it will be necessary to choose the name of a variable, its initial value, the final value, the sign of the increment (increasing for increment, decreasing for decrement) and the step (value of the increment/decrement).

Example of using `for` in Flowgorithm.

The following code snippet provides the transcription of the text in the image.

Main

Integer counter

For counter = 0 to 4
    Done
    Next
        Output (counter + 1) & "Hello, my name is Franco."

Output "End"

End

The defined alternatives are Next and Done. It must be observed that the final value is included in the repetitions, as it will happen in the structure for Lua.

Visual Programming Languages: Scratch

In Scratch, repeat 10 is the closest block to for. To use it, it suffices to provide a number of a variable that stores a number with the number of times to repeat the code in the block.

Example of using `repeat` in Scratch, as a `for`.

In the image, the value 5 can be swapped by a variable with the number of repetitions. Alternatively, in the case of this image in particular, it would be possible to omit the use of the variable if there was no interesting in writing the number of the counter in the phrase.

Text Programming Languages: JavaScript, Python, Lua and GDScript

As commented, the resources for for can vary among programming languages.

for (let counter = 0; counter < 5; ++counter) {
    console.log(counter + 1, ". Hello, my name is Franco!")
}

console.log("End")
for counter in range(0, 5, 1):
    print(counter + 1, ". Hello, my name is Franco!")

print("End")
for counter = 0, 4, 1 do
    print(counter + 1, ". Hello, my name is Franco!")
end

print("End")
extends Node

func _ready():
    for counter in range(0, 5, 1):
        print(counter + 1, ". Hello, my name is Franco!")

    print("End")

In all languages, the variable declaration for the repetition, verification of condition and update of the value were defined in a single line of code.

  • In JavaScript, the format is: for (VARIABLE; CONDITION; UPDATE). In the language, the for command is similar to while, although it can be written in a more convenient way. The variable created for a counter is local to the structure, if it is created using let;

  • In Python, the format is: for VARIABLE in range(INITIAL_VALUE, FINAL_VALUE, INCREMENT). If must be noted that the condition used by default is:

    • INITIAL_VALUE < FINAL_VALUE, if INCREMENT > 0;
    • INITIAL_VALUE > FINAL_VALUE, if INCREMENT < 0.

    Furthermore, it is possible to omit some fields in range() (documentation). There are three main forms of using it:

    • range(INITIAL_VALUE, FINAL_VALUE, INCREMENT);
    • range(INITIAL_VALUE, FINAL_VALUE): assumes that INCREMENT is 1;
    • range(FINAL_VALUE): assumes that INITIAL_VALUE is 0 and INCREMENT is 1.

    The variable created for the counter is local to the structure.

  • In Lua, the format is: for VARIABLE = INITIAL_VALUE, FINAL_VALUE, INCREMENT do.

    It must be noted that the condition used by default is:

    • INITIAL_VALUE <= FINAL_VALUE, if INCREMENT > 0;
    • INITIAL_VALUE >= FINAL_VALUE, if INCREMENT < 0.

    Thus, in Lua, the final value is included in the condition. This is convenient because, in the language, it is more usual to start counting from 1 instead of zero. Thus, one can use values from 1 until FINAL_VALUE, instead of from 0 until FINAL_VALUE - 1.

    Like Python, it is possible to omit the last field:

    • for VARIABLE = INITIAL_VALUE, FINAL_VALUE, INCREMENT do;
    • for VARIABLE = INITIAL_VALUE, FINAL_VALUE do: assumes that INCREMENT is 1.

    The variable created for the counter is local to the structure.

  • In GDScript, the format is the same as in Python.

The next blocks illustrate cases of counting down.

for (let counter = 5; counter > 0; --counter) {
    console.log(counter, ". Hello, my name is Franco!")
}

console.log("End")
for counter in range(5, 0, -1):
    print(counter, ". Hello, my name is Franco!")

print("End")
for counter = 5, 1, -1 do
    print(counter, ". Hello, my name is Franco!")
end

print("End")
extends Node

func _ready():
    for counter in range(5, 0, -1):
        print(counter, ". Hello, my name is Franco!")

    print("End")

The next blocks present examples of zero repetitions.

for (let counter = 5; counter < 5; ++counter) {
    console.log(counter + 1, ". Hello, my name is Franco!")
}

console.log("End")
for counter in range(5, 5, 1):
    print(counter + 1, ". Hello, my name is Franco!")

print("End")
for counter = 5, 4, 1 do
    print(counter + 1, ". Hello, my name is Franco!")
end

print("End")
extends Node

func _ready():
    for counter in range(5, 5, 1):
        print(counter + 1, ". Hello, my name is Franco!")

    print("End")

In programming languages such as C, C++ and JavaScript, every field of the for command are optional. Therefore, it is possible to make any combination of fields. The following examples are valid, although I would not advise using them:

let i = 0
// Equivalent to while (i < 3).
for (; i < 3; ) {
    ++i
}

// Equivalent to while (j < 3).
for (let j = 0; ;) {
    ++j
    if (j >= 3) {
        break
    }
}

// Equivalent to while (k < 3).
let k = 0
for (; ; ++k) {
    if (k >= 3) {
        break
    }
}

// Equivalent to while (l < 3).
for (let l; l < 3; ) {
    ++l
}

// Infinite loop: equivalent to while (true).
for (;;) {
}

When one is learning to program, it is interesting to avoid using commands such as break. There are people that even prohibit the use of commands such as break in didactic or professional activities. Personally, I consider it as a resource as any other. If the result is simpler to read (or, in the case of optimizations, more efficient), personally I do not consider the use problematic.

However, for repetitions, the use of while is much cleaner than the usage of an adapted for with break or omitting fields. Thus, except for presenting as a curiosity, there are few reasons to use break alongside for. It is often better and simpler to choose while instead.

Repeat Until (repeat... until) and/or Repeat While (do... while)

Some programming languages provide repetition structures with verification at the end, that always execute the code defined in the block at least once. There are two common structures for this purporse: repeat until and repeate while. Some programming languages provide one of them; others provide both; some languages do not offer either.

In pseudocode, both structures are similar. Repeat until:

repeat
begin
    // Code to be repeated.
    // Code the potentially changes the result of the contition.
until (CONDITION)

Repeat while:

repeat
begin
    // Code to be repeated.
    // Code the potentially changes the result of the contition.
until (CONDITION)

The difference between them is that repeat until repeats the block of code while the condition is False (in other words, until the condition results True), though repeat while repeats the code while the condition results True. A way to think about it is that until is equivalent to not WHILE_CONDITION.

Flowcharts: Flowgorithm

To use the repeat while command in Flowgorithm, choose Do after clicking an arrow. The definition for the block is similar to the While block.

Example of using `Fazer` in Flowgorithm, which corresponds to `repeat while`.

The following code snippet provides the transcription of the text in the image.

Main

Integer counter
counter = 1

// Begin Do
    False
    True
        Output counter & "Hello, my name is Franco."
        counter = counter + 1
Do counter <= 5

Output "End"

End

The available alternatives are True and False. The repetitions occur in the True side; the False side returns to the main flow.

Text Programming Languages: JavaScript, Python, Lua and GDScript

Python and GDScript do not offer either structures. JavaScript defines repeat while. Lua defines repeat until.

let counter = 1
do {
    console.log(counter, ". Hello, my name is Franco!")
    ++counter
} while (counter <= 5)

console.log("End")
local counter = 1
repeat
    print(counter, ". Hello, my name is Franco!")
    counter = counter + 1
until (counter > 5)

print("End")

In both code blocks, it can be observed the conditions are the opposite of each other. In JavaScript, the condition to repeat is counter <= 5; in Lua, the condition is counter > 5. Thus, one condition is the negation of the other.

In languages that not provide either structure, it is easy to simulate them. A way is to duplicate the code for the first iteration. For instance, the following code blocks simulate the repeat while structure using the conventional while.

let counter = 1
console.log(counter, ". Hello, my name is Franco!")
++counter
while (counter <= 5) {
    console.log(counter, ". Hello, my name is Franco!")
    ++counter
}

console.log("End")
counter = 1
print(counter, ". Hello, my name is Franco!")
counter += 1
while (counter <= 5):
    print(counter, ". Hello, my name is Franco!")
    counter += 1

print("End")
local counter = 1
print(counter, ". Hello, my name is Franco!")
counter = counter + 1
while (counter <= 5) do
    print(counter, ". Hello, my name is Franco!")
    counter = counter + 1
end

print("End")
extends Node

func _ready():
    var counter = 1
    print(counter, ". Hello, my name is Franco!")
    counter += 1
    while (counter <= 5):
        print(counter, ". Hello, my name is Franco!")
        counter += 1

    print("End")

The alternative works because the code outside the repetition structure will always be executed. Consequently, the code will be executed at least once.

To avoid duplicating the code, it is possible to extract the common code as a subroutine.

function code_to_repeat(counter) {
    console.log(counter, ". Hello, my name is Franco!")
    return counter + 1
}

let counter = 1
counter = code_to_repeat(counter)
while (counter <= 5) {
    counter = code_to_repeat(counter)
}

console.log("End")
def code_to_repeat(counter):
    print(counter, ". Hello, my name is Franco!")
    return counter + 1

counter = 1
counter = code_to_repeat(counter)
while (counter <= 5):
    counter = code_to_repeat(counter)

print("End")
function code_to_repeat(counter)
    print(counter, ". Hello, my name is Franco!")
    return counter + 1
end

local counter = 1
counter = code_to_repeat(counter)
while (counter <= 5) do
    counter = code_to_repeat(counter)
end

print("End")
extends Node

func code_to_repeat(counter):
    print(counter, ". Hello, my name is Franco!")
    return counter + 1

func _ready():
    var counter = 1
    counter = code_to_repeat(counter)
    while (counter <= 5):
        counter = code_to_repeat(counter)

    print("End")

With the function, any changed performed in code_to_repeat() would apply to the first and any other repetitions.

Another possibility is using break. The next examples implement repeat until using while and break.

let counter = 1
while (true) {
    console.log(counter, ". Hello, my name is Franco!")
    ++counter
    if (counter > 5) {
        break
    }
}

console.log("End")
counter = 1
while (True):
    print(counter, ". Hello, my name is Franco!")
    counter += 1
    if (counter > 5):
        break

print("End")
local counter = 1
while (true) do
    print(counter, ". Hello, my name is Franco!")
    counter = counter + 1
    if (counter > 5) then
        break
    end
end

print("End")
extends Node

func _ready():
    var counter = 1
    while (true):
        print(counter, ". Hello, my name is Franco!")
        counter += 1
        if (counter > 5):
            break

    print("End")

If one wishes to avoid using break, she/he can use a logic expression.

let counter = 1
let first_repetition = true
while ((first_repetition) || (counter <= 5)) {
    first_repetition = false
    console.log(counter, ". Hello, my name is Franco!")
    ++counter
}

console.log("End")
counter = 1
first_repetition = True
while ((first_repetition) or (counter <= 5)):
    first_repetition = False
    print(counter, ". Hello, my name is Franco!")
    counter += 1

print("End")
local counter = 1
local first_repetition = true
while ((first_repetition) or (counter <= 5)) do
    first_repetition = false
    print(counter, ". Hello, my name is Franco!")
    counter = counter + 1
end

print("End")
extends Node

func _ready():
    var counter = 1
    var first_repetition = true
    while ((first_repetition) or (counter <= 5)):
        first_repetition = false
        print(counter, ". Hello, my name is Franco!")
        counter += 1

    print("End")

The use of an or always forces the first repetition to occur, using a domination, as first_repetition will always be True before the repetitions. Therefore, in the first comparison, the expression is equivalent to while (first_repetition), that is, enquanto (True).

In the first iteration inside the structure, first_repetition becomes False. Due to identity, the condition will become equivalent to while (counter <= 5) from the second iteration onwards. The result is, thus, an implementation equivalent to the repeat while command.

For yet another implementation alternative, one can define an initial value that forces the first iteration in a while, fixing the value inside the block to be repeated. Although it is less generic than the previous ones, it is possible in the case that the values and limits for the condition are known beforehand. One example of such application is building blocks with a variable number of repetitions, which will be presented in the techniques that follows.

Additional Techniques and Concepts

As experience in programming is acquired, it is common to identify to techniques and recurring solutions for similar problems (for instance, software patterns). This section describes some additional topics about repetition structures, to provide with you with new ideas and resources to use in your projects.

Variable Number of Repetitions

As previously discussed, it is possible to define a number in a variable as the upper limit to stop a loop. In potential, it can be read as an input to define a fixed number of repetitions.

There is also another possibility. Instead of fixing a number of repetitions, it is possible to continuously request the input of a value that marks the end of a repetition. The value can be of any type: a number, a string or a logic value. To determine if the loop should end, it suffices to compare the read value with the expected one.

The following programs only finish when one writes end (with any combination of uppercase or lowercase characters) as an input. The GDScript example adopts a fixed number of tries to change the value of the variable.

let text = ""
let counter = 0
while (text.toLowerCase() !== "end") {
    text = prompt("Write end to finish the repetitions.")
    ++counter
}

console.log("You provided ", counter, " input(s).")
text = ""
counter = 0
while (text.lower() != "end"):
    text = input("Write end to finish the repetitions. ")
    counter += 1

print("You provided ", counter, " input(s).")
local text = ""
local counter = 0
while (text:lower() ~= "end") do
    print("Write end to finish the repetitions. ")
    text = io.read("*line")
    counter = counter + 1
end

print("You provided ", counter, " input(s).")
extends Node

func _ready():
    var text = ""
    var counter = 0
    var repetitions_count = 5
    while (text.to_lower() != "end"):
        counter += 1
        if (counter == repetitions_count):
            text = "END"

    print("You provided ", counter, " input(s).")

Instead of end, one could use a specific number, or an expected condition as a combination of relational and logic operators. Therefore, one can adapt the technique to the requirements of a problem.

Input Validation

With conditional structures, it was possible to check whether an input was valid or invalid. With repetition structures, it is not possible to reread values until the input of a valid value.

The following example requests the input of an integer value, repeating the request if one is not provided.

let number = parseInt(prompt("Type a number: "))
while (isNaN(number)) {
    console.log("The provided value is not a number.")
    number = parseInt(prompt("Type a number: "))
}

console.log("2 * ", number, " = ", 2 * number)
number = None
while (number == None):
    try:
        # int() / float()
        number = int(input("Type a number: "))
    except ValueError:
        print("The provided value is not a number.")

print("2 * ", number, " = ", 2 * number)
print("Type a number: ")
local number = tonumber(io.read("*line"))
while (number == nil) do
    print("The provided value is not a number.")
    print("Type a number: ")
    number = tonumber(io.read("*line"))
end

print("2 * ", number, " = ", 2 * number)
extends Node

func _ready():
    # Número definido no código, pois GDScript não permite leitura via terminal.
    var valor = "Franco"
    var counter = 0
    # is_valid_float() / is_valid_integer()
    while (not valor.is_valid_integer()):
        print("The provided value is not a number.")
        if (counter < 3):
            counter += 1
        else:
            valor = "1234"

    var number = int(valor)
    print("2 * ", number, " = ", 2 * number)

Input validation is a good example of a problem that can be solved with a repetition structure with a check at the end. Try to modify the solution using do while, repeat until or simulating the repetition at the end. Also consider how you would modify the solution to write a different message from the second request on.

Accumulators

Variables in repetition structure used to store partial results from iterations of interest are called accumulators. Accumulators are commonly used to count element, store summations, create strings, arrays, data structures and other composite data types.

For instance, image a situation on which:

  1. Five numbers should be requested from the end-user of the program;
  2. There is a wish to know how many of those numbers are negative or even;
  3. The number of identified numbers must be informed;
  4. The sum of negative or even numbers must also be informed.
let negative_count = 0
let even_count = 0
let negative_or_even_count = 0
let negative_or_even_count = 0
for (let counter = 0; counter < 5; ++counter) {
    let number = parseInt(prompt("Type a number: "))
    let negative_number = (number < 0)
    // JavaScript returns -0 for the remainder of a negative number division.
    // To avoid the problem, the absolute value of the number can be used
    let even_number = ((Math.abs(number) % 2) == 0)
    if (negative_number) {
        ++negative_count
    }

    if (even_number) {
        ++even_count
    }

    if (negative_number || even_number) {
        ++negative_or_even_count
        negative_or_even_count += number
    }
}

console.log("Total of negative numbers: ", negative_count)
console.log("Total of even numbers: ", even_count)
console.log("Total of negative or even numbers: ", negative_or_even_count)
console.log("Sum of negative or even numbers: ", negative_or_even_count)
negative_count = 0
even_count = 0
negative_or_even_count = 0
negative_or_even_count = 0
for counter in range(5):
    number = int(input("Type a number: "))
    negative_number = (number < 0)
    even_number = ((number % 2) == 0)
    if (negative_number):
        negative_count += 1

    if (even_number):
        even_count += 1

    if (negative_number or even_number):
        negative_or_even_count += 1
        negative_or_even_count += number

print("Total of negative numbers: ", negative_count)
print("Total of even numbers: ", even_count)
print("Total of negative or even numbers: ", negative_or_even_count)
print("Sum of negative or even numbers: ", negative_or_even_count)
local negative_count = 0
local even_count = 0
local negative_or_even_count = 0
local negative_or_even_count = 0
for counter = 0, 4 do
    print("Type a number: ")
    local number = io.read("*number")
    local negative_number = (number < 0)
    local even_number = ((number % 2) == 0)
    if (negative_number) then
        negative_count = negative_count + 1
    end

    if (even_number) then
        even_count = even_count + 1
    end

    if (negative_number or even_number) then
        negative_or_even_count = negative_or_even_count + 1
        negative_or_even_count = negative_or_even_count + number
    end
end

print("Total of negative numbers: ", negative_count)
print("Total of even numbers: ", even_count)
print("Total of negative or even numbers: ", negative_or_even_count)
print("Sum of negative or even numbers: ", negative_or_even_count)
extends Node

func _ready():
    var negative_count = 0
    var even_count = 0
    var negative_or_even_count = 0
    var negative_or_even_count = 0
    for counter in range(5):
        # Using the index as an alternative to the lack of terminal input
        var number = counter
        var negative_number = (number < 0)
        var even_number = ((number % 2) == 0)
        if (negative_number):
            negative_count += 1

        if (even_number):
            even_count += 1

        if (negative_number or even_number):
            negative_or_even_count += 1
            negative_or_even_count += number

    print("Total of negative numbers: ", negative_count)
    print("Total of even numbers: ", even_count)
    print("Total of negative or even numbers: ", negative_or_even_count)
    print("Sum of negative or even numbers: ", negative_or_even_count)

In the implementations, negative_count, even_count, negative_or_even_count and negative_or_even_count are accumulators.

There are other ways to avoid counting twice a number that is negative and even. Think in some and modify the solution. Besides, how could you simplify the solution if only the total of negative and even numbers was wanted?

Break and Continue

The command break allows forcing the end of a repetition structure, bypassing the originally defined condition. It is used, for instance, to end a loop defined as while (True). Although I would recommend using it with parsimony (usually only when there is not a better alternative), it is an additional tool to solve problems.

There is also the continue command, which allows ignoring the current iteration and advance to the next repetition. It must be noted that Lua does not define the command.

let counter = 0
while (counter < 5) {
    ++counter
    if (counter < 3) {
        continue
    }

    console.log(counter, ". Hello, my name is Franco!")
}
counter = 0
while (counter < 5):
    counter += 1
    if (counter < 3):
        continue

    print(counter, ". Hello, my name is Franco!")
extends Node

func _ready():
    var counter = 0
    while (counter < 5):
        counter += 1
        if (counter < 3):
            continue

        print(counter, ". Hello, my name is Franco!")

One simple alternative to continue is using a conditional structure, such as if. For an equivalent code that is virtually identical, an empty block with the original conditional can be used. It would also be possible to invert the condition (counter >= 3) to avoid using else.

let counter = 0
while (counter < 5) {
    ++counter
    if (counter < 3) {

    } else {
        console.log(counter, ". Hello, my name is Franco!")
    }
}

counter = 0
while (counter < 5) {
    ++counter
    if (counter >= 3) {
        console.log(counter, ". Hello, my name is Franco!")
    }
}
counter = 0
while (counter < 5):
    counter += 1
    if (counter < 3):
        pass
    else:
        print(counter, ". Hello, my name is Franco!")

counter = 0
while (counter < 5):
    counter += 1
    if (counter >= 3):
        print(counter, ". Hello, my name is Franco!")
local counter = 0
while (counter < 5) do
    counter = counter + 1
    if (counter < 3) then

    else
        print(counter, ". Hello, my name is Franco!")
    end
end

counter = 0
while (counter < 5) do
    counter = counter + 1
    if (counter >= 3) then
        print(counter, ". Hello, my name is Franco!")
    end
end
extends Node

func _ready():
    var counter = 0
    while (counter < 5):
        counter += 1
        if (counter < 3):
            pass
        else:
            print(counter, ". Hello, my name is Franco!")

    counter = 0
    while (counter < 5):
        counter += 1
        if (counter >= 3):
            print(counter, ". Hello, my name is Franco!")

As Python and GDScript does not allow creating empty blocks, pass can be used as an empty statement.

Furthermore, it should be noted that break and continue are not, necessarily, equivalent. To observe the difference, you can run one of the following programs and compare the results.

console.log("Continue")
let counter = 0
while (counter < 5) {
    ++counter
    if (counter === 3) {
        continue
    }

    console.log(counter, ". Hello, my name is Franco!")
}

console.log("Break")
counter = 0
while (counter < 5) {
    ++counter
    if (counter === 3) {
        break
    }

    console.log(counter, ". Hello, my name is Franco!")
}
print("Continue")
counter = 0
while (counter < 5):
    counter += 1
    if (counter == 3):
        continue

    print(counter, ". Hello, my name is Franco!")

print("Break")
counter = 0
while (counter < 5):
    counter += 1
    if (counter == 3):
        break

    print(counter, ". Hello, my name is Franco!")
extends Node

func _ready():
    print("Continue")
    var counter = 0
    while (counter < 5):
        counter += 1
        if (counter == 3):
            continue

        print(counter, ". Hello, my name is Franco!")

    print("Break")
    counter = 0
    while (counter < 5):
        counter += 1
        if (counter == 3):
            break

        print(counter, ". Hello, my name is Franco!")

The use of continue only ignores the repetition for the value 3, although break ends the repetitions when counter stores the value 3.

In short, some languages define continue and break. However, often there are alternatives to avoid their use that usually can result in code that is simpler to read.

Nested Repetition Structures

As it was possible to nest conditional structures, it is also possible to nest repetition structures. A very simple example of nested structures is the creation of a multiplication (times) table.

for (let multiplier = 0; multiplier < 11; ++multiplier) {
    console.log("Multiplication table for ", multiplier)
    for (let multiplicand = 0; multiplicand < 11; ++multiplicand) {
        console.log(multiplier, " * ", multiplicand, " = ", multiplier * multiplicand)
    }
    console.log("") // Add a blank line to separate results.
}
for multiplier in range(11):
    print("Multiplication table for ", multiplier)
    for multiplicand in range(11):
        print(multiplier, " * ", multiplicand, " = ", multiplier * multiplicand)
    print("") # Add a blank line to separate results.
for multiplier = 0, 10 do
    print("Multiplication table for ", multiplier)
    for multiplicand = 0, 10 do
        print(multiplier, " * ", multiplicand, " = ", multiplier * multiplicand)
    end
    print("") -- Add a blank line to separate results.
end
extends Node

func _ready():
    for multiplier in range(11):
        print("Multiplication table for ", multiplier)
        for multiplicand in range(11):
            print(multiplier, " * ", multiplicand, " = ", multiplier * multiplicand)
        print("") # Add a blank line to separate results.

The implementations in Python and GDscript use the conventions to omit the starting value and increment; thus, they are equivalent to range(0, 11, 1). The implementation in Lua omits the increment, and it is equivalent to for multiplier = 0, 10, 1.

I would recommend doing a trace table to understand for nested repetition structures work. It should be noted that each external loop performs the next iteration only after all the repetitions of the inner loop(s) ends(end). In fact, the program has performed 121 multiplications: 11 multiplications for each possible multipler for the most external loop.

Computational Complexity, Big Oh and Big Theta

The example of multiplication tables using nested repetition structures shows that, with few lines of code, it is possible to create programs that potentially perform hundreds of operations. In fact, by increasing the final limits, the program could perform thousands, millions, billions of repetitions. Then, it is up to the question: are there limits for the quantity of operations?

The computational complexity (or algorithmic complexity) studies complexity of algorithms.

For a simple introduction that is sufficient to answer the question, although intuitive to show how the use of nested repetition structures multiply the number of operations that will be performed in a program, analyze the next program. Add or remove levels to verify how the number of repetitions will increase or decrease by a factor of REPETITIONS. The only requirement is that the line incrementing the counter must be in the last level of repetition.

const REPETITIONS = 10
let total_repetitions = 0
for (let level_1 = 0; level_1 < REPETITIONS; ++level_1) {
    for (let level_2 = 0; level_2 < REPETITIONS; ++level_2) {
        for (let level_3 = 0; level_3 < REPETITIONS; ++level_3) {
            for (let level_4 = 0; level_4 < REPETITIONS; ++level_4) {
                for (let level_5 = 0; level_5 < REPETITIONS; ++level_5) {
                    ++total_repetitions
                }
            }
        }
    }
}

console.log(total_repetitions)
from typing import Final

REPETITIONS: Final = 10
total_repetitions = 0
for level_1 in range(REPETITIONS):
    for level_2 in range(REPETITIONS):
        for level_3 in range(REPETITIONS):
            for level_4 in range(REPETITIONS):
                for level_5 in range(REPETITIONS):
                    total_repetitions += 1

print(total_repetitions)
local REPETITIONS <const> = 10
local total_repetitions = 0
for level_1 = 1, REPETITIONS do
    for level_2 = 1, REPETITIONS do
        for level_3 = 1, REPETITIONS do
            for level_4 = 1, REPETITIONS do
                for level_5 = 1, REPETITIONS do
                    total_repetitions = total_repetitions + 1
                end
            end
        end
    end
end

print(total_repetitions)
extends Node

const REPETITIONS = 10

func _ready():
    var total_repetitions = 0
    for level_1 in range(REPETITIONS):
        for level_2 in range(REPETITIONS):
            for level_3 in range(REPETITIONS):
                for level_4 in range(REPETITIONS):
                    for level_5 in range(REPETITIONS):
                        total_repetitions += 1

    print(total_repetitions)

The previous examples belong to a complexity called polynomial (P class). In this case, it is possible to calculate the number of performed instructions by raising REPETITIONS to the power of the number of levels:

  • For one level, the program would perform REPETITIONS increments (10, with the value of the constant). The program would have linear complexity, expressed in Big O (read as big oh) notation as or in Big Theta notation as ;
  • For two levels, the program would perform REPETITIONS times REPETITIONS increments (100, with the value of the constant). The program would have quadratic complexity, expressed in notation as or ;
  • For three levels, the program would perform REPETITIONS times REPETITIONS times REPETITIONS increments (1000, with the value of the constant). The program would have cubic complexity, expressed in notation as or . From this level on, the complexity is usually called polynomial;
  • For four levels, 10000 increments. The program would have polynomial complexity, expressed in notation as or ;
  • For five levels, the program 100000 increments. The program would have polynomial complexity, expressed in notation as or .

The Big Oh and Bit Theta notations are also known as asymptotic notations. The Big Theta notation requires more rigour than the Big Oh notation, as it provides a number the is closer to the real one. In the case of Big Oh, it suffices to define a superior limit for which the behavior of a function converges, to estimate the growth rate considering the size of inputs. In other words, Big Oh estimates the worst possible case for the execution of the program. Big Theta estimates the average case, which normally (although not always) will happen the program is run. There is also a third notation called Big Omega (), used the estimate the best case.

In this example, the number of instructions will always be equal for a very same value of REPETITIONS, which means that all notations will have the same result. Considering REPETITIONS as the size of the input, the exponential growth of the number of required operations can be estimated for each new level that is considered. If REPETITIONS was set with a value such as 1000 instead of 10, the number of required operations would grow much faster. In the 5 levels, repetitions would be performed. In other words, 1.000.000.000.000.000 (one quadrillion) of repetitions.

In fact, if you change the value of the constant in the program to 1000 instead of 10, the program will take a significant time to end. For instance, let us assume that each repetition would take 1 nanosecond ( seconds). By multiplying , that is, 1.000.000 seconds, which, divided by 3600 (number of seconds in an hour), would result in about 277,78 hours.

In other words, current computers have limits and problems that require the use of many nest repetition structures can become computational intractable fast. Depending on the size of the complexity class, the best results with current computers can only be approximations of the exact ones.

If, by curiosity, you want to estimate the time that your computer takes to perform a repetition of the program that was defined, you can measure the required time using a performance counter or a timer. For a result that is closer to the real one, it is ideal to repeat the code snippet several times to avoid interference.

For example:

  • In JavaScript, performance.now() (documentation) can be used. The time is measured in milliseconds ( seconds).
  • In Python, time.perf_counter() (documentation) can be used. time.perf_counter() measures the time in seconds using a high precision timer.
  • In Lua, there is no high precision timer available by default. One alternative is to obtain the time and subtract it to approximate the result. To do this, os.time() can be used to retrieve the current time of the machine (documentation) and os.difftime() can calculate the time difference in seconds (documentation). Unfortunately, the maximum precision is seconds, which means that the code will have to be repeated until it takes some seconds to finish.
  • In GDScript, OS.get_ticks_usec() can provide times in microseconds ( seconds; documentation) or OS.get_ticks_msec() can provide time in milliseconds (documentation) can be used.

Only for curiosity, an implementation is C++ is also provided (although it will not be explained).

const REPETITIONS = 10
let total_repetitions = 0
beginning = performance.now()
for (let level_1 = 0; level_1 < REPETITIONS; ++level_1) {
    for (let level_2 = 0; level_2 < REPETITIONS; ++level_2) {
        for (let level_3 = 0; level_3 < REPETITIONS; ++level_3) {
            for (let level_4 = 0; level_4 < REPETITIONS; ++level_4) {
                for (let level_5 = 0; level_5 < REPETITIONS; ++level_5) {
                    ++total_repetitions
                }
            }
        }
    }
}

end = performance.now()
console.log("Total of repetitions: ", total_repetitions)
time_single_repetition_seconds = (end - beginning) / total_repetitions * 1000.0
console.log("Time per repetition:", time_single_repetition_seconds, " s (", time_single_repetition_seconds * 1.0e9 ," ns).")
import time
from typing import Final

REPETITIONS: Final = 10
total_repetitions = 0
beginning = time.perf_counter()
for level_1 in range(REPETITIONS):
    for level_2 in range(REPETITIONS):
        for level_3 in range(REPETITIONS):
            for level_4 in range(REPETITIONS):
                for level_5 in range(REPETITIONS):
                    total_repetitions += 1

end = time.perf_counter()
print("Total of repetitions: ", total_repetitions)
time_single_repetition_seconds = (end - beginning) / total_repetitions
print("Time per repetition:", time_single_repetition_seconds, " s (", time_single_repetition_seconds * 1.0e9 ," ns).")
local REPETITIONS <const> = 10
local total_repetitions = 0
local beginning = os.time()

-- Additional repetitions to make the execution take some seconds.
for i = 1, 1000 do    for level_1 = 1, REPETITIONS do
        for level_2 = 1, REPETITIONS do
            for level_3 = 1, REPETITIONS do
                for level_4 = 1, REPETITIONS do
                    for level_5 = 1, REPETITIONS do
                        total_repetitions = total_repetitions + 1
                    end
                end
            end
        end
    end
end

local end_time = os.time()
print(total_repetitions)
local time_single_repetition_seconds = os.difftime(end_time, beginning) / total_repetitions
print("Time per repetition:", time_single_repetition_seconds, " s (", time_single_repetition_seconds * 1.0e9 ," ns).")
extends Node

const REPETITIONS = 10

func _ready():
    var total_repetitions = 0
    var beginning = OS.get_ticks_usec()
    for level_1 in range(REPETITIONS):
        for level_2 in range(REPETITIONS):
            for level_3 in range(REPETITIONS):
                for level_4 in range(REPETITIONS):
                    for level_5 in range(REPETITIONS):
                        total_repetitions += 1

    var end = OS.get_ticks_usec()
    print("Total of repetitions: ", total_repetitions)
    var time_single_repetition_seconds = (end - beginning) / (1.0e6 * total_repetitions)
    print("Time per repetition:", time_single_repetition_seconds, " s (", time_single_repetition_seconds * 1.0e9 ," ns).")
    # Custom formatting to show decimal values with more places (15).
    print("Time per repetition: %0.15f s." % time_single_repetition_seconds)
// g++ main.c && ./a.out

#include <chrono>
#include <iomanip>
#include <iostream>

int main()
{
    const int REPETITIONS = 10;
    // Inteiro normalmente definido com 64 ou 128 bits.
    long long total_repetitions = 0;
    auto beginning = std::chrono::high_resolution_clock::now();
    for (int level_1 = 0; level_1 < REPETITIONS; ++level_1)
    {
        for (int level_2 = 0; level_2 < REPETITIONS; ++level_2)
        {
            for (int level_3 = 0; level_3 < REPETITIONS; ++level_3)
            {
                for (int level_4 = 0; level_4 < REPETITIONS; ++level_4)
                {
                    for (int level_5 = 0; level_5 < REPETITIONS; ++level_5)
                    {
                        ++total_repetitions;
                    }
                }
            }
        }
    }

    auto end = std::chrono::high_resolution_clock::now();
    double time_single_repetition_seconds =
        std::chrono::duration<double>(end - beginning).count() / total_repetitions;
    std::cout << std::setprecision(15)
              << "Time per repetition: " << time_single_repetition_seconds << " s ("
              << time_single_repetition_seconds * 1.0e9 << " ns)." << std::endl;

    return 0;
}

Something to note is that, depending on the value that is chosen for REPETITIONS, overflow can happen in some programming languages (if total_repeatitions exceed the maximum integer value that can be stored).

In the Lua implementation, it is important noticing that the REPETITIONS value was not modified, although the code was repeated 1000 times. Thus, the code was repeated times, that is, 100,000,000 (one hundred millions; a number that is, thus, 10 million of times smaller than ).

For curiosity, the results in my computer were:

  • JavaScript em Firefox: 259.99999999999994 nanoseconds (ns) per repetition;
  • Python: 363.8855700046406 ns per repetition;
  • Lua: 9.99000999001 ns per repetition;
  • GDScript: 81.34 ns per repetition. The implementation in GDScript uses an additional call to write the result as the first result (0) is a round error from print(). To work around it, the desired number of decimal places to write the number can be specified, as done in the following line of code.

Therefore, it is interesting to note that, in this example, Lua is significantly faster than all other considered programming languages.

As Lua and the other programming languages are interpreted (as LuaJIT was not used), the code in a compiled language such as C++ usually could be even faster. As a curiosity, the execution of the previous program in C++ took 1.33741e-09 s (1.33741 ns) per repetition in the same machine, using a standard compilation without optimizations.

For more precise results, one can follow the same strategy used in Lua to increase the number of repetitions using an external loop.

The created program is very simple, and it is not representative for real use case scenarios. However, although the difference shall vary for more complex programs, the comparison illustrates that compiled languages are usually faster than interpreted ones. Furthermore, the example also shows why Lua is commonly used as a scripting language for games: even the standard interpreter is quite fast for the standards of interpreted languages.

Examples

Repetition structures can be hard to understand and use by beginners in programming. A potentially helpful tip is thinking in solving a problem generically for a single element, as if one was creating a subroutine. Next, one uses the repetition structure to apply the solution for several values. Besides beneficial, thinking and programming this way lead to modular code.

Factorial

The recursive implementation to calculate factorial was first presented as a subroutine. With repetition structures, it is possible to implement an iterative solution for the problem.

As mentioned in the cited page, from the definition:

"A factorial number is a number generated by the expression , with (read as zero factorial) defined as and a natural number. For instance, ."

A careful observation reveals that the definition and the example constitute a sequence of numbers that can be generated by a repetition structure. It is, simply, the product notation of a countdown from the number until , which corresponds to .

let number = 5
let factorial = 1
for (let multiplier = number; multiplier > 0; --multiplier) {
    factorial = factorial * multiplier
}

console.log(factorial)
number = 5
factorial = 1
for multiplier in range(number, 0, -1):
    factorial = factorial * multiplier

print(factorial)
local number = 5
local factorial = 1
for multiplier = number, 1, -1 do
    factorial = factorial * multiplier
end

print(factorial)
extends Node

func _ready():
    var number = 5
    var factorial = 1
    for multiplier in range(number, 0, -1):
        factorial = factorial * multiplier

    print(factorial)

Optionally, it would be possible to end the repetition with a last multiplication by 2 instead of 1, as a multiplication by 1 does not change the result. Furthermore, as the multiplication is associative and commutative, the implementation can increment the numbers in ascending order. The next examples perform both changes.

let number = 5
let factorial = 1
for (let multiplier = 2; multiplier <= number; ++multiplier) {
    factorial = factorial * multiplier
}

console.log(factorial)
number = 5
factorial = 1
for multiplier in range(2, number + 1):
    factorial = factorial * multiplier

print(factorial)
local number = 5
local factorial = 1
for multiplier = 2, number do
    factorial = factorial * multiplier
end

print(factorial)
extends Node

func _ready():
    var number = 5
    var factorial = 1
    for multiplier in range(2, number + 1):
        factorial = factorial * multiplier

    print(factorial)

Thus, when one is working with operations that are commutative and associative, it can be convenient to change the order of the factors to make the implementation of a solution easier. Nevertheless, some data and operations are not commutative, such as a multiplication of matrices. Therefore, it is convenient taking the proper care for each problem.

Repetition Structures with Subroutines

To improve the solution created to calculate the factorial, one can add conditional structures to handle errors and also refactor the solution as a subroutine. For the function, number can become an input parameter, while factorial can the result.

function factorial(number) {
    if (number < 0) {
        return -1
    }

    let factorial = 1
    for (let multiplier = 2; multiplier <= number; ++multiplier) {
        factorial *= multiplier
    }

    return factorial
}

console.log(factorial(5))
def factorial(number):
    if (number < 0):
        return -1

    factorial = 1
    for multiplier in range(2, number + 1):
        factorial *= multiplier

    return factorial

print(factorial(5))
function factorial(number)
    if (number < 0) then
        return -1
    end

    local factorial = 1
    for multiplier = 2, number do
        factorial = factorial * multiplier
    end

    return factorial
end

print(factorial(5))
extends Node

func factorial(number):
    if (number < 0):
        return -1

    var factorial = 1
    for multiplier in range(2, number + 1):
        factorial *= multiplier

    return factorial

func _ready():
    print(factorial(5))

It is interesting to note that the defined function use many concepts studied hitherto.

However, although JavaScript, Python, Lua and GDScript allow defining a same name for a function and a variable, this can be confusing. Thus, if one wishes, she/he can rename the variable factorial to something like result.

Besides renaming the variable, another opportunity to illustrate another use of repetition structure consists of printing a list of factorial numbers using a loop that calls the function factorial() for several values.

function factorial(number) {
    if (number < 0) {
        return -1
    }

    let result = 1
    for (let multiplier = 2; multiplier <= number; ++multiplier) {
        result *= multiplier
    }

    return result
}

for (let i = 0; i < 26; ++i) {
    console.log(i, "! = ", factorial(i))
}
def factorial(number):
    if (number < 0):
        return -1

    result = 1
    for multiplier in range(2, number + 1):
        result *= multiplier

    return result

for i in range(26):
    print(i, "! = ", factorial(i))
function factorial(number)
    if (number < 0) then
        return -1
    end

    local result = 1
    for multiplier = 2, number do
        result = result * multiplier
    end

    return result
end

for i = 0, 25 do
    print(i, "! = ", factorial(i))
end
extends Node

func factorial(number):
    if (number < 0):
        return -1

    var result = 1
    for multiplier in range(2, number + 1):
        result *= multiplier

    return result

func _ready():
    for i in range(26):
        print(i, "! = ", factorial(i))

A table lists factorial numbers from to . It is a good way to identify the occurrence of overflows in integer numbers. Depending on the programming language (that is, if the language does not provide integer numbers with arbitrary precision), some results can be negative or less than the previous ones, showing the occurrence of overflow.

Summation and Mathematical Series

One way to calculate the Math constant (pi, approximately 3.141592) is to multiply by 4 the following mathematical series:

The series is infinite; the calculated value approximates the value of . The greater the chosen number of terms, the closer the result will be of the value of .

With the programming knowledge acquired hitherto, it already is possible to define an algorithm to implement it. To do this, the first step is trying to identify a pattern for each term.

One adjustment for the first term can help to identify of the patterns.

A careful examination of the formula reveals that:

  1. The numerator for each term is always 1;
  2. The denominator for each term is incremented 2 by 2;
  3. The operation alternates between a subtraction and an addition every new term.

The second item can be implemented with a repetition structure that increment a counter by 2 each step. The result of the expression can be stored in an accumulator. The number of terms is of your choice. For instance, you can choose to use ten, a thousand, or a million of terms. You can also define a constant. The greater the number of terms, the more accurate the result will be; however, the time required to calculate the result will also increase.

const NUMBER_TERMS = 1000
let pi_4 = 0.0

let numerator = 1.0
let denominator = 1.0
for (let term = 0; term < NUMBER_TERMS; ++term) {
    if (term % 2 === 0) {
        pi_4 += numerator / denominator
    } else {
        pi_4 -= numerator / denominator
    }

    // console.log("PI/4 for ", term, " term(s): ", 4.0 * pi_4)

    denominator += 2.0
}

let pi = 4.0 * pi_4

console.log("PI/4 = ", pi_4)
console.log("PI = ", pi)
from typing import Final

NUMBER_TERMS: Final = 1000
pi_4 = 0.0

numerator = 1.0
denominator = 1.0
for term in range(NUMBER_TERMS):
    if (term % 2 == 0):
        pi_4 += numerator / denominator
    else:
        pi_4 -= numerator / denominator

    # print("PI/4 for ", term, " term(s): ", 4.0 * pi_4)

    denominator += 2.0

pi = 4.0 * pi_4

print("PI/4 = ", pi_4)
print("PI = ", pi)
local NUMBER_TERMS <const> = 1000
local pi_4 = 0.0

local numerator = 1.0
local denominator = 1.0
for term = 0, (NUMBER_TERMS - 1) do
    if (term % 2 == 0) then
        pi_4 = pi_4 + (numerator / denominator)
    else
        pi_4 = pi_4 - (numerator / denominator)
    end

    -- print("PI/4 for ", term, " term(s): ", 4.0 * pi_4)

    denominator = denominator + 2.0
end

local pi = 4.0 * pi_4

print("PI/4 = ", pi_4)
print("PI = ", pi)
extends Node

const NUMBER_TERMS = 1000

func _ready():
    var pi_4 = 0.0

    var numerator = 1.0
    var denominator = 1.0
    for term in range(NUMBER_TERMS):
        if (term % 2 == 0):
            pi_4 += numerator / denominator
        else:
            pi_4 -= numerator / denominator

        # print("PI/4 for ", term, " term(s): ", 4.0 * pi_4)

        denominator += 2.0

    var pi = 4.0 * pi_4

    print("PI/4 = ", pi_4)
    print("PI = ", pi)

If one wishes to observe the partial result for every step of the loop, she/he can remove the comment inside the repetition structure. It is interesting noticing that the execution time of the program will be higher if the intermediate results are written. This happens because input and output operations normally requires greater times per instruction than logic, relational or arithmetic operations.

Guess Words

Most examples hitherto use numbers and Math, as they are simpler to operate. However, repetition structures can be used to manipulate any data type. An example was the loop that ended when the program received the value end.

A second simple example is an algorithm that requests a person to guess a word. The implementation can provide some tips after certain numbers of guesses, besides imposing a maximum limit of guesses. The tips are convenient because, at this time, it still was not discussed how to verify individual characters in a string. Thus, guess could be any valid string, which would make the game harder.

As this will be one of the first programs that do not involve a direct translation from a formula or expression, it is convenient to practice using computational thinking to create the solution. The goal of the implementation is transforming the problem into something that the computer can follow to achieve the solution.

What is required to implement a game of guessing a word?

In the simplest implementation:

  1. Store the secret word;
  2. Request a guess;
  3. Compare the guess with the secret word:
    • If the guess is correct, the game ends with a victory;
    • If the guess is incorrect, the game ends with a defeat.

For the first version, a comparison and/or conditional structure suffices to determine the result. Try to implement a solution before consulting the following example. To show the solution, click on "Show/Hide the Partial Solution".

Show/Hide the Partial Solution

const SECRET_WORD = "Franco"
const LOWERCASE_SECRET_WORD = SECRET_WORD.toLowerCase()

let guess = ""
guess = prompt("Choose a word.")
if (guess.toLowerCase() === LOWERCASE_SECRET_WORD) {
    console.log("Congratulations!")
    console.log("You have guessed correct in your first attempt!")
    console.log("You have superpowers, are very lucky, have or read the source code.")
} else {
    console.log("Oh, dear!")
    console.log("The word was ", SECRET_WORD)
}
from typing import Final

SECRET_WORD: Final = "Franco"
LOWERCASE_SECRET_WORD: Final = SECRET_WORD.lower()

guess = input("Choose a word. ")
if (guess.lower() == LOWERCASE_SECRET_WORD):
    print("Congratulations!")
    print("You have guessed correct in your first attempt!")
    print("You have superpowers, are very lucky, have or read the source code.")
else:
    print("Oh, dear!")
    print("The word was ", SECRET_WORD)
local SECRET_WORD <const> = "Franco"
local LOWERCASE_SECRET_WORD <const> = SECRET_WORD:lower()

print("Choose a word.")
local guess = io.read("*line")
if (guess:lower() == LOWERCASE_SECRET_WORD) then
    print("Congratulations!")
    print("You have guessed correct in your first attempt!")
    print("You have superpowers, are very lucky, have or read the source code.")
else
    print("Oh, dear!")
    print("The word was ", SECRET_WORD)
end
extends Node

const SECRET_WORD = "Franco"
const LOWERCASE_SECRET_WORD = "franco" # SECRET_WORD.to_lower()

func _ready():
    var guess = ""
    if (guess.to_lower() == LOWERCASE_SECRET_WORD):
        print("Congratulations!")
        print("You have guessed correct in your first attempt!")
        print("You have superpowers, are very lucky, have or read the source code.")
    else:
        print("Oh, dear!")
        print("The word was ", SECRET_WORD)

The simplest implementation serves as the core of the solution, prototype and proof of concept about the viability of the solution. Hereafter, you can improve it with more features.

For a first improvement, the program could request new guesses until a correct one.

  1. Store the secret word;
  2. Repeat:
    1. Request the input of a new guess;
    2. Compare the guess with the secret word:
      • If the guess is correct, the game ends with a victory;
      • If the guess is incorrect, the program returns to Step 2.1.

Try to implement a solution before consulting the following example.

Show/Hide the Partial Solution

const SECRET_WORD = "Franco"
const LOWERCASE_SECRET_WORD = SECRET_WORD.toLowerCase()

let guess = ""
while (guess.toLowerCase() !== LOWERCASE_SECRET_WORD) {
    guess = prompt("Choose a word.")
}

console.log("Congratulations!")
from typing import Final

SECRET_WORD: Final = "Franco"
LOWERCASE_SECRET_WORD: Final = SECRET_WORD.lower()

guess = ""
while (guess.lower() != LOWERCASE_SECRET_WORD):
    guess = input("Choose a word. ")

print("Congratulations!")
local SECRET_WORD <const> = "Franco"
local LOWERCASE_SECRET_WORD <const> = SECRET_WORD:lower()

local guess = ""
while (guess:lower() ~= LOWERCASE_SECRET_WORD) do
    print("Choose a word.")
    guess = io.read("*line")
end

print("Congratulations!")
extends Node

const SECRET_WORD = "Franco"
const LOWERCASE_SECRET_WORD = "franco" # SECRET_WORD.to_lower()

func _ready():
    var guess = ""
    while (guess.to_lower() != LOWERCASE_SECRET_WORD):
        guess = "FRANCO"

    print("Congratulations!")

A second improvement could include the definition of a max number of guesses.

  1. Store the secret word;
  2. Store the maximum number of guesses;
  3. Start a guesses counter;
  4. Repeat:
    1. Increment the guesses counter;
    2. Compare the guesses counter with the maximum number allowed:
      • If the number is greater, the program ends with a defeat.
      • If the number is less than or equal:
        1. Request the input of a new guess;
        2. Compare the guess with the secret word:
          • If the guess is correct, the game ends with a victory;
          • If the guess is incorrect, the program returns to Step 4.1.

Try to implement a solution before consulting the following example. In the provided answer, some implementation use repeat instead of while to illustrate the use of a repetition command with verification at the end. If your answer is based in the previous examples using while, you can keep using it instead of changing it.

Show/Hide the Partial Solution

const SECRET_WORD = "Franco"
const LOWERCASE_SECRET_WORD = SECRET_WORD.toLowerCase()
const MAXIMUM_ATTEMPTS_NUMBER = 150

let guess = ""
let number_attempts = 0
let correct_guess = false
do {
    ++number_attempts
    guess = prompt("Choose a word.")
    correct_guess = (guess.toLowerCase() === LOWERCASE_SECRET_WORD)
} while ((!correct_guess) && (number_attempts < MAXIMUM_ATTEMPTS_NUMBER))

if (correct_guess) {
    console.log("Congratulations!")
    if (number_attempts === 1) {
        console.log("You have guessed correct in your first attempt!")
        console.log("You have superpowers, are very lucky, have or read the source code.")
    } else {
        console.log("You have guessed the word correctly after ", number_attempts, " guesses.")
    }
} else {
    console.log("Oh, dear!")
    console.log("The word was ", SECRET_WORD)
}
from typing import Final

SECRET_WORD: Final = "Franco"
LOWERCASE_SECRET_WORD: Final = SECRET_WORD.lower()
MAXIMUM_ATTEMPTS_NUMBER: Final = 150

guess = ""
number_attempts = 0
correct_guess = False
while ((not correct_guess) and (number_attempts < MAXIMUM_ATTEMPTS_NUMBER)):
    number_attempts += 1
    guess = input("Choose a word. ")
    correct_guess = (guess.lower() == LOWERCASE_SECRET_WORD)

if (correct_guess):
    print("Congratulations!")
    if (number_attempts == 1):
        print("You have guessed correct in your first attempt!")
        print("You have superpowers, are very lucky, have or read the source code.")
    else:
        print("You have guessed the word correctly after ", number_attempts, " guesses.")
else:
    print("Oh, dear!")
    print("The word was ", SECRET_WORD)
local SECRET_WORD <const> = "Franco"
local LOWERCASE_SECRET_WORD <const> = SECRET_WORD:lower()
local MAXIMUM_ATTEMPTS_NUMBER <const> = 150

local guess = ""
local number_attempts = 0
local correct_guess = False
repeat
    number_attempts = number_attempts + 1
    print("Choose a word.")
    guess = io.read("*line")
    correct_guess = (guess:lower() == LOWERCASE_SECRET_WORD)
until ((correct_guess) or (number_attempts >= MAXIMUM_ATTEMPTS_NUMBER))

if (correct_guess) then
    print("Congratulations!")
    if (number_attempts == 1) then
        print("You have guessed correct in your first attempt!")
        print("You have superpowers, are very lucky, have or read the source code.")
    else
        print("You have guessed the word correctly after ", number_attempts, " guesses.")
    end
else
    print("Oh, dear!")
    print("The word was ", SECRET_WORD)
end
extends Node

const SECRET_WORD = "Franco"
const LOWERCASE_SECRET_WORD = "franco" # SECRET_WORD.to_lower()
const MAXIMUM_ATTEMPTS_NUMBER = 150

func _ready():
    var guess = ""
    var number_attempts = 0
    var correct_guess = false
    while ((not correct_guess) and (number_attempts < MAXIMUM_ATTEMPTS_NUMBER)):
        number_attempts += 1
        # guess = input("Choose a word. ")
        correct_guess = (guess.to_lower() == LOWERCASE_SECRET_WORD)

    if (correct_guess):
        print("Congratulations!")
        if (number_attempts == 1):
            print("You have guessed correct in your first attempt!")
            print("You have superpowers, are very lucky, have or read the source code.")
        else:
            print("You have guessed the word correctly after ", number_attempts, " guesses.")
    else:
        print("Oh, dear!")
        print("The word was ", SECRET_WORD)

A third improvement could include tips at certain number of guesses.

  1. Store the secret word;
  2. Store the maximum number of guesses;
  3. Start a guesses counter;
  4. Repeat:
    1. Increment the guesses counter;
    2. Compare the guesses counter with the maximum number allowed:
      • If the number is greater, the program ends with a defeat.
      • If the number is less than or equal:
        1. If the number of the attempt is equal one of pre-defined ones, show a tip;
        2. Request the input of a new guess;
        3. Compare the guess with the secret word:
          • If the guess is correct, the game ends with a victory;
          • If the guess is incorrect, the program returns to Step 4.1.

With the change, the resulting code could resemble the following one.

const SECRET_WORD = "Franco"
const LOWERCASE_SECRET_WORD = SECRET_WORD.toLowerCase()
const MAXIMUM_ATTEMPTS_NUMBER = 150

let guess = ""
let number_attempts = 0
let correct_guess = false
do {
    switch (number_attempts) {
    case 3:
        console.log("The word starts with 'f'.")
        break

    case 5:
        console.log("The word ends with 'o'.")
        break

    case 8:
        console.log("The word has six characters.")
        break

    case 10:
        console.log("The word is in the address of this page.")
        break

    case 30:
        console.log("F _ _ n _ o")
        break

    case 60:
        console.log("F _ a n _ o")
        break

    case 100:
        console.log("F r a n _ o")
        break

    default:
        break
    }

    ++number_attempts
    guess = prompt("Choose a word.")
    correct_guess = (guess.toLowerCase() === LOWERCASE_SECRET_WORD)
} while ((!correct_guess) && (number_attempts < MAXIMUM_ATTEMPTS_NUMBER))

if (correct_guess) {
    console.log("Congratulations!")
    if (number_attempts === 1) {
        console.log("You have guessed correct in your first attempt!")
        console.log("You have superpowers, are very lucky, have or read the source code.")
    } else {
        console.log("You have guessed the word correctly after ", number_attempts, " guesses.")
    }
} else {
    console.log("Oh, dear!")
    console.log("The word was ", SECRET_WORD)
}
from typing import Final

SECRET_WORD: Final = "Franco"
LOWERCASE_SECRET_WORD: Final = SECRET_WORD.lower()
MAXIMUM_ATTEMPTS_NUMBER: Final = 150

guess = ""
number_attempts = 0
correct_guess = False
while ((not correct_guess) and (number_attempts < MAXIMUM_ATTEMPTS_NUMBER)):
    if (number_attempts == 3):
        print("The word starts with 'f'.")
    elif (number_attempts == 5):
        print("The word ends with 'o'.")
    elif (number_attempts == 8):
        print("The word has six characters.")
    elif (number_attempts == 10):
        print("The word is in the address of this page.")
    elif (number_attempts == 30):
        print("F _ _ n _ o")
    elif (number_attempts == 60):
        print("F _ a n _ o")
    elif (number_attempts == 100):
        print("F r a n _ o")

    number_attempts += 1
    guess = input("Choose a word. ")
    correct_guess = (guess.lower() == LOWERCASE_SECRET_WORD)

if (correct_guess):
    print("Congratulations!")
    if (number_attempts == 1):
        print("You have guessed correct in your first attempt!")
        print("You have superpowers, are very lucky, have or read the source code.")
    else:
        print("You have guessed the word correctly after ", number_attempts, " guesses.")
else:
    print("Oh, dear!")
    print("The word was ", SECRET_WORD)
local SECRET_WORD <const> = "Franco"
local LOWERCASE_SECRET_WORD <const> = SECRET_WORD:lower()
local MAXIMUM_ATTEMPTS_NUMBER <const> = 150

local guess = ""
local number_attempts = 0
local correct_guess = False
repeat
    if (number_attempts == 3) then
        print("The word starts with 'f'.")
    elseif (number_attempts == 5) then
        print("The word ends with 'o'.")
    elseif (number_attempts == 8) then
        print("The word has six characters.")
    elseif (number_attempts == 10) then
        print("The word is in the address of this page.")
    elseif (number_attempts == 30) then
        print("F _ _ n _ o")
    elseif (number_attempts == 60) then
        print("F _ a n _ o")
    elseif (number_attempts == 100) then
        print("F r a n _ o")
    end

    number_attempts = number_attempts + 1
    print("Choose a word.")
    guess = io.read("*line")
    correct_guess = (guess:lower() == LOWERCASE_SECRET_WORD)
until ((correct_guess) or (number_attempts >= MAXIMUM_ATTEMPTS_NUMBER))

if (correct_guess) then
    print("Congratulations!")
    if (number_attempts == 1) then
        print("You have guessed correct in your first attempt!")
        print("You have superpowers, are very lucky, have or read the source code.")
    else
        print("You have guessed the word correctly after ", number_attempts, " guesses.")
    end
else
    print("Oh, dear!")
    print("The word was ", SECRET_WORD)
end
extends Node

const SECRET_WORD = "Franco"
const LOWERCASE_SECRET_WORD = "franco" # SECRET_WORD.to_lower()
const MAXIMUM_ATTEMPTS_NUMBER = 150

func _ready():
    var guess = ""
    var number_attempts = 0
    var correct_guess = false
    while ((not correct_guess) and (number_attempts < MAXIMUM_ATTEMPTS_NUMBER)):
        match (number_attempts):
            3:
                print("The word starts with 'f'.")
            5:
                print("The word ends with 'o'.")
            8:
                print("The word has six characters.")
            10:
                print("The word is in the address of this page.")
            30:
                print("F _ _ n _ o")
            60:
                print("F _ a n _ o")
            100:
                print("F r a n _ o")
            127:
                guess = "FRANCO"

        number_attempts += 1
        # guess = input("Choose a word. ")
        correct_guess = (guess.to_lower() == LOWERCASE_SECRET_WORD)

    if (correct_guess):
        print("Congratulations!")
        if (number_attempts == 1):
            print("You have guessed correct in your first attempt!")
            print("You have superpowers, are very lucky, have or read the source code.")
        else:
            print("You have guessed the word correctly after ", number_attempts, " guesses.")
    else:
        print("Oh, dear!")
        print("The word was ", SECRET_WORD)

A version with more features could implement the hangman game, asking individual letters and showing the correct guesses at every step. However, it is necessary to learn more about arrays and strings before trying to implement it. These will be discussed in the next entries.

Regardless, the most important is noticing how computational solutions can be built in an iterative way, with increasingly complex prototypes until the desired solution (or a satisfactory one).

Random (Pseudorandom) Numbers and Random Number Generators (RNGs / PRNGs)

Although it is not yet possible to add more advanced features in the program to guess words, it is possible to further improve the solution if it uses numbers instead. Thus, the previous solution can be modified to guess a number.

Instead of defining a fixed number as the secret in the program, this section introduces a useful resource that is commonly available in subroutines of programming languages: random number generation. More technically, random numbers in programming languages usually are defined as pseudorandom numbers, build using Mathematical or Statistical means.

A Random Number Generator is popularly known by is RNG acronym. To keep the technical term, the acronym PRGN can be used for Pseudorandom Number Generator.

PRNGs are often used in digital games, simulations, procedural content generation, or even to add random (stochastic) behaviors to simple programs.

Program language usually provide three subroutines for basic use of random numbers with uniform distribution (which is potentially biased):

  1. A function to generate pseudorandom numbers for the real data type, normally with values ranging between 0.0 and 1.0 (normally as );
  2. A function to generate pseudorandom numbers for the integer type, normally with values ranging from 0 and a maximum value that is implementation dependent;
  3. A procedure to initialize the seed used to determine the sequence of pseudorandom numbers that will be drawn. The same seed generates the very same sequence of numbers, which can be useful to test a program.

Some languages do not provide the three subroutines in standard libraries, although it is common to exist, at least, the procedure to initialize the seed and one of the functions to generate a number.

  • JavaScript: Math.random() (documentation) generate numbers between . The language does not provide a procedure to choose a seed, nor a function to generate integer numbers. The choice of seed is implementation dependant.
  • Python: the random modulo provides several subroutines to use pseudorandom numbers, including:
  • Lua: (documentation);
    • math.randomseed() (documentation) to set the seed;
    • math.random() (documentation) to generate a decimal value in the range ;
    • math.random(max) (documentation) to draw an integer number between ;
    • math.random(min, max) (documentation) to draw an integer number between ;
  • GDScript: (documentation).
    • rand_seed() (documentation) to set the seed;
    • randomize() (documentation) to let the engine set a seed;
    • randf() (documentation) to generate a decimal value in the range ;
    • randi() (documentation) to draw an integer number. To define an interval, one can use the remainder of a division. For instance, randi() % 10 + 1 generate numbers between ;
    • rand_range(min, max) (documentation) to draws a real value between .

In languages that only provide function to draw decimal values between , it is possible to draw an integer number in belonging to an arbitrary interval using floor(min + random() * (max + 1 - min)). Mathematically: . For numbers in the interval , it suffices to omit the addition of 1.

For languages that do not define intervals for integer numbers, the remainder of division can be used to define arbitrary intervals, as commented for GDScript. Generically: randi() % (maximum_value + 1 - minimum_value) + minimum_value. It is common to round maximum_value down (using floor()) and maximum_value up (using ceil()).

function random_integer(inclusive_minimum, inclusive_maximum) {
    let minimum = Math.ceil(inclusive_minimum)
    let maximum = Math.floor(inclusive_maximum)

    return Math.floor(minimum + Math.random() * (maximum + 1 - minimum))
}

for (let i = 0; i < 10; ++i) {
    // [0, 10]
    let integer_part = random_integer(0, 10)
    // [0.0, 1.0[
    let real_part = Math.random()
    let number = integer_part + real_part
    console.log(integer_part)
    console.log(real_part)
    console.log(number)
    console.log("")
}
import random

random.seed()
for i in range(10):
    # [0, 10]
    integer_part = random.randint(0, 10)
    # [0.0, 1.0[
    real_part = random.random()
    number = integer_part + real_part
    print(integer_part)
    print(real_part)
    print(number)
    print("")
math.randomseed(os.time())
for i = 0, 9 do
    -- [0, 10]
    local integer_part = math.random(0, 10)
    -- [0.0, 1.0[
    local real_part = math.random()
    local number = integer_part + real_part
    print(integer_part)
    print(real_part)
    print(number)
    print("")
end
extends Node

func random_integer(inclusive_minimum, inclusive_maximum):
    var minimum = ceil(inclusive_minimum)
    var maximum = floor(inclusive_maximum)

    # randi(): [0.0, 1.0[
    return randi() % int(maximum + 1 - minimum) + minimum

func _ready():
    randomize() # rand_seed(OS.get_unix_time()) / rand_seed(OS.get_system_time_secs())
    for i in range(10):
        # [0, 10]
        var integer_part = random_integer(0, 10)
        var real_part = randf()
        var number = integer_part + real_part # rand_range(0.0, 10.0)
        print(integer_part)
        print(real_part)
        print(number)
        print("")

The seed is used to define the generated sequence of pseudorandom numbers. If the same seed is always used, the drawn values will always be the same. A practical way to modify the seed at every of the program consists of initializing it using the current time of the machine. Some programming languages do that by default.

If one knows how to draw random numbers, the creation of a program to guess a number is similar to one to guess a word. The ideal is to draw an integer number as the secret for the program, to avoid precision issues. To create the program, it must:

  1. Drawn a number;
  2. Repeat:
    1. Request the input of a guess;
    2. Compare the guess with the drawn number.
      • If the guess is correct, the game ends with a victory.
      • If the guess is incorrect, the program returns to Step 2.1. Optionally, it can be informed whether the drawn number is less than or greater than the last guess.
const SMALLEST_VALUE = 0
const LARGEST_VALUE = 100

function random_integer(inclusive_minimum, inclusive_maximum) {
    let minimum = Math.ceil(inclusive_minimum)
    let maximum = Math.floor(inclusive_maximum)

    return Math.floor(minimum + Math.random() * (maximum + 1 - minimum))
}

let secret_number = random_integer(SMALLEST_VALUE, LARGEST_VALUE)
let smallest_possible_value = SMALLEST_VALUE
let largest_possible_value = LARGEST_VALUE
let guess = SMALLEST_VALUE - 1
while (guess !== secret_number) {
    guess = parseInt(prompt("Choose a number between " + smallest_possible_value + " and " + largest_possible_value + ":"))
    if (guess < secret_number) {
        smallest_possible_value = guess
        console.log("Number is too small; choose a larger one.")
    } else if (guess > secret_number) {
        largest_possible_value = guess
        console.log("Numer is too large; choose a smaller one.")
    }
}

console.log("Congratulations!")
import random
from typing import Final

SMALLEST_VALUE: Final = 0
LARGEST_VALUE: Final = 100

random.seed()

secret_number = random.randint(SMALLEST_VALUE, LARGEST_VALUE)
smallest_possible_value = SMALLEST_VALUE
largest_possible_value = LARGEST_VALUE
guess = SMALLEST_VALUE - 1
while (guess != secret_number):
    guess = int(input("Choose a number between " + str(smallest_possible_value) + " and " + str(largest_possible_value) + ": "))
    if (guess < secret_number):
        smallest_possible_value = guess
        print("Number is too small; choose a larger one.")
    elif (guess > secret_number):
        largest_possible_value = guess
        print("Numer is too large; choose a smaller one.")

print("Congratulations!")
local SMALLEST_VALUE <const> = 0
local LARGEST_VALUE <const> = 100

math.randomseed(os.time())

local secret_number = math.random(SMALLEST_VALUE, LARGEST_VALUE)
local smallest_possible_value = SMALLEST_VALUE
local largest_possible_value = LARGEST_VALUE
local guess = SMALLEST_VALUE - 1
while (guess ~= secret_number) do
    print("Choose a number between " .. smallest_possible_value .. " and " .. largest_possible_value .. ": ")
    guess = io.read("*number")
    if (guess < secret_number) then
        smallest_possible_value = guess
        print("Number is too small; choose a larger one.")
    elseif (guess > secret_number) then
        largest_possible_value = guess
        print("Numer is too large; choose a smaller one.")
    end
end

print("Congratulations!")
extends Node

const SMALLEST_VALUE = 0
const LARGEST_VALUE = 100

func random_integer(inclusive_minimum, inclusive_maximum):
    var minimum = ceil(inclusive_minimum)
    var maximum = floor(inclusive_maximum)

    # randi(): [0.0, 1.0[
    return randi() % int(maximum + 1 - minimum) + minimum

func _ready():
    randomize()

    var secret_number = random_integer(SMALLEST_VALUE, LARGEST_VALUE)
    var smallest_possible_value = SMALLEST_VALUE
    var largest_possible_value = LARGEST_VALUE
    var guess = SMALLEST_VALUE - 1
    while (guess != secret_number):
        print("Choose a number between " + str(smallest_possible_value) + " and " + str(largest_possible_value) + ": ")
        # Although it is not possible to read a value, it is possible to draw a random one.
        guess = random_integer(smallest_possible_value, largest_possible_value)
        print(guess)
        if (guess < secret_number):
            smallest_possible_value = guess
            print("Number is too small; choose a larger one.")
        elif (guess > secret_number):
            largest_possible_value = guess
            print("Numer is too large; choose a smaller one.")

    print("Congratulations!")

To practice, write how many guesses were necessary to find the correct drawn number. You can also:

  • Add special messages for certain numbers of guesses;
  • Avoid the input of values that are not possible anymore;
  • Define difficult levels. For instance:
    • Easy: 10 numbers;
    • Medium: 100 numbers;
    • Hard: 1000 numbers;
    • Try your luck: 100000000 numbers.
  • Request a minimum and maximum value for the drawn.

With a good strategy and a program that informs whether the guess is greater or less than the secret value, you can identify the drawn value in a few guesses. To do this, it suffices to always choose the medium value of the interval, adapting the next choice to the new interval until the guess is correct. For instance, a round with 100000000 possibilities can take around guesses at the worst case. Using the JavaScript interpreter to perform the calculation Math.log2(100000000), this means that you will need, at maximum, 27 guesses to find the correct value.

New Items for Your Inventory

Tools:

  • Performance counter and timer;
  • Pseudorandom Number Generators (PRNGs);
  • Trace table.

Skills:

  • Program debugging using trace table;
  • Creation of repetitions;
  • Use of random numbers;
  • Input validation.

Concepts:

  • Repetitions, iterations and loops;
  • Repetition structures;
  • Repetition with test at the beginning;
  • Repetition with test at the end;
  • Trace table;
  • Infinite loop;
  • Accumulator;
  • Break and continue;
  • Computational complexity;
  • Pseudorandom numbers;
  • Idiomatic code.

Programming resources:

  • Repetition structures;
  • Random numbers;
  • Timers;
  • Brute force solution (in Practice).

Practice

To complement the suggestions of modifications to the provided examples, this section presents exercises to practice. The exercises of mathematical series show to calculate some constants, functions and values from Mathematics.

  1. Create a program that read ten numeric values. Write the maximum and minimum values. Also write whether a negative value was read.

  2. Create a program that implement the power of numbers with integer exponents. You can define the program as a function with the signature power(base: real, exponent: integer). Assume that exponent is a positive value or zero. For instance, to perform power(1.23, 3), you should perform the operation 1.23 * 1.23 * 1.23. Also remember that potencia(x, 0) results 1 for any x different from zero.

  3. Sum a sequence of positive numbers provided by the end user. End the summation when she/he provides the value -1 (or any other negative value). Write the result of the sum.

  4. Modify the previous program for summations. After reading each new value, read a string. If the string is result, show the result. If the string is quit, end the program. Otherwise, request a new value and continue the summation.

  5. The series to calculate a harmonic number in Mathematics can be defined by the following expression:

    Write a program that calculates , , , and . Unlike other series that have been presented, the harmonic series not converge to a specific number. In other words, the result will keep increasing as the number of terms increase, (slowly) approximating infinite.

  6. To get an approximation of , you can use the following series:

    Write a program to calculate the value and compare the result to Math.log(2) em JavaScript. If you do not want to make the call, the value is, approximately, 0.69314718.

  7. To calculate the Euler's number, you can use the following series:

    As the denominator uses a factorial, it is convenient to choose a relatively small number of terms to avoid overflow. For instance, you can assume the calculus for ten terms. The result should be close to Math.E in JavaScript (approximately 2.7182818).

  8. To calculate the cosine of a number, you can use the following series:

    With x an angle in radians. For instance, cos(0) is equal to 1. The cosine of 30° ($ rad) is, approximately, 0.8660254037844387. You can test the values using, for instance, Math.cos(Math.PI / 6.0) in JavaScript.

    If it makes it easier, you can change the first term to write it in function of zero.

  9. Imagine that you want to create a system for a supermarket cashier. Calculate the total price of a purchase in which, at each step, the user provides as input:

    • The unit value of the product;
    • The quantity of items.

    If you want to improve your program, you can provide options:

    1. Sale by item:

      • The unit value of the product;
      • The quantity of items.
    2. Sale by mass (weight):

      • The price of the item per gram ($/g);
      • The mass of the item in grams.

      You can use another unit for weight if you prefer (such as pounds).

    Read new items until the program receives a negative value as the unit price. If you prefer, you can determine another condition to stop the loop.

  10. In the implementation of listing of values of factorial as presented as following, it is possible to calculate the result without requiring the nested repetition (the external would suffice). In other words, the complexity of the solution could be simplified from quadratic to linear. Can you identify how? Modify the code with your solution.

    for (let number = 0; number < 11; ++number) {
        let factorial = 1
        for (let multiplier = 2; multiplier <= number; ++multiplier) {
            factorial *= multiplier
        }
    
        console.log(number, "! = ", factorial)
    }
    for number in range(11):
        factorial = 1
        for multiplier in range(2, number + 1):
            factorial *= multiplier
    
        print(number, "! = ", factorial)
    for number = 0, 10 do
        local factorial = 1
        for multiplier = 2, number do
            factorial = factorial * multiplier
        end
    
        print(number, "! = ", factorial)
    end
    extends Node
    
    func _ready():
        for number in range(11):
            var factorial = 1
            for multiplier in range(2, number + 1):
                factorial *= multiplier
    
            print(number, "! = ", factorial)
  11. To calculate the arithmetic mean of values, you can use the following expression:

    Write a program that read 10 values and write the resulting arithmetic mean. Next, modify the program to make it keep reading values until the input of -1. After the value -1, calculate the arithmetic mean of the values and show the result.

  12. Write a program the simulates rolling a number of dice provided as input. Sum the values and show the result. To roll a die, use a function to obtain a random number between 1 and 6.

  13. Consider a program that simulate rolling dices with a number of faces provided as an input. For example, if the input is 20, roll a die with numbers between 1 and 20. Simulate the rolling of 1000 dices and provide the arithmetic mean of the obtained values.

  14. The calculus of the profit of a fictitious store for a product can be calculated using the following expression:

    Considering that is the price of the product, what is the price that would provide the best profit, that is, the highest value of ? Assume that . Write the highest profit and the price that generated it.

    Although it is possible to find the answer mathematically, it is also possible to find it computationally using a simulation. Calculate all values of the range with an increment e store the most promising results. This technique to solve problems is known in programming as brute force, in which an exhaustive search for the result is performed.

    Note. Some programming languages allow using for only with integer values. For instance, in Python, you can use while instead of for to define the repetitions.

    Show/Hide Solution

    function l(x) {
        let x_2 = x * x
        let x_3 = x_2 * x
        let result = -0.012 * x_3 + 4.56 * x_2 + 77.7 * x + 1234.56
        return result
    }
    
    let best_profit = l(0.01)
    let best_profit_price = l(0.01)
    for (let price = 0.02; price < 1000.00; price += 0.01) {
        let profit = l(price)
        if (profit > best_profit) {
            best_profit = profit
            best_profit_price = price
        }
    }
    
    console.log("Best profit: ", best_profit)
    console.log("Price for the best profit: ", best_profit_price)
    def l(x):
        x_2 = x * x
        x_3 = x_2 * x
        result = -0.012 * x_3 + 4.56 * x_2 + 77.7 * x + 1234.56
        return result
    
    best_profit = l(0.01)
    best_profit_price = l(0.01)
    price = 0.02
    while (price <= 1000.00):
        profit = l(price)
        if (profit > best_profit):
            best_profit = profit
            best_profit_price = price
    
        price += 0.01
    
    print("Best profit: ", best_profit)
    print("Price for the best profit: ", best_profit_price)
    function l(x)
        local x_2 = x * x
        local x_3 = x_2 * x
        local result = -0.012 * x_3 + 4.56 * x_2 + 77.7 * x + 1234.56
        return result
    end
    
    local best_profit = l(0.01)
    local best_profit_price = l(0.01)
    for price = 0.02, 1000.00, 0.1 do
        local profit = l(price)
        if (profit > best_profit) then
            best_profit = profit
            best_profit_price = price
        end
    
        price = price + 0.01
    end
    
    print("Best profit: ", best_profit)
    print("Price for the best profit: ", best_profit_price)
    extends Node
    
    func l(x):
        var x_2 = x * x
        var x_3 = x_2 * x
        var result = -0.012 * x_3 + 4.56 * x_2 + 77.7 * x + 1234.56
        return result
    
    func _ready():
        var best_profit = l(0.01)
        var best_profit_price = l(0.01)
        var price = 0.02
        while (price <= 1000.00):
            var profit = l(price)
            if (profit > best_profit):
                best_profit = profit
                best_profit_price = price
    
            price += 0.01
    
        print("Best profit: ", best_profit)
        print("Price for the best profit: ", best_profit_price)

  15. In conditional structures, one of the exercises described the implementation of a game of Rock, Paper and Scissors.

    With random numbers, you are now able to draw one of the plays. For instance, you can draw the values from 0 to 2 (0, 1 e 2):

    • 0 could be rock;
    • 1 could be paper;
    • 2 could be scissors.

    You can choose any values for numbers that you want. Modify your program.

    To add repetition structures, create a championship in the format best of three. The first person (or machine) that wins two times is declared the winner.

Next Steps

Unlike people, computers never get tired of repeating instructions. They are excellent machines to calculate and repeat. Now that you know both operations, you can use a good part of the potential of the machines.

Thus, the introduction of repetition structures represents a milestone for this material. With what has been discussed so far, it already is possible to solve many problems using programming. Data types, variables, arithmetic, logic and relational operations, input and output, subroutines, conditional and repetition structures are fundamental resources to create programs. Many of these resources will be present in any program that you write.

However, some problems are not yet possible to be solved, while other problems could be more convenient with new resources.

Hitherto, each variable can store a single value. For instance, a program to guess numbers can store the highest and smallest provided values, although it cannot store all of them. Although it is possible to declare a variable per value, it certainly is not convenient.

The next topics will allow solving problems involving more data in a more convenient way. With data structures such as arrays, lists and dictionaries, a single variable will become able to map and store multiple values accessible using a key.

  1. Introduction;
  2. Entry point and program structure;
  3. Output (for console or terminal);
  4. Data types;
  5. Variables and constants;
  6. Input (for console or terminal);
  7. Arithmetic and basic Mathematics;
  8. Relational operations and comparisons;
  9. Logic operations and Boolean Algebra;
  10. Conditional (or selection) structures;
  11. Subroutines: functions and procedures;
  12. Repetition structures (or loops);
  13. Arrays, collections and data structures;
  14. Records (structs);
  15. Files and serialization (marshalling);
  16. Libraries;
  17. Command line input;
  18. Bitwise operations;
  19. Tests and debugging.
  • Informatics
  • Programming
  • Beginner
  • Computational Thinking
  • Learn Programming
  • Python
  • Lua
  • Javascript
  • Godot
  • Gdscript
  • Scratch
  • Flowgorithm