Loops#

The for Statement#

  • Iterates over each element of an iterable object (e.g., sequence datatypes) until the last element is reached

  • Syntax:

for item in iter_expr:
    body
  • for: Required Python keyword

  • item: Variable name to which the elements of the iterable expression/object are asigned to

  • iter_expr: Expression evaluates to an iterable Python object

    • Iterable objects: String, List, Tuple, Range, Dictionary, etc.

    • iter built-in function can be used to check if an object is iterable

  • body: A sequence of statements and expressions to be excuted repeatedly

    • Body may be multiple lines but all lines must be indented by four spaces

    • IDEs like VS Code and JupyterLab automatically insert the tab indent

    • Otherwise, use the Tab key to insert manually

Looping over Sequences#

  • Code for printing each element/item of a sequence type (say a str object)

s = "Hello"

print(s[0])
print(s[1])
print(s[2])
print(s[3])
print(s[4])
H
e
l
l
o
  • What if the sequence object has a large number of elements?

    • Use a for loop

  • Basic for loop over elements of a str object to print each character

  • The looping variable name is arbitrary

# String object
s = "Hello"

# Loop over the string
for c in s:
    print(c)
    
# Indent removed; not part of the loop body
# Adding an indent accidentally will cause unexpected behavior
print("Done looping over the string!")
H
e
l
l
o
Done looping over the string!
  • The statement for c in s: should be read as: “for each character c in the string sequence s, do something using c in the loop body

  • More complex string handling: looping over a list of strings

# List of strings
names = ['einstein', 'tesla', 'lagrange', 'euler', \
        'newton', 'edison', 'maxwell', 'boltzmann', \
        'navier', 'schrodinger', 'bohr', 'heisneberg']
  • Code for formatting and tabulating the list of names and the number of characters

# Loop counter initialized to 1
index = 1

# For loop over a list
for item in names:
    print(f"{index:>2}\t{item.title():^14}\t{len(item)}")
    # Increment the loop counter by 1
    index += 1

# Indent removed; not a part of the for loop
print(f"\nDone looping over {index-1} names!")
 1	   Einstein   	8
 2	    Tesla     	5
 3	   Lagrange   	8
 4	    Euler     	5
 5	    Newton    	6
 6	    Edison    	6
 7	   Maxwell    	7
 8	  Boltzmann   	9
 9	    Navier    	6
10	 Schrodinger  	11
11	     Bohr     	4
12	  Heisneberg  	10

Done looping over 12 names!
  • Loop counter index helps keep track of the iteration number

  • The built-in function enumerate

    • Input is any iterable object

    • Output is the enumerate object, also an iterable object

    • Each item of the enumerate object is a tuple of length 2: (index, value)

sep = ' | '
for index, name in enumerate(names):
    print(f"{index:>2}{sep}{name.title():^14}{sep}{len(item)}")
 0 |    Einstein    | 10
 1 |     Tesla      | 10
 2 |    Lagrange    | 10
 3 |     Euler      | 10
 4 |     Newton     | 10
 5 |     Edison     | 10
 6 |    Maxwell     | 10
 7 |   Boltzmann    | 10
 8 |     Navier     | 10
 9 |  Schrodinger   | 10
10 |      Bohr      | 10
11 |   Heisneberg   | 10

Looping over Lists, Tuples, and Dictionaries#

  • Looping over a list or tuple of numbers

l = [1, 1, 2, 3, 5]

for i in l:
    print(i, end=', ')
1, 1, 2, 3, 5, 
  • range object can be used when working with multiple sequences

l = [1, 1, 2, 3, 5]
t = (1, 2, 6, 12, 20)
for i in range(len(l)):
    print(l[i], t[i], sep='  ')
1  1
1  2
2  6
3  12
5  20
  • Cycling through a dictionary’s keys

d = {'x' : 10, 'y' : 20, 'z' : 30}

for key in d:
    print(key, end=' ')
x y z 
  • Replacing d with d.keys() gives the same output

  • Cycling through a dictionary’s values

d = {'x' : 10, 'y' : 20, 'z' : 30}

for val in d.values():
    print(val, end=' ')
10 20 30 
  • Cycling through a dictionary’s key-value pairs

d = {'x' : 10, 'y' : 20, 'z' : 30}

for key, val in d.items():
    print(key, ":", val, sep='  ', end=' ')
