What the code is supposed to do
The function printOddNumbers takes an integer n and prints the odd numbers below it, one per line. Calling printOddNumbers(8) should print 1, 3, 5 and 7. The starter code compiles and runs without errors, but it prints nothing at all:
func printOddNumbers(_ n: Int) {
for i in 0..<n {
if n % 2 == 1 {
print(n)
}
}
}
Your job is to find out why and fix the two lines inside the loop.
What goes wrong
The loop header is correct. 0..<n is a half-open range: it contains 0, 1, 2 and so on up to n - 1, and the variable i takes one of those values on each pass. For n = 8 the loop runs eight times, with i from 0 to 7.
The trouble is that the body never looks at i. Both lines use the parameter n, which stays 8 for the whole call:
n % 2 == 1asks whether 8 is odd. It is not, and it never will be during this call, so the condition is false on every pass.- Even if the condition were true,
print(n)would print 8 each time, not the number the loop is currently at.
The result is a loop that spins eight times and does nothing. The compiler gives a hint: it warns that the immutable value i was never used and suggests replacing it with _ or removing it. A loop variable that is never read is a strong sign that the body is working with the wrong variable.
The same bug has a second face. Call printOddNumbers(5) with the starter code and n % 2 == 1 is true on every pass, so the function prints 5 five times. Which behaviour you see depends only on whether n is odd, which is another way to tell that the loop variable is being ignored.
Fixing it step by step
- Leave the loop header alone.
for i in 0..<nalready visits every candidate from 0 ton - 1. - Change the test to use
i. The question for each pass is whether the current number is odd, so the condition becomesi % 2 == 1. - Print
i. The number to show is the one that passed the test, soprint(n)becomesprint(i).
The fixed code in Swift
func printOddNumbers(_ n: Int) {
for i in 0..<n {
if i % 2 == 1 {
print(i)
}
}
}
func mainFunction() {
printOddNumbers(8)
}
Calling printOddNumbers(8) prints:
1
3
5
7
A few Swift details are worth knowing here:
- Half-open versus closed ranges.
0..<nstops beforen, while0...nincludesn. Swapping the operator would change the answer only whennitself is odd:printOddNumbers(7)would print 1, 3, 5 with0..<7and 1, 3, 5, 7 with0...7. Keep the operator the code already has. - The remainder operator.
%gives the remainder after division, soi % 2is 0 for even numbers and 1 for odd ones. Because the loop starts at 0, everyiis non-negative and comparing with 1 is safe. For negative numbers the remainder takes the sign of the dividend:-3 % 2is-1, so a test written asx % 2 == 1would miss negative odd numbers.x % 2 != 0works for both signs. - Parameters are constants. Inside a Swift function,
ncan't be reassigned. That is one reason the fix uses the loop variable rather than changingn. - The label
_. The function is declared with_ n: Int, so the call isprintOddNumbers(8)without an argument label.
Edge cases and common mistakes
- Testing
ninstead ofi. This is the bug in the lesson. Whenever a loop variable exists, check that the body actually reads it. - Testing
ibut printingn. The condition would pick the right passes, but every line would show the same number. - Zero and one.
printOddNumbers(0)andprintOddNumbers(1)print nothing: the first range is empty, and the second contains only 0, which is even. - Printing the even numbers instead. Comparing with 0, as in
i % 2 == 0, would print 0, 2, 4 and 6.
Testing
printOddNumbers(8)prints 1, 3, 5, 7.printOddNumbers(7)prints 1, 3, 5.printOddNumbers(2)prints 1.printOddNumbers(1)prints nothing.
