LambdaLabTM
Computer Science · Class 11 · Strings Revisited
StringsPositions⏱️ 14 min read

Traversal by Index

for i in range(len(word)): — longer to write, and the only form that can answer four kinds of question: where is this character, what is next to it, what is at the same place in another string, and what does the string look like backwards. All four need a number, and only this loop has one.

shape.py
for i in range(len(word)):
              |    |
              |    +-- len(word) is 6, so this is range(6)
              +------- range(6) hands out 0, 1, 2, 3, 4, 5

    print(word[i])   <- the character at that position

Three things stacked in one line, and it is worth unstacking them once. len(word) is a number — 6 for a six-letter word. range(6) counts 0 to 5, which is exactly the set of valid positions, because positions start at 0 and range() leaves its stop out. The two off-by-one rules cancel, which is why the header is written this way and not with a - 1 or a + 1 anywhere.

Watch Out
The two ways to get this wrong. range(len(word) - 1) stops at the second-to-last character — no error, just a program that quietly ignores the last letter. range(len(word) + 1) asks for one position too many and raises IndexError: string index out of range on the very last round, after printing everything else.
too_far.py
# one position too far

word = 'cat'

for i in range(len(word) + 1):
    print(word[i])
Output
c
a
t
Traceback (most recent call last):
  File "too_far.py", line 6, in <module>
    print(word[i])
          ~~~~^^^
IndexError: string index out of range

Read the traceback the way the Errors chapter taught: the output before it is real, the program got three characters in, and it died reaching for a fourth that was never there.

2Program 1 — the characters at even positions

📋 The problem

Print the characters that sit at positions 0, 2, 4 … along with their positions.

even_positions.py
# print the characters that sit at an even position

word = 'LAMBDALAB'

for i in range(len(word)):
    if i % 2 == 0:
        print(i, word[i])
Output
0 L
2 M
4 D
6 L
8 B

The test is on i, not on word[i] — the question is about where the character is, not what it is. That is the clearest signal you need this form: the condition mentions the position.

3Program 2 — find the double letters

📋 The problem

Report every place where a character is the same as the character straight after it — the ll and oo of balloon.

doubles.py
# find the double letters: a character equal to the one after it

word = 'balloon'

for i in range(len(word) - 1):
    if word[i] == word[i + 1]:
        print('Double letter', word[i], 'at positions', i, 'and', i + 1)
Output
Double letter l at positions 2 and 3
Double letter o at positions 4 and 5
for i in range(len(word) - 1):

The - 1 is right here, and it is not the off-by-one mistake. The body looks at word[i + 1], so the last position must not be visited — there is nothing after it. The loop stops one early on purpose.

if word[i] == word[i + 1]:

Two lookups, one comparison. This is the line the character form cannot write: ch would hold the letter but would have no way of naming the next one.

Key Takeaway
Reaching forward costs one round; reaching back costs one round at the other end. A loop whose body uses word[i + 1] must stop at len(word) - 1. A loop whose body uses word[i - 1] must start at 1. Get either wrong and you get an IndexError — and it will happen on the first or last round, never in the middle, which is why it is so easy to miss when testing.

4Program 3 — compare two words position by position

📋 The problem

Two words are the same length. Report the positions at which they differ.

compare.py
# compare two words of the same length, position by position

first = 'CAT'
second = 'COT'

for i in range(len(first)):
    if first[i] != second[i]:
        print('Position', i, 'differs:', first[i], 'against', second[i])
Output
Position 1 differs: A against O

One number, two lookups. A loop variable holding a character can only be in one string at a time; a loop variable holding a number is in both at once. This is the shape behind anything that lines two strings up — checking a password, comparing an answer with the key, spotting where two spellings part company.

5Program 4 — walk the string backwards

📋 The problem

Build the reverse of a word by visiting its positions from the last to the first.

reverse_by_index.py
# build the reverse by walking the positions backwards

word = 'python'
backwards = ''

for i in range(len(word) - 1, -1, -1):
    backwards = backwards + word[i]

print('Reversed:', backwards)
Output
Reversed: nohtyp
Watch Out
range(len(word) - 1, -1, -1) — read it three numbers at a time. Start at len(word) - 1, the last valid position (5 for a six-letter word). Stop at -1, which is one step past 0 going down, so that 0 is included. Step -1, so it counts backwards. Writing 0 as the stop is the classic slip: the loop then misses the first character entirely.
reverse_by_index.py — with 0 as the stop instead of -1
Output
Reversed: nohty

The p is missing and nothing was reported. This is the same “one step past the last value” rule as the backwards patterns in the practice chapter — going down, one step past 0 is −1.

reverse_by_index.py
Tip
Python can do this in one step, and you should know both. word[::-1] is a slice with a step of −1 and gives 'nohtyp' immediately. Use it when you just want the answer; write the loop when the question says “using a loop”, and when the reversing is mixed up with something else.

6The other way to walk backwards

Positions can be counted from the right, as the sequences lesson showed: word[-1] is the last character, word[-2] the one before it. So a plain forward-counting loop can read a string backwards, by making the position negative:

negative_walk.py
# the same reversal, with the positions counted from the right

word = 'python'
backwards = ''

for i in range(1, len(word) + 1):
    backwards = backwards + word[-i]

print('Reversed:', backwards)
Output
Reversed: nohtyp

i runs 1 to 6 and word[-i] walks n, o, h, t, y, p. It starts at 1, not 0, because word[-0] is word[0] there is no negative zero, so the first character would come out at both ends of the walk.

7Recap

range(len(word)) is 0 to len − 1

Exactly the valid positions. The two off-by-one rules — positions start at 0, range() excludes its stop — cancel out.

Looking forward or back shortens the loop

word[i + 1] means stopping at len(word) - 1; word[i - 1] means starting at 1. Both errors land on the first or last round only.

One number can index two strings

first[i] and second[i] compare the same place in both. No character-form loop can do that.

Backwards is range(len - 1, -1, -1)

Last position, one step past 0, step of -1. A stop of 0 quietly drops the first character.

✍️ Now write these yourself
  1. 1

    Print each character of a word with its position, as 0 : P.

    Hint · print(i, ':', word[i]) — three things, commas between them.

  2. 2

    Print the characters at odd positions only.

    Hint · i % 2 == 1. The test is on the position, which is the whole reason for this form.

  3. 3

    Report every place where a character is followed by the same letter in the other case, like aA.

    Hint · Compare word[i].lower() with word[i + 1].lower(), and stop the loop one early.

  4. 4

    Given two words of the same length, count how many positions match.

    Hint · A counter above the loop and first[i] == second[i] inside it.

  5. 5

    Build a string of every second character of a word, starting from the first.

    Hint · Either an if i % 2 == 0, or a step of 2 in the range() — try both and compare.

Quick Check

A loop body uses word[i + 1]. What must its range be?

Quick Check

for i in range(len(word) - 1, -1, -1) — why is the stop -1 rather than 0?

Quick Check

Why does the negative-index walk start at 1 instead of 0?