x  :  10 y  :  20 z  :  30 
  • The output of d.items() is an iterable object called dict_items which is similar to enumerate

  • Each item in this iterable object is a tuple with two elements: (key, value)

Accumulator Pattern#

  • Code for adding the integers from 1 to n

    • We can loop over the numbers 1 to n, storing the partial sum in a new variable

  • This pattern of iterating and updating is called the accumulator pattern, since you accumulate data/value each new iteration

    • The variable where the data is stored is the accumulator

    • This pattern is extremely useful - learn it well

# Number of integers
n = 92

# Accumulator variable
s = 0  # initialize the accumulator

# Loop over the integers
for i in range(1, n+1):
    # Update the accumulator
    s += i

print(f"The integers from 1 to {n} sum to {s}")
The integers from 1 to 92 sum to 4278
  • Example: factorial of an integer

    • We can use the accumulator pattern with multiplication instead of addition

    • Initialize the accumulator to 1 instead of 0

# Input
n = 12

# Accumulator variable
f = 1  # initialize the accumulator

# Loop
print(f" i \t i! ")
print("---\t---")
for i in range(1, n+1):
    f *= i
    print(f"{i:>2}\t{f:<}")
 i 	 i! 
---	---
 1	1
 2	2
 3	6
 4	24
 5	120
 6	720
 7	5040
 8	40320
 9	362880
10	3628800
11	39916800
12	479001600
n = 12

fact = 1
for i in range(1, n+1):
    fact *= i
    
print(f"{n}! = {fact}")
12! = 479001600
  • Example: accumulating multiple values

    • Find the square and cube of every number in a list.

# Input
nums = [1 ,4, 5, 2, 19, -2, 4.5]

# Accumulator variables
nums_squared = []  #initialize list
nums_cubed = []

# Loop over the input
for num in nums:
    nums_squared.append(num**2)
    nums_cubed.append(num**3)

print(nums)
print(nums_squared)
print(nums_cubed)
[1, 4, 5, 2, 19, -2, 4.5]
[1, 16, 25, 4, 361, 4, 20.25]
[1, 64, 125, 8, 6859, -8, 91.125]
  • We need an index to refer to two individual lists throughout the loop

    • Use range

x = (1, 3, 5)
y = (2, 2, 4)

dot_prod = 0
for i in range(len(x)):
    dot_prod += x[i] * y[i]
    
print(f"{x} dot {y} = {dot_prod}")
(1, 3, 5) dot (2, 2, 4) = 28
  • Add every other number in a tuple

  • Use range to index every other number

# Input
t = (1, 3, 9, 19, 1, 23, 5, 23, 10)

# Accumulator
s = 0  # initialize the accumulator

# Loop
for i in range(0, len(t), 2):
    s += t[i]

print(f"Sum of numbers with even index is {s}")
Sum of numbers with even index is 26
  • This codes works exactly the same way with list objects

  • Or any sequence within a sequence

  • Iterate on multiple variables simulaneously

x = [[1,2],[3,4],[5,6]]

for a, b in x:
    print(f"{a} + {b} = {a+b}")
1 + 2 = 3
3 + 4 = 7
5 + 6 = 11

Nested for loops#

  • The entirety of the inner loop will executed for each iteration of the outer loop.

# Outer for loop
for i in range(10):
    # Body of outer for loop
    
    # Inner for loop
    for j in range(10):
        # Body of inner for loop
        print(f"({i},{j})", end=' ')
    
    print() # Adds a new line
    # End of outer for loop
(0,0) (0,1) (0,2) (0,3) (0,4) (0,5) (0,6) (0,7) (0,8) (0,9) 
(1,0) (1,1) (1,2) (1,3) (1,4) (1,5) (1,6) (1,7) (1,8) (1,9) 
(2,0) (2,1) (2,2) (2,3) (2,4) (2,5) (2,6) (2,7) (2,8) (2,9) 
(3,0) (3,1) (3,2) (3,3) (3,4) (3,5) (3,6) (3,7) (3,8) (3,9) 
(4,0) (4,1) (4,2) (4,3) (4,4) (4,5) (4,6) (4,7) (4,8) (4,9) 
(5,0) (5,1) (5,2) (5,3) (5,4) (5,5) (5,6) (5,7) (5,8) (5,9) 
(6,0) (6,1) (6,2) (6,3) (6,4) (6,5) (6,6) (6,7) (6,8) (6,9) 
(7,0) (7,1) (7,2) (7,3) (7,4) (7,5) (7,6) (7,7) (7,8) (7,9) 
(8,0) (8,1) (8,2) (8,3) (8,4) (8,5) (8,6) (8,7) (8,8) (8,9) 
(9,0) (9,1) (9,2) (9,3) (9,4) (9,5) (9,6) (9,7) (9,8) (9,9) 
  • Inner for loop range object can be made of variable size

