Embercode
✕
Flowcharts and PseudocodeLesson 10 of 13

45 min read

Reading unfamiliar pseudocode

Why this matters

You will often read code that someone else wrote. A teammate leaves a function behind. A textbook gives an algorithm in pseudocode. An interview question shows a short procedure and asks what it does. In each case you did not choose the names, the starting values or the structure, and the author did not explain them.

Most people read such code from the top, line by line, and hope that the meaning appears. It often does not. A loop can look harmless and still run forever, return the wrong thing, or depend on a value that is set somewhere else. The fix is a checklist. You ask the same few questions every time, in the same order, and you write the answers down before you try to understand the whole thing.

This lesson gives that checklist. It is about reading and understanding. Tracing a run step by step with a table is covered in the tracing lesson, and comparing two procedures is covered in the lesson on comparing two algorithms on paper.

The idea: a stranger's kitchen

Suppose you walk into a kitchen you have never used and someone asks you to make tea. Before you touch anything, you look around. Where is the kettle? Where are the cups? Is there milk in the fridge, and is it fresh? Which tap is hot water? You do not start pouring until you know where things are and what state they are in.

Reading pseudocode works the same way. Before you think about what the algorithm does, find out three things.

  • The state. Which named values does the algorithm keep, and what does each one hold at the start?
  • The loop condition. What must be true for the loop to keep going, and what makes it false?
  • The exit. How does the algorithm finish? Does the loop end normally, does it hit a BREAK, or does it RETURN early from inside the loop?

Once you know these three things, most of the algorithm becomes a set of simple questions. Without them, you are guessing.

The word state means the values a program holds at a given moment. If a program is a recipe, the state is what is in the bowl right now. A loop is a block of steps that repeats. The loop condition is the question asked before each repeat. An exit is the way the program leaves the loop, or leaves the whole procedure.

The checklist

Work through these seven steps in order. Write each answer on paper or in a comment. Do not skip a step because it looks obvious. The obvious parts are where bugs usually hide.

1. Read the name and the inputs

The name often says what the algorithm is supposed to return. A name like largest or findFirstRepeat tells you the goal, but it is only a claim. Check the body to see whether it does what the name says.

Write down the inputs and their types. Is the input a list of numbers, a string, a number, or a list of lists? If the pseudocode does not say, guess from how the input is used, and write the guess down as a guess.

2. List every variable and its starting value

Go through the procedure and list every name that is set with an arrow, such as result <- 0, or by a language assignment. For each one, write the first value it receives and the line where that happens.

Pay special attention to values that start at zero, empty or None. These are called initial values. They are chosen by the author for a reason, and that reason is often a hidden assumption. A variable that starts at 0 assumes the answer is never below zero. A variable that starts as an empty list assumes there is nothing to add yet.

Also check the order. If a variable is used on a line before any line sets it, the program will fail, or it will use a value you did not expect.

3. Find the loop condition and what makes it false

Find every WHILE, FOR, REPEAT or similar loop. Copy its condition onto your paper exactly as written. Then ask two questions.

  • What must be true for the loop to run one more time?
  • Which variable in the condition changes inside the loop, and how?

If nothing in the loop changes a variable used in the condition, the loop may never end. If the condition uses < and the variable starts at the wrong place, the loop may run one time too few or one time too many.

Read AND and OR carefully. In most languages, the right side of an AND is checked only when the left side is true. This is called short-circuit evaluation. It matters when the right side could fail if the left side is false. A condition such as pos < length AND values[pos] <> 0 is safe only because the length check comes first.

4. Find what changes on each pass

Inside the loop, find every line that changes a variable. Write down what it does in plain words. For example, i <- i + 2 means "move the position forward by two each pass". A plain-words description makes it much easier to notice a mistake, such as a step that is too big, or a step that is in the wrong place.

Ask whether the change happens before or after the work in the loop body. A counter that goes up before it is used gives different results from one that goes up after.

5. Find every exit

List all the ways the algorithm can stop. There may be more than one.

  • The loop condition becomes false. This is the normal exit.
  • A BREAK or EXIT LOOP line leaves the loop early.
  • A RETURN line leaves the whole procedure early, even from inside a loop.

For each exit, write down what value is returned or what state is left behind. An early exit can hide a bug, because the normal ending of the loop never runs. Code after the loop may be skipped entirely.

6. Check the boundaries

A boundary is the first and last position that the algorithm touches. Check the first pass and the last pass.

  • What is the first index? Many textbooks start at 1, while Python and many other languages start at 0.
  • What is the last index? Is the loop condition < or <=? Does that match the length of the list?
  • What happens with an empty input? With a single item? With the largest possible input?

