# Iteration refinements: break and continue

## Contents

# 8. Iteration refinements: `break`

and `continue`

#

## 8.1. Introduction#

Altough all iterations an be programmed with either a `for`

or `while`

loop — and in fact as noted below,
all iteration can be done with `while`

loops — the two extra statements `break`

and `continue`

can sometimes make code more readable and concise.

For example, a `break`

statement can be used to avoid the repetition seen in
Examples B and D of Iteration with while.

## 8.2. Case 1: loops that must execute at least once#

It is common numerical computing that a loop must be execute at least once, because the information used to decides whether to keep going is only generated within the loop itself.

The structure is like this:

Do the steps in the loop

Decide whether to keep going

Do the steps in the loop

Decide whether to keep going

Do the steps in the loop

Decide whether to keep going

and so on

With a `while`

loop, we can do this:

```
Do the steps in the loop
while "we are not there yet":
Do the steps in the loop
```

The statement `break`

can avoid this repetition of code:
its meaning is that it stops the (`while`

or `for`

) loop immediately, with execution continuing at the next line of code afer the loop.

There is still one thing to deal with: making the loop run for sure the first time.
With a `while`

loop this is done by the slightly inelegant trick of making the `while`

statement always keep going,
relying soley on the `break`

to end things:

```
while True:
Do the steps in the loop
if "our work here is done":
break
```

(Note that the condition for the `break`

is the logical negation of the one used in the `while`

statement version.)

## 8.3. Case 2: loops with multiple stopping conditions, that can arise at different stages#

A second common pattern for iteration is when the information used to decide whether to stop iterating arises part way through the calculations in the loop — for example, a division by zero is about to happen, so you must bail out:

Decide whether to keep going, due to condition 1

Do part A

Decide whether to keep going, due to condition 2

Do part B

Decide whether to keep going, due to condition 1

Do part A

Decide whether to keep going, due to condition 2

Do part B

Etc.

This can be donw with a `while`

loop:

```
while "condition 1 to keep going":
part A
if "condition 1 to finish":
break
part B
```

## 8.4. Case 2b: loops with multiple stopping conditions, one suitable for a `for`

loop#

A second common pattern for iteration is when the information used to decide whether to stop iterating arises part way through the calculations in the loop — for example, a division by zero is about to happen, so you must bail out:

A common sub-case of the above — to be seen in the next example is where there are two reasons to stop

A predetermined maximum allowed number of iterations has been done

Something comes up during a pass of the loop which means you can (or must) end the iterations.

For example with an iterative calculation like that for the cube root in Example B of Iteration with while, it might not be certain in advance that sufficient accuracy will ever be achieved, so a maximum allowed number of iterations is needed to avoid a possibly unending repetition.

That example could be redone with such a limit, using a`for`

loop:

### Example A: cube roots again#

```
a = 8
root = 1
# I will tolerate an error of this much:
errorTolerance = 1e-8
# and allow at most this mant iterations:
iterationsMax = 10
for iteration in range(iterationsMax):
root = (2*root + a/root**2)/3
errorEstimate = abs(root**3 - a)
if errorEstimate <= errorTolerance: # we are done
break
print(f'The cube root of {a:g} is approximately {root:20.16g}')
print(f'The backward error in this approximation is {abs(root**3 - a)}')
print(f'This required {iteration} iterations')
```

```
The cube root of 8 is approximately 2.000000000012062
The backward error in this approximation is 1.447428843448506e-10
This required 5 iterations
```

**Note** A side benefit of using `for`

loops in situations like this is getting an “automatic” counting of how much work was required,
by the crude measure of how many iterations were needed.

### Example B: Solving equations by Newton’s method#

As you might have seen in a calculus course **Newton’s Method** for solving an equation \(f(x) = 0\) is based on getting a sequence of approximations \(x_0, x_1, x_2 , \dots\) with

Get an initial guess, \(x_0\)

