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.
1Reading the header from the inside out
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 positionThree 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.
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.# one position too far
word = 'cat'
for i in range(len(word) + 1):
print(word[i])c
a
t
Traceback (most recent call last):
File "too_far.py", line 6, in <module>
print(word[i])
~~~~^^^
IndexError: string index out of rangeRead 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
Print the characters that sit at positions 0, 2, 4 … along with their positions.
# 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])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
Report every place where a character is the same as the character straight after it — the ll and oo of balloon.
# 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)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.
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
Two words are the same length. Report the positions at which they differ.
# 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])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
Build the reverse of a word by visiting its positions from the last to the first.
# 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)Reversed: nohtyp
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.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.
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:
# 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)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
Exactly the valid positions. The two off-by-one rules — positions start at 0, range() excludes its stop — cancel out.
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.
first[i] and second[i] compare the same place in both. No character-form loop can do that.
Last position, one step past 0, step of -1. A stop of 0 quietly drops the first character.
- 1
Print each character of a word with its position, as
0 : P.Hint ·
print(i, ':', word[i])— three things, commas between them. - 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
Report every place where a character is followed by the same letter in the other case, like
aA.Hint · Compare
word[i].lower()withword[i + 1].lower(), and stop the loop one early. - 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
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 therange()— try both and compare.
A loop body uses word[i + 1]. What must its range be?
for i in range(len(word) - 1, -1, -1) — why is the stop -1 rather than 0?
Why does the negative-index walk start at 1 instead of 0?