# Outer for loop
for i in range(10):
    
    # inner for loop
    for j in range(i+1):
        print(f"({i},{j})", end=' ')

    print()
(0,0) 
(1,0) (1,1) 
(2,0) (2,1) (2,2) 
(3,0) (3,1) (3,2) (3,3) 
(4,0) (4,1) (4,2) (4,3) (4,4) 
(5,0) (5,1) (5,2) (5,3) (5,4) (5,5) 
(6,0) (6,1) (6,2) (6,3) (6,4) (6,5) (6,6) 
(7,0) (7,1) (7,2) (7,3) (7,4) (7,5) (7,6) (7,7) 
(8,0) (8,1) (8,2) (8,3) (8,4) (8,5) (8,6) (8,7) (8,8) 
(9,0) (9,1) (9,2) (9,3) (9,4) (9,5) (9,6) (9,7) (9,8) (9,9) 
  • Example: Multiplication Tables

# Outer for loop
for i in range(1, 13):
    # inner for loop
    for j in range(1, 6):
        print(f"{j} x {i: >2} = \033[91m{i*j: >2}\033[0m\t", end=' ')
    print()
1 x  1 =  1	 2 x  1 =  2	 3 x  1 =  3	 4 x  1 =  4	 5 x  1 =  5	 
1 x  2 =  2	 2 x  2 =  4	 3 x  2 =  6	 4 x  2 =  8	 5 x  2 = 10	 
1 x  3 =  3	 2 x  3 =  6	 3 x  3 =  9	 4 x  3 = 12	 5 x  3 = 15	 
1 x  4 =  4	 2 x  4 =  8	 3 x  4 = 12	 4 x  4 = 16	 5 x  4 = 20	 
1 x  5 =  5	 2 x  5 = 10	 3 x  5 = 15	 4 x  5 = 20	 5 x  5 = 25	 
1 x  6 =  6	 2 x  6 = 12	 3 x  6 = 18	 4 x  6 = 24	 5 x  6 = 30	 
1 x  7 =  7	 2 x  7 = 14	 3 x  7 = 21	 4 x  7 = 28	 5 x  7 = 35	 
1 x  8 =  8	 2 x  8 = 16	 3 x  8 = 24	 4 x  8 = 32	 5 x  8 = 40	 
1 x  9 =  9	 2 x  9 = 18	 3 x  9 = 27	 4 x  9 = 36	 5 x  9 = 45	 
1 x 10 = 10	 2 x 10 = 20	 3 x 10 = 30	 4 x 10 = 40	 5 x 10 = 50	 
1 x 11 = 11	 2 x 11 = 22	 3 x 11 = 33	 4 x 11 = 44	 5 x 11 = 55	 
1 x 12 = 12	 2 x 12 = 24	 3 x 12 = 36	 4 x 12 = 48	 5 x 12 = 60	 
  • Example: Matrices

  • Constructing matrices as list of lists

\[\begin{split} \begin{pmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ 8 & 9 & 10 & 11 \\ 12 & 13 & 14 & 15 \end{pmatrix} \end{split}\]
matrix = []

# Outer for loop
for i in range(4):
    row = []
    # Inner for loop
    for j in range(4):
        row.append(4*i + j)
    matrix.append(row)

print(matrix)
[[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14, 15]]

The while Statement#

  • An alternative to the for loop

  • Syntax:

while test_expr:
    body
  • while: The required Python keyword

  • test_expr: Boolean expression that evaluates to True or False

    • Truth value is determined at start of iteration

    • Loop terminates if the expression evaluates to False

  • body: A sequence of indented statements and expressions to be executed repeatedly

  • Example: Countdown

# Loop counter
counter = 10

print("Start Countdown")

while counter >= 0:
    print(counter)
    
    # Decrement counter
    counter -= 1
    
print("End Countdown")
Start Countdown
10
9
8
7
6
5
4
3
2
1
0
End Countdown
  • The test_expr is counter >= 0

  • What happens if counter is initialized to -`?

    • The loop will not start and nothing is printed

  • What happens if the test expression is counter >= 10?

    • The loop runs indefinitely

  • while loops are indefinite loops - they can run indefinitely

  • for loops are definite loops - they loop over a fixed number of elements

while True:
    print("*", end='')
  • An infinite loop

    • The program will run indefinitely until the device runs out of resources

while -1:
    print("*", end='')
  • Is this an infinite loop?

    • Yes, the Boolean value of -1 is `True`

  • Python expressions that evaluate to Boolean value False

    • Zero numeric values: 0, 0.0, 0.0j

    • Empty sequences: ‘’, [], (), range(0)

    • Empty dictionary: {}

    • NoneType object: None

      • Referred to as a null object in general programming terms

      • A null object has no value associated with it

# A heterogenous tuple
aTuple = (0, 0.0, 0.0j, [], (), range(0), '', {}, None)

# Check the Boolean value iteratively
for val in aTuple:
    print(f'Boolean value of \033[92m{val}\033[0m is \033[91m{bool(val)}\033[0m')
Boolean value of 0 is False
Boolean value of 0.0 is False
Boolean value of 0j is False
Boolean value of [] is False
Boolean value of () is False
Boolean value of range(0, 0) is False
Boolean value of  is False
Boolean value of {} is False
Boolean value of None is False
  • Note: Infinite loops are possible with the for statement too

  • Example: Looping over a List

  • Emptying the list iteratively

lst = [1, 2, 3, 4, 5]

while lst:
    print(lst.pop())
5
4
3
2
1
  • Example: Loops and Conditionals

Find the minimum of a list of numbers and its location

# Define a list
l = [5, 2, 6.4, -2, 2.5, 0, 8]

# Variable to keep track of the min value
minVal = l[0]

# Variable to keep track of the min location
minLoc = 0

# loop
for i, val in enumerate(l):
    # Update min value and location
    if val < minVal:
        minVal = val
        minLoc = i
        
print(f"The minimum value of the list {l} is {minVal} at index {minLoc}")
The minimum value of the list [5, 2, 6.4, -2, 2.5, 0, 8] is -2 at index 3

The break Statement#

  • Halts the iterations of the encolsing for or while loop

  • Any code following the loop statement is executed

# Find the index of a number n in list l
l = [1, 2, 3, 4, 5]
n = 3

# Loop
for i in range(len(l)):
    if l[i] == n:
        #print(i)
        break
        
seqType = str(type(l)).split("'")
print(f"Element '{n}' found at index {i} in {seqType[1]} {l}")
Element '3' found at index 2 in list [1, 2, 3, 4, 5]
  • Program that requests a string input without numbers or symbols

word = input("Enter your word (no numbers of symbols): ")

while True:
    if word.isalpha():
        print(f"Your word '{word}' is accepted.")
        break
    else:
        word = input('Your word contains numbers or symbols. Retry: ')
---------------------------------------------------------------------------
StdinNotImplementedError                  Traceback (most recent call last)
Cell In[28], line 1
----> 1 word = input("Enter your word (no numbers of symbols): ")
      3 while True:
      4     if word.isalpha():

File /opt/hostedtoolcache/Python/3.11.9/x64/lib/python3.11/site-packages/ipykernel/kernelbase.py:1281, in Kernel.raw_input(self, prompt)
   1279 if not self._allow_stdin:
   1280     msg = "raw_input was called, but this frontend does not support input requests."
-> 1281     raise StdinNotImplementedError(msg)
   1282 return self._input_request(
   1283     str(prompt),
   1284     self._parent_ident["shell"],
   1285     self.get_parent("shell"),
   1286     password=False,
   1287 )

StdinNotImplementedError: raw_input was called, but this frontend does not support input requests.
  • What happens if the break statement is not present?

    • We end up with an _infinite_ loop!

The continue Statement#

  • Skips the rest of the body of the enclosing loop and continues to the next iteration

  • Example: a program that computes the square root for positive numbers and skips negative numbers

numbers = 1, 4, -4, 16, -100, 144

for num in numbers:
    # Negative numbers
    if num < 0:
        print(f"The square root of {num:>4} is complex!")
        continue
    
    # Positive numbers
    num_sqrt = num**0.5
    print(f'The square root of {num:>4} is {num_sqrt}.')