For-Loops

lecture
python
for-loops

See csse1001 for course logistics — this note covers Lecture 5A’s technical content. See python-for-loops for the full reference on for-loop syntax, ranges, and enumerate.

Today’s outline

  • For-loops: definition and syntax
  • Iterating over strings, lists, and dictionaries
  • The accumulator pattern
  • range and enumerate

Definitions

Loop — a control structure that repeats code that belongs to it.

For-loop — a control structure that, given a group, repeats code for every member of that group, in order.

for <name> in <iterator>:
    <code>

String iteration (character by character)

A for-loop iterates through a string’s characters, in order:

>>> for x in "abcd":
...     print(x)
a
b
c
d
>>> x
'd'

Note the looping name (x) retains its final value after the loop exits.

Nesting for-loops

>>> (digits, alphas) = ("012", "xy")
>>> for d in digits:
...     for a in alphas:
...         print(d + a)
0x
0y
1x
1y
2x
2y

Python disallows empty for-loop bodies — use pass (the empty instruction, which has no effect) as a placeholder:

>>> for d in digits:
...     for a in alphas:
...         pass
...     print(d + a)
0y
1y
2y

Again, the names used to iterate through an iterator retain their values after the for-loop exits — by the time the outer loop’s print runs, the inner loop has already finished, leaving a at its last value ('y') each time.

The accumulator pattern

Recall an accumulator is a variable a loop uses to build up an aggregate value:

>>> acc = ""
>>> for x in "abcd":
...     acc = acc + x
...     print(acc)
a
ab
abc
abcd

Building it in reverse order just by swapping the order of concatenation:

>>> acc = ""
>>> for x in "abcd":
...     acc = x + acc
...     print(acc)
a
ba
cba
dcba

For-loops vs. while-loops

Can every for-loop be rewritten with only while-loops? Yes:

>>> for x in xs:
...     ...   # do something with x

is code-equivalent to:

>>> k = 0
>>> while k < len(xs):
...     ...   # do something with xs[k]
...     k += 1

Can every while-loop be rewritten with only for-loops? No — consider how many times you’d need to repeat asking a user for correct input (not known in advance).

Technical caveat: a for-loop can be tricked into looping forever in Python, by giving it an iterator that never exhausts. And if you dig deep enough, Python itself used a while-loop to build that iterator in the first place.

Exercise: removing characters

def remove(c: str, cs: str) -> str:
    """ Return the string with all instances of c removed from cs.
    Precondition: len(c) == 1
    >>> remove('b', "bluey")
    'luey'
    >>> remove('b', "Bluey")
    'Bluey'
    >>> remove('b', "chilli")
    'chilli'
    >>> remove('b', "")
    ''
    """

The lecture slide’s worked solution has a variable-naming slip — it reuses c as the loop variable (shadowing the parameter) and references an undefined x:

acc = ""
for c in cs:
    if not c == x:
        acc += c
return acc

The corrected version below (matching the companion source file) uses a different name for the loop variable.

Fill in the blank to complete remove:

acc = ""
for x in cs:
    if x != c:
        acc += x
return acc

List looping (index by index)

A for-loop also iterates directly through a list’s elements:

>>> xs = ['a', 2, 'c', 4, 'e']
>>> for x in xs:
...     print(x)
a
2
c
4
e

Exercise: unique elements

def make_unique(xs: list[int]) -> list[int]:
    """ Returns the UNIQUE elements of xs.
    >>> make_unique([1, 2, 3, 4])
    [1, 2, 3, 4]
    >>> make_unique([1, 2, 2, 2, 3, 1, 4, -3])
    [1, 2, 3, 4, -3]
    """
acc = []
for x in xs:
    if x not in acc:
        acc.append(x)
return acc

This is an out-of-place computation — the original list xs is not modified. An in-place change would instead modify xs directly; those are covered later, in the object-oriented part of the course.

Ranges (iterating through numbers)

range builds an iterator of numbers, with the general form range([start], stop[, step]) (square brackets denote optional arguments) — similar to list slicing:

>>> range(10)
range(0, 10)
>>> type(range(10))
<class 'range'>
>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list(range(2, 7))
[2, 3, 4, 5, 6]
>>> list(range(2, 7, 3))
[2, 5]
>>> list(range(2, 7, -1))
[]
>>> list(range(7, 2, -1))
[7, 6, 5, 4, 3]

range is commonly used to walk through a list by index:

>>> xs = [11, 22, 33, 44]
>>> for k in range(len(xs)):
...     ...   # code involving xs[k]

Enumerate (iterating with index)

enumerate simplifies the index-and-value pattern above. Roughly:

enumerate([x0, ..., xn]) == [(0, x0), ..., (n, xn)]

(this isn’t the full story — enumerate actually returns an iterable, not a list — but the pairs it produces work the same way):

>>> xs = [1, 2, 3, 4]
>>> for k, xk in enumerate(xs):
...     ...   # code involving k and xk == xs[k]

Each (k, xk) pair is an immutable tuple:

>>> for k, x in enumerate(["Scientia", "ac", "Labore"]):
...     print(k, x)
0 Scientia
1 ac
2 Labore

(“Scientia ac Labore” is UQ’s motto — “by means of knowledge and hard work”.)

Dictionary looping (key by key)

A for-loop iterates through a dictionary’s keys:

>>> h = {"red": 1, "blue": 2, "green": 3}
>>> for key in h:
...     print(key)
red
blue
green
>>> for key in h:
...     print(h[key])
1
2
3

Exercise: character index

Write a function that returns the character frequency of a string:

def char_index(xs: str) -> dict[str, list[int]]:
    """ Return a dictionary mapping the characters (length one
    substrings) of <xs> to the list of indices at which they occur.
    >>> char_index("")
    {}
    >>> char_index("aaAbbB")
    {'a': [0, 1], 'A': [2], 'b': [3, 4], 'B': [5]}
    """

The companion source file’s signature annotates the return type as dict[str, int], but both the doctest and the actual behaviour return dict[str, list[int]] — a list of indices per character, not a single count. The annotation above has been corrected to match.

acc = {}
for k, x in enumerate(xs):
    if x in acc:
        acc[x].append(k)
    else:
        acc[x] = [k]
return acc

Summary

For-loops let us iterate through anything iterable. Strictly speaking they’re not necessary (a while-loop, or copy-pasting code k times, can always do the same job) but they’re used often enough to merit their own dedicated syntax.

Next: 2025-08-25-comprehensions (Lecture 5B) — a rapid way of building lists and dictionaries that would otherwise need an accumulator for-loop.