Boundary errors are the most common bug in loops. Checking them takes a minute and prevents a large share of wrong answers.

7. Test with a small input

Last, choose a very small input and follow the algorithm by hand. Two or three items are usually enough. Pick an input where you already know the answer, and check that the algorithm agrees. Then try an input that should trigger the exit, such as a repeated value or a zero.

You do not need to write a full trace table yet. Just keep track of the state after each pass in a few words. The full method is taught in the lesson on trace tables.

Example 1: a maximum with a hidden start value

Here is an algorithm written by someone else. Read it using the checklist.

ALGORITHM mystery(values)
    result <- 0
    pos <- 0
    WHILE pos < LENGTH(values) AND values[pos] <> 0
        IF values[pos] > result THEN
            result <- values[pos]
        END IF
        pos <- pos + 1
    END WHILE
    RETURN result
END ALGORITHM

Step 1. The name is mystery, which tells you nothing. The input is values, a list of numbers.

Step 2. There are two variables. result starts at 0. pos starts at 0. The start of result is the first thing to question. It means the answer is always at least 0, even if every number in the list is negative.

Step 3. The loop condition is pos < LENGTH(values) AND values[pos] <> 0. The loop keeps going while the position is inside the list and the current value is not zero. The position goes up by one each pass, so the first part of the condition will eventually become false.

Step 4. Each pass, the algorithm compares the current value with result, and keeps the bigger one. Then it moves pos forward by one.

Step 5. There is one exit through the loop condition and one exit through RETURN, which runs after the loop. There is no early RETURN inside the loop. A zero in the list stops the loop early, because the second part of the condition becomes false.

Step 6. The first position is 0, which matches a programming language like Python. The loop stops before pos reaches the length, so the last index is safe. An empty list gives pos < 0, which is false straight away, so the loop never runs.

Step 7. Try [3, 8, 2] by hand. result becomes 3, then 8, then stays 8. The answer is 8. That matches the purpose of the algorithm, which is to find the largest value before a zero.

The checklist has now revealed two things. The loop stops at the first zero, which is probably intended but should be written in a comment. And result starts at 0, which gives a wrong answer when every number is negative. Here is the Python version, so you can check the claims by running it.

def mystery(values):
    result = 0
    pos = 0
    while pos < len(values) and values[pos] != 0:
        if values[pos] > result:
            result = values[pos]
        pos = pos + 1
    return result
 
print(mystery([3, 8, 2, 0, 9]))
print(mystery([3, 8, 2]))
print(mystery([]))
print(mystery([-4, -1, -7]))
print(mystery([0, 5]))

Run it with python3 mystery.py. Output:

8
8
0
0
0

The fourth line is the surprise. The list [-4, -1, -7] has a largest value of -1, but the function returns 0, because result never rises above its starting value. The fix is to start result at the first value in the list, or at a value that every number is bigger than. That fix is covered in the exercises. The point of this example is that the checklist found the hidden assumption before any test failed.

The last line, mystery([0, 5]), also returns 0. The algorithm stops at the first zero, so the 5 is never read. That may or may not be what the author wanted, and only the author or the specification can say. The checklist does not decide whether the behaviour is right. It makes sure you notice it.

Example 2: finding the first repeated value

Here is a second algorithm. It is longer, and it has an early exit.

ALGORITHM findFirstRepeat(items)
    seen <- empty list
    found <- NONE
    i <- 0
    WHILE i < LENGTH(items)
        IF items[i] IS IN seen THEN
            found <- items[i]
            BREAK
        END IF
        ADD items[i] TO seen
        i <- i + 1
    END WHILE
    RETURN found
END ALGORITHM

Step 1. The name says the goal: find the first value that appears twice. The input is a list.

Step 2. Three variables. seen starts empty. found starts as NONE, which means nothing. i starts at 0. Both starting values make sense for the goal. Nothing has been seen yet, and nothing has been found yet.

Step 3. The loop runs while i is inside the list. Each pass, i goes up by one, so the loop must end after the last item.

Step 4. Each pass, the algorithm checks whether the current item is already in seen. If it is, it records the item and stops. If not, it adds the item to seen and moves on. Notice the order. The check happens before the item is added, so an item is never compared with itself.

Step 5. There are two exits. One is BREAK, which leaves the loop as soon as a repeat is found. The other is the loop condition, which ends when every item has been checked without a repeat. In that case found is still NONE, and that is what gets returned. The BREAK skips the add step and the counter step, which is correct, because the algorithm is done.