Get the rest successively with \(x_{n+1} = x_n - f(x_n)/Df(x_n)\) Here I use the notation \(Df\) for the derivaitve \(f'\), because it can be the name of a Python function.

There are three reasons that the iteration should stop:

The approximation is sufficiently accurate — we use the criterion that \(|f(x_n)|\) is “small enough”.

We are about to divide by zero (because \(Df(x_n) = 0\), and so have to give up.

It is taking too many iterations, as in the previous example — this is a real hazard with Newton’s Method if you are not careful!

```
a = 8. # We seek its cube root, as the solution of f(x) = x^3-a = 0
def f(x): return x**3 - a
def Df(x): return 3*x**2
x = 1. # All x_n values will be stored in x
errorTolerance = 1e-15
iterationsMax = 10
print(f"Before the iterations, x_0 = {x}, with backward error {abs(f(x))}")
for iteration in range(iterationsMax):
Df_of_x = Df(x)
if Df_of_x == 0: # Give up
break
x -= f(x)/Df_of_x
errorEstimate = abs(f(x))
print(f"After {iteration+1} iterations, x_{iteration+1} = {x}, with backward error {errorEstimate}")
if errorEstimate <= errorTolerance: # Success
break
print("")
print(f"The solution is approximately {x}")
print(f'The backward error in this approximation is {abs(root**3 - a)}')
print(f'This required {iteration} iterations')
```

```
Before the iterations, x_0 = 1.0, with backward error 7.0
After 1 iterations, x_1 = 3.3333333333333335, with backward error 29.037037037037045
After 2 iterations, x_2 = 2.462222222222222, with backward error 6.92731645541838
After 3 iterations, x_3 = 2.081341247671579, with backward error 1.0163315496105625
After 4 iterations, x_4 = 2.003137499141287, with backward error 0.03770908398584538
After 5 iterations, x_5 = 2.000004911675504, with backward error 5.894025079733467e-05
After 6 iterations, x_6 = 2.0000000000120624, with backward error 1.447482134153688e-10
After 7 iterations, x_7 = 2.0, with backward error 0.0
The solution is approximately 2.0
The backward error in this approximation is 1.447428843448506e-10
This required 6 iterations
```

## 8.5. Skipping part of the code in a loop: the `continue`

statement#

Another situation that can arise, though less often in numeircla computing, is that you sometimes can omit the result of the steps in an iteration, but wish to continue with the rest — for example, the result of the iteration has been gone more easily than usual.
The `continue`

statement does this.

### Example C#

For the natural numbers `n`

from 0 to `N`

decide of they are a multiple of 3, and if not, print the cube of the remainder are dividing by three:

```
N = 10
for n in range(N+1):
n_by_3 = n%3
if n_by_3 == 0:
continue
print(f"For {n=} the remainder cubed is {n_by_3**3}")
```

```
For n=1 the remainder cubed is 1
For n=2 the remainder cubed is 8
For n=4 the remainder cubed is 1
For n=5 the remainder cubed is 8
For n=7 the remainder cubed is 1
For n=8 the remainder cubed is 8
For n=10 the remainder cubed is 1
```

This effect can also be achieved with an `if`

statement deciding whether to execute the remaining code in the loop:

```
N = 10
for n in range(N+1):
n_by_3 = n%3
if n_by_3 != 0:
print(f"For {n=} the remainder cubed is {n_by_3**3}")
```

```
For n=1 the remainder cubed is 1
For n=2 the remainder cubed is 8
For n=4 the remainder cubed is 1
For n=5 the remainder cubed is 8
For n=7 the remainder cubed is 1
For n=8 the remainder cubed is 8
For n=10 the remainder cubed is 1
```

Thus the benefits are less obvious than with `break`

;
it can payoff when there are multiple places in the loop to `continue`

,
in which situation it can help avoid convoluted nesting of `if`

statements.

### Execise A: Newton’s Method#

A more careful algorithm for Newtons’s methods uses an actual estimate of the error \(e_n = |r - x_n|\) where \(r\) is the root: \(f(r) = 0\).

This in turn is estimated by the difference between the two most recent approximations: \(e_n \approx E_n = |x_n - x_{n-1}|\). (This is typically quite pessimistic, with the error usually being far smaller, so the algorithm is “cautious”.)

One difficulty with this — and why it was not used in the example above — is that the estimate is not available till towards the end of the first iteration, so it is not well-suited to a `while`

loop.
Thus there again three reasons the iterations can end, all coming at different places in the loop:

Error estimate small enough: \(|x_n - x_{n-1}| \leq errorTolerance\).

Avoiding division by zero: \(Df(x_n) = 0\).

Too many iterations, suggesting an infinite loop:

`iteration > maxIterations`

.

The final issue to deal with is having both \(x_n\) and \(x_{n-1}\) available at the n-th iteration, preferably done without storing the whole sequence but just just the two most recent values.

Implement a Python function with usage

```
`(root, errorEstimate, iterationsNeeded) = newton(f, Df, x0, errorTolerance, iterationsMax)`
```

and test it on a few equations including

\(\sin(x) = 1/2\),

\(x = \cos x\)

one of the polynomials used in previous sections that has known real roots, and

one of the polynomials that has no real roots.

## 8.6. Footnote: every `for`

loop could be done as a `while`

loop, but …#

The for loop

```
for i in range(a,b,step):
do stuff
```

can be replaced by

```
i = a
while a < b:
do stuff
i += step
```

but this is less clear and concise. More generally iteration over a list or such can also be reworked this way, but it becomes even more convoluted.