Apr 28, 2026 | 1525 words | 15 min read
13.1.1. Materials#
Repetition Structures#
Repetition structures, also known as iteration structures or loops, allow us to execute a block of code multiple times based on a condition or a sequence. When the number of iterations is known beforehand, we call it a definite loop. When the number of iterations is not known beforehand, we call it an indefinite loop.
- Definite loop
the number of iterations is known beforehand
- Indefinite loop
the number of iterations is not known beforehand
In Python, we have two different keywords we can use to create loops:
while and for. Their usage is summarized below.
01_repetition_structures.py
02_while_loop.py
03_for_loop.py
04_nested_loops.py
05_control_statements.py
The while Loop#
A while loop is used to repeatedly execute a block of statements as long as
its condition expression evaluates to True. They are typically used when the
number of iterations is not known beforehand (i.e. an indefinite loop). However, they
can also be used to create definite loops.
Definite while Loops#
To create a definite while loop, we:
initialize a control variable before entering the loop
use the control variable in the condition expression, entering the loop if the expression evaluates to
True.update the control variable inside the loop such that the condition expression will eventually evaluate to
False
Example:
count = 1 # initialize a control variable
while count <= 5: # check the control variable
print("Count is:", count)
count += 1 # update the control variable
Count is: 1
Count is: 2
Count is: 3
Count is: 4
Count is: 5
Infinite while Loops#
An infinite loop is a loop that never ends. This is usually caused by a mistake in the
code. It occurs when the condition expression never evaluates to False. A
common cause of infinite loops is forgetting to update the control variable inside the
loop.
Example:
count = 1 # initialize a control variable
while count <= 5: # check the control variable
print("Count is:", count)
# missing update of the control variable
Warning
The above code will create an infinite loop because the control variable count
is never updated inside the loop. Infinite loops can cause your program to become
unresponsive. If you accidentally create an infinite loop, you can stop it by pressing
Ctrl + C in your terminal.
Indefinite while Loops#
To create an indefinite while loop, we set the condition expression such that
it depends on some external factor such as user input. There is no way of knowing ahead
of time, how many times the loop will execute.
Example:
In this example, the user enters "yes" the first two times they are prompted
for input, and then enters "yes!" the third time, which causes the loop to
terminate because "yes!" is not equal to "yes".
response = input("Would you like to start the program? (yes/no): ")
while response == "yes":
print("The program is running.")
response = input("Would you like to run the program again? (yes/no): ")
print("The program has ended.")
Would you like to start the program? (yes/no): yes
The program is running.
Would you like to run the program again? (yes/no): yes
The program is running.
Would you like to run the program again? (yes/no): yes!
The program has ended.
Sentinel-Controlled while Loops#
The sentinel-controlled loop is a common type of indefinite loop where the loop
continues until the a a special value called the sentinel value is encountered. The
sentinel value should be distinct from other values in the sequence. Common examples of
sentinel values include: \(0\), \(-1\), q, Q, quit,
done, and EOF (end of file).
Example:
number = int(input("Enter a number (-1 to stop): "))
while number != -1:
number = int(input("Enter a number (-1 to stop): "))
print("You entered -1. The program has stopped.")
Enter a number (-1 to stop): 1
Enter a number (-1 to stop): 6
Enter a number (-1 to stop): 7
Enter a number (-1 to stop): -1
You entered -1. The program has stopped.
The for Loop#
A for loop is used to iterate over a sequence or other iterable objects. They
are typically used when the number of iterations is known beforehand (i.e. a definite
loop).
Definite for Loops#
To create a definite for loop, we use the for keyword followed by a
loop variable, the in keyword, and an iterable object such as a list, tuple,
string, or range. The loop variable takes on the value of each element in the iterable
object one by one, and the block of code inside the loop is executed for each element.
Example:
for i in range(1, 6): # iterate over a range of numbers from 1 to 5
print("Count is:", i)
Count is: 1
Count is: 2
Count is: 3
Count is: 4
Count is: 5
Feature |
The |
The |
|---|---|---|
Use Case |
When the number of iterations is known |
When the number of iterations is not known |
Syntax |
|
|
Control Variable |
Implicitly defined by the iterable |
Explicitly defined and updated by the programmer |
Readability |
More concise and easier to read for definite loops |
Can be less readable for complex conditions |
Nested Loops#
A nested loop is a loop inside another loop. The inner loop is executed completely for
each iteration of the outer loop. Both for loops and while loops
can be nested within each other.
Example
# Nested for loop to print a multiplication table
for i in range(1, 6): # Outer loop for rows
for j in range(1, 6): # Inner loop for columns
print(f"{i * j:4}", end=' ') # Print product with formatting
print() # New line after each row
1 2 3 4 5
2 4 6 8 10
3 6 9 12 15
4 8 12 16 20
5 10 15 20 25
Control Statements#
Control statements can be used to alter the flow of execution within loops.
breakExits the loop immediately, regardless of the loop’s condition. Control is transferred to the statement immediately following the loop.
continueCauses the loop to skip the remainder of its body and immediately retest its condition prior to reiterating.
passA null operation; nothing happens when it is executed. It is useful as a placeholder when a statement is required syntactically but no action is needed or desired.
returnWhile not strictly a loop control statement, it is often used within functions that contain loops to exit the function and return a value to the caller. A return statement can be used to exit a loop when that loop is inside a function.
More Data Structures#
Lists#
A list is a collection of elements of different types stored in potentially non-contiguous memory locations. Lists are mutable, meaning that their elements can be changed after creation. Lists are one of the most commonly used data structures in Python due to their flexibility and ease of use. See the Python Official Documentation on Lists for more details.
# Creating a list
my_list = [1, 2, 3, 4, 5]
print(my_list)
# Creating a list with different data types
my_list = [1, "Hello", 3.14, True]
print(my_list)
# Creating a list with a for loop
my_squares = []
for i in range(1, 6):
my_squares.append(i ** 2)
print(my_squares)
# This can equivalently be done using list comprehension
my_squares = [i ** 2 for i in range(1, 6)]
print(my_squares)
[1, 2, 3, 4, 5]
[1, 'Hello', 3.14, True]
[1, 4, 9, 16, 25]
[1, 4, 9, 16, 25]
Common List Methods and Operations#
For a list m, index i, iterable object t, and arbitrary
object x:
m[i] = xreplaces the element at index i with xm.append(x)adds x to the end of the listm.insert(i, x)inserts x at index im.remove(x)removes the first occurrence of xm.pop(i)removes the element at index im.clear()removes all elementsm.extend(t)adds the elements of t to the end of the listm += tadds the elements of t to the end of the listm *= nrepeats the list n timesm.reverse()reverses the listm.sort()sorts the listm.copy()returns a shallow copy of the listm.index(x)returns the index of the first occurrence of xm.count(x)returns the number of occurrences of xlen(m)returns the number of elements in mmin(m)returns the smallest element in mmax(m)returns the largest element in msum(m)returns the sum of elements in msorted(m)returns a new sorted list
Indexing and Slicing:#
m[i]returns the element at index im[i:j]returns the elements from index i to j-1m[i:j:k]returns the elements from index i to j-1 with step km[-i]returns the element at index -im[-i:]returns the elements from index -i to the end of the listm[:-i]returns the elements from the beginning of the list to index -im[::-1]returns the elements in reverse order
Example#
Indexing and slicing can be applied to strings just like lists since they are also sequences.
H |
e |
l |
l |
o |
W |
o |
r |
l |
d |
, |
H |
a |
i |
l |
P |
u |
r |
d |
u |
e |
! |
|||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
-25 |
-24 |
-23 |
-22 |
-21 |
-20 |
-19 |
-18 |
-17 |
-16 |
-15 |
-14 |
-13 |
-12 |
-11 |
-10 |
-9 |
-8 |
-7 |
-6 |
-5 |
-4 |
-3 |
-2 |
-1 |
# string initialization
s = "Hello World, Hail Purdue!"
print(s[0]) # = 'H'
print()
print(s[18:24]) # = 'Purdue'
print(s[-7:-1]) # = 'Purdue'
print()
print(s[0:5]) # = 'Hello'
print(s[:5]) # = 'Hello'
print()
print(s[-1]) # = '!'
print(s[24]) # = '!'
print(s[-1:]) # = '!'
print(s[24:]) # = '!'
print(s[len(s)-1]) # = '!'
print(s[-1:len(s)]) # = '!'
print()
print(s[:-1]) # = 'Hello World, Hail Purdue'
print(s[:24]) # = 'Hello World, Hail Purdue'
print()
print(s[::-1]) # = '!eudruP liaH ,dlroW olleH'
print(s[-1:-26:-1]) # = '!eudruP liaH ,dlroW olleH'
H
Purdue
Purdue
Hello
Hello
!
!
!
!
!
!
Hello World, Hail Purdue
Hello World, Hail Purdue
!eudruP liaH ,dlroW olleH
!eudruP liaH ,dlroW olleH
Nested Lists for storing multi-dimensional data#
Lists can be nested within other lists to create multi-dimensional data structures, such as matrices (2D lists) or tensors (3D lists and higher). We use repeated indexing to access elements in nested lists. For example:
One-dimensional list:
list_name[index]Two-dimensional list:
list_name[row][column]Multidimensional list:
list_name[dim1][dim2][dim3]...
Examples of creating different dimensional lists are shown below.
One-Dimensional List:#
Here we create a literal one-dimensional list containing elements of different types.
my_list = [1, "a", True, ...]
print(my_list)
[1, 'a', True, Ellipsis]
Two-Dimensional List:#
Here we create a literal two-dimensional list (matrix) containing integers, strings, and boolean values.
my_2d_list = [
[1, 2, 3, 4],
["a", "b", "c"],
[True, False]
]
print(my_2d_list)
[[1, 2, 3, 4], ['a', 'b', 'c'], [True, False]]
We can also use loops to create a 2D list (matrix).
# Creating a 2D list (matrix) with 2 rows and 3 columns using nested for loops
my_2d_list = []
for i in range(2): # 2 rows
row = []
for j in range(3): # 3 columns
row.append(i * 3 + j) # Fill with numbers from 0 to 5
my_2d_list.append(row)
print(my_2d_list)
[[0, 1, 2], [3, 4, 5]]
Multidimensional List:#
Here we create a literal multidimensional list (3D and higher).
# Creating a 2x3x2 multidimensional list (2 rows, 3 columns, 2 depth)
my_3d_list = [
[
[1, 2, 3],
[4, 5, 6]
],
[
[7, 8, 9],
[10, 11, 12]
]
]
print(my_3d_list)
[[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]]
This can equivalently be done with nested loops.
my_3d_list = []
counter = 1
for i in range(2): # Outer loop: Iterates through the 2 depth layers
depth = []
for j in range(2): # Middle loop: Iterates through the 2 sub-lists (rows)
row = []
# Inner loop: Appends the next 3 sequential numbers
for k in range(3):
row.append(counter)
counter += 1
depth.append(row)
my_3d_list.append(depth)
print(my_3d_list)
[[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]]
Or by nesting list comprehensions.
my_3d_list = [[[6*i + 3*j + k + 1 for k in range(3)] for j in range(2)] for i in range(2)]
print(my_3d_list)
[[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]]
Python Arrays#
Python has a built-in array module for creating arrays, which are collections of elements of the same type stored in contiguous memory locations. Arrays are more efficient than lists for storing large amounts of homogeneous data; however, all elements must be of the same type and arrays can only be one-dimensional. See the Python Official Documentation on Arrays for more details. Due to these limitations, we will primarily use NumPy arrays when working with large datasets and for storing multi-dimensional data.
NumPy Arrays#
Creating NumPy arrays#
Import the NumPy library as
npCreate an array using the
np.array()function
Example
import numpy as np
# Creating a 1D array
array_1d = np.array([1, 2, 3, 4, 5])
print(f"array_1d = {array_1d}")
# Creating a 2D array (matrix)
array_2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(f"array_2d = \n{array_2d}")
# Creating an array from a PIllow Image object (will be relevant for Py5)
from PIL import Image
# Create a simple 2x2 image with RGB values
image = Image.new("RGB", (2, 2), color=(255, 0, 0)) # Red image
image_array = np.array(image)
print(f"image_array shape = {image_array.shape}")
array_1d = [1 2 3 4 5]
array_2d =
[[1 2 3]
[4 5 6]
[7 8 9]]
image_array shape = (2, 2, 3)
NumPy array attributes#
shapeReturns the dimensions of the array.sizeReturns the total number of elements in the array.ndimReturns the number of dimensions of the array.dtypeReturns the data type of the elements in the array.
Common NumPy array methods#
np.zeros(shape)Creates an array of zeros with the specified shape.np.ones(shape)Creates an array of ones with the specified shape.np.full(shape, value)Creates an array of the specified shape filled with the specified value.np.arange(start, stop, step)Creates an array with a range of values from start to stop with the specified step.np.linspace(start, stop, num)Creates an array with a range of values from start to stop with the specified number of elements.np.random.rand(shape)Creates an array of random values with the specified shape.np.reshape(array, new_shape)Reshapes the array to the specified new shape.np.transpose(array)Transposes the array (swaps rows and columns).np.concatenate((array1, array2), axis)Concatenates two arrays along the specified axis.np.vstack((array1, array2))Stacks arrays vertically.np.hstack((array1, array2))Stacks arrays horizontally.np.split(array, indices_or_sections)Splits the array into multiple sub-arrays.np.max(array)Returns the maximum value in the array.np.min(array)Returns the minimum value in the array.np.mean(array)Returns the mean of the values in the array.np.std(array)Returns the standard deviation of the values in the array.np.median(array)Returns the median of the values in the array.np.dot(array1, array2)Computes the dot product of two arrays.
Common NumPy array operations#
With NumPy arrays, mathematical operations can be performed element-wise or using matrix operations. Here are some common operations:
Element-wise operations#
Element-wise operations are operations that are performed on each corresponding element of two arrays of the same shape.
Example
import numpy as np
# create two 2D arrays (matrices)
A = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
B = np.array([[9, 8, 7], [6, 5, 4], [3, 2, 1]])
# element-wise addition
C = A + B
print(f"C = \n{C}")
# element-wise multiplication
D = A * B
print(f"D = {D}")
C =
[[10 10 10]
[10 10 10]
[10 10 10]]
D = [[ 9 16 21]
[24 25 24]
[21 16 9]]
Broadcasting#
Broadcasting stretches the smaller array across the larger array so that they have compatible shapes for element-wise operations. This allows operations to be performed on arrays of different shapes. Details on the broadcasting rules can be found in the NumPy documentation.
Example
import numpy as np
# create a 2D array (matrix) and a 1D array (vector)
A = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
B = np.array([1, 2, 3])
# broadcasting addition
C = A + B
print(f"C = \n{C}")
C =
[[ 2 4 6]
[ 5 7 9]
[ 8 10 12]]
Transpose#
Transposing an array involves swapping its rows and columns. This is done using the
np.transpose() function.
Example
import numpy as np
# create a 2D array (matrix)
matrix = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# transpose the matrix
transposed_matrix = matrix.transpose()
# Results
print(f"original matrix = \n{matrix}")
print(f"transposed_matrix = \n{transposed_matrix}")
original matrix =
[[1 2 3]
[4 5 6]
[7 8 9]]
transposed_matrix =
[[1 4 7]
[2 5 8]
[3 6 9]]
Slicing#
Slicing is used to access specific elements or sub-arrays within a NumPy array.
Example
import numpy as np
# create a 2D array (matrix)
matrix = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# slice the matrix to get the first two rows and first two columns
sub_matrix = matrix[0:2, 0:2]
print(f"sub_matrix = \n{sub_matrix}")
sub_matrix =
[[1 2]
[4 5]]