Step 6. The first index is 0, and the loop condition is < the length. The last index is length - 1, which is safe. An empty list gives i < 0, which is false, so the loop never runs and found stays NONE.

Step 7. Try [4, 7, 1, 7, 4] by hand. seen becomes [4], then [4, 7], then [4, 7, 1]. At i = 3, the item 7 is already in seen, so found becomes 7 and the loop breaks. The answer is 7, even though 4 also repeats. The algorithm returns the value whose second copy comes first, not the value that appeared first in the list. The name findFirstRepeat can be read two ways, and the checklist shows which one this code uses.

Here is the Python version.

def find_first_repeat(items):
    seen = []
    found = None
    i = 0
    while i < len(items):
        if items[i] in seen:
            found = items[i]
            break
        seen.append(items[i])
        i = i + 1
    return found
 
print(find_first_repeat([4, 7, 1, 7, 4]))
print(find_first_repeat([4, 7, 1]))
print(find_first_repeat([]))

Output:

7
None
None

The second line shows the normal exit. No value repeats, so the loop ends without a break, and None comes back. Python prints None where the pseudocode says NONE.

Example 3: a boundary bug that the checklist finds

The third example is a small change to the first one, which a careless editor might make. Someone changes < to <= in the loop condition and writes the two checks in the other order.

ALGORITHM mysteryBroken(values)
    result <- 0
    pos <- 0
    WHILE pos <= LENGTH(values) AND values[pos] <> 0
        IF values[pos] > result THEN
            result <- values[pos]
        END IF
        pos <- pos + 1
    END WHILE
    RETURN result
END ALGORITHM

Step 3 of the checklist catches this. The loop condition now allows pos to equal the length of the list. A list of length 3 has positions 0, 1 and 2. Position 3 does not exist. The condition asks for values[3] on the pass when pos is 3, and the list has no such item.

The Python version shows the real error.

def mystery_broken(values):
    result = 0
    pos = 0
    while pos <= len(values) and values[pos] != 0:
        if values[pos] > result:
            result = values[pos]
        pos = pos + 1
    return result
 
print(mystery_broken([3, 8, 2]))

Output:

Traceback (most recent call last):
  File ".../mystery_broken.py", line 10, in <module>
    print(mystery_broken([3, 8, 2]))
          ~~~~~~~~~~~~~~^^^^^^^^^^^
  File ".../mystery_broken.py", line 4, in mystery_broken
    while pos <= len(values) and values[pos] != 0:
                                 ~~~~~~^^^^^
IndexError: list index out of range

The folder part of each file path is shortened to .... The rest is the output as Python 3.14 printed it.

The last line is the important one. IndexError means the code asked for a position that is not in the list. The traceback points to the exact line, and the ^ marks show the part that failed. A pseudocode reader does not see a traceback, but the same reasoning applies. Step 6 of the checklist, the boundary check, predicts this failure before you run anything.

Common mistakes

Assuming a variable starts at zero. Students often read result <- 0 as "result is zero, so nothing has happened yet". In a maximum or minimum search, zero is a value like any other. The mystery example returned 0 for [-4, -1, -7]. Write down the starting value and ask what it means for the inputs you have not tried.

Reading < as <= or the reverse. A condition with <= runs one more pass than a condition with <. When the list length is the boundary, the extra pass reads a position that does not exist. The message is usually IndexError: list index out of range in Python, or an array index error in other languages. Check every comparison against the length.

Ignoring the second half of an AND. When the left side of an AND is false, the right side is not checked. A condition written as values[pos] <> 0 AND pos < LENGTH(values) fails in a different way. In many languages the first part reads past the end of the list before the length check runs. Keep the length check first.

Stopping at the first RETURN you see. If you read the bottom of the algorithm before the loop, you may think the answer is the value of a variable at that point. A BREAK or an early RETURN changes the result, and it may skip lines you assumed would run. Always list every exit.

Using a name that was never set. A variable that is used before it is assigned produces an error in most languages. In Python, a line such as big = big + 1 before any line sets big gives this message:

NameError: name 'big' is not defined. Did you mean: 'bin'?

In pseudocode the same mistake is harder to see, because the author may have forgotten a line, or may have meant to set the variable in a part of the algorithm you have not read yet. Step 2 of the checklist finds it, because you wrote down every variable and its first line.

Changing the state before the check. In the findFirstRepeat example, the item is checked against seen before it is added. If the add step came first, every item would be a repeat of itself, and the algorithm would stop at the first item. The order of two lines can change the whole answer, so note the order whenever a line changes state.

Practice

Exercise 1. Here is an algorithm. Name the state, the loop condition and the exit. Then say what it returns for [2, 2, 3, 3, 3], [7] and [].

ALGORITHM countRuns(values)
    runs <- 0
    i <- 1
    WHILE i < LENGTH(values)
        IF values[i] = values[i - 1] THEN
            runs <- runs + 1
        END IF
        i <- i + 1
    END WHILE
    RETURN runs
END ALGORITHM

Show the answer

The state is runs, which starts at 0, and i, which starts at 1. The loop condition is i < LENGTH(values). The loop ends when i reaches the length, and the only exit is RETURN runs after the loop. The function counts the places where a value is the same as the one just before it. For [2, 2, 3, 3, 3] it returns 3, because the pairs (2, 2), (3, 3) and (3, 3) match. For [7] it returns 0, because i starts at 1, which is not less than 1. For [] it also returns 0. The starting value of i is 1 on purpose, because the code looks one step back at values[i - 1], and that step would fail at i = 0.

Exercise 2. Use the findFirstRepeat algorithm from Example 2. What does it return for [9, 3, 9, 3]? Explain which value wins and why.

Show the answer

It returns 9. The first repeat found is the second 9, at position 2. That happens before the second 3, at position 3. The algorithm returns the value whose second copy comes first in the list, not the value that appears first.

Exercise 3. The mystery function from Example 1 returns 0 for [-4, -1, -7]. Change the algorithm so it returns -1 for that input and still returns 8 for [3, 8, 2, 0, 9]. Say which line you changed and why the change works.

Show the answer

Change the start value of result from 0 to the first item in the list, and leave the rest alone. In pseudocode, that is result <- values[0]. For [-4, -1, -7], the loop then starts with result at -4, moves to -1 because it is larger, and stays there. For [3, 8, 2, 0, 9], the result starts at 3, becomes 8, and stays 8. The change does not work for an empty list, because values[0] does not exist. A good answer adds an empty-list check before that line.

Exercise 4. Read this pseudocode. Find the bug, using step 4 of the checklist.

ALGORITHM sumUntilNegative(values)
    total <- 0
    i <- 0
    WHILE i < LENGTH(values) AND values[i] >= 0
        total <- total + values[i]
    END WHILE
    RETURN total
END ALGORITHM

Show the answer

The loop never changes i. The condition stays true for the same position on every pass, so the loop runs forever when the first value is not negative. The missing line is i <- i + 1, placed inside the loop body after the total is updated. With that line added, sumUntilNegative([4, 5, -1, 9]) returns 9. The sum stops at the first negative value, which is -1 in this list. Without the fix, the same input never returns.

Exercise 5. Here is a short algorithm. What does it return for the input [1, 2, 3, 2] with a target of 2? Does the name match what the code does?

ALGORITHM lastIndexOf(values, target)
    found <- -1
    i <- 0
    WHILE i < LENGTH(values)
        IF values[i] = target THEN
            found <- i
            BREAK
        END IF
        i <- i + 1
    END WHILE
    RETURN found
END ALGORITHM

Show the answer

The function returns 1. The loop reaches position 1, finds the 2, sets found to 1 and stops at the BREAK, so the later 2 at position 3 is never checked. The name does not match. lastIndexOf suggests the last match, but the BREAK makes the function return the first one. To match the name, remove the BREAK line. Then the loop keeps going, and each match overwrites found, so the function returns 3 for this input. If no value matches, the function returns -1, which is the starting value of found.

Exercise 6. Write the three things you would look for first in a new algorithm, in your own words, and give one example of a mistake each one would catch.

Show the answer

The state, which is the variables and their starting values, catches a hidden assumption such as a maximum that starts at 0. The loop condition, with its boundary, catches an off-by-one error such as a <= that reads past the end of a list. The exit, meaning every BREAK, RETURN and normal ending, catches a result that is returned too early or a line that is skipped. Any three examples that match these categories are correct.

Summary

  • Before you think about what an unfamiliar algorithm does, find its state, its loop condition and its exit.
  • Write down the starting value of every variable, and ask what each start value assumes about the input.
  • Check each loop condition for its boundary, and check the order of an AND when one side could fail.
  • List every BREAK and RETURN, because an early exit changes the answer.
  • Predict the failure before you run the code. A boundary error such as <= on a list length usually means an IndexError.
  • Test with a very small input where you know the answer, and use it to check each claim you made.

Stuck? Ask the tutor

Ask about anything in this lesson. Answers are short and use simple words.

Sign in to ask the tutor.

← Trace tables for calls