0% found this document useful (0 votes)
4 views

Java - Recursion (Full Notes)

Uploaded by

francoispoh
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views

Java - Recursion (Full Notes)

Uploaded by

francoispoh
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 20

1 Recursion

1.1 Recursion

Recursion Recursion
Slide 1

Recursion

Drossopoulou & Wheelhouse (DoC) Discrete Mathematics, Logic & Reasoning 1 / 25

We now consider how to deal with recursively defined functions.

1
Recursion Recursion

We already know how to handle method calls,


recursion requires no special treatment!
Slide 2

(but we do need to be careful with our book-keeping.)

Drossopoulou & Wheelhouse (DoC) Discrete Mathematics, Logic & Reasoning 2 / 25

We can deal with recursion using the techniques we have already seen for reasoning about
method calls.
The “trick” is that when proving that a method’s body satisfies its specification, we can
assume that the method specifications hold for any methods that are called within the
body, including any recursive calls.
This allows us to tackle our proofs in a modular way.
As a running example we shall use a recursive method that returns the sum of all elements
in an array.

2
Recursion Recursion

Recursion - Example

1 int sum(int[] a)
2 // PRE: a 6= null P (P1 )
3 // POST: a[..) ≈ a[..)pre ∧ r = a[..) (Q1 )
4 {
Slide 3

5 int res = sumAux(a,0); P


6 // MID: a[..) ≈ a[..)pre ∧ res = a[..) (M1 )
7 return res;
8 }

where sumAux satisfies the following specification:


int sumAux(int[] a, int i)
// PRE: a 6= null ∧ 0 ≤ i ≤ a.length
P (P2 )
// POST: a[..) ≈ a[..)pre ∧ r = a[i..) (Q2 )

Drossopoulou & Wheelhouse (DoC) Discrete Mathematics, Logic & Reasoning 3 / 25

Note that we can derive the mid-condition M1 without any knowledge of the specification
of sumAux by working backwards from the post-condition Q1 of sum. The only way that
thePreturn statement can satisfy the post-condition is if res has been set up to be equal
to a[..).

3
Recursion Recursion

Reasoning about sum


Slide 4

The correctness of sum and sumAux can be shown independently.


This is another example of modular verification.

Drossopoulou & Wheelhouse (DoC) Discrete Mathematics, Logic & Reasoning 4 / 25

Recursion Recursion

Reasoning about sum - Proof Obligations (Informal)

The correctness of sum relies on the following arguments:


line 5: The pre-condition of sum must establish the pre-condition of sumAux.
i.e. P1 −→ P2
Slide 5

line 6: The post-condition of sumAux must establish the mid-condition M1 .


i.e. P1 ∧ Q2 ∧ res = sumAux(a,0); −→ M1

line 7: The mid-condition M1 must establish the post-condition of sum.


i.e. M1 ∧ return res; −→ Q1

Important: in the above we use sumAux as a “black box”.

Drossopoulou & Wheelhouse (DoC) Discrete Mathematics, Logic & Reasoning 5 / 25

4
Recursion Recursion

Reasoning about sum - Proof Obligations (Explicit)

The correctness of sum relies on the following arguments:


line 5: The pre-condition of sum must establish the pre-condition of sumAux.
Slide 6

P1 −→ P2 [i 7→ 0]

line 6: The post-condition of sumAux must establish the mid-condition M1 .


(P1 ∧ Q2 [i 7→ 0] ∧ res = r) −→ M1

line 7: The mid-condition M1 must establish the post-condition of sum.


(M1 ∧ r = res) −→ Q1

Drossopoulou & Wheelhouse (DoC) Discrete Mathematics, Logic & Reasoning 6 / 25

We discuss the development of each of these explicit proof obligations in more detail
below, paying particular attention to the substitutions and implicit assumptions we need
to make in each case to reflect the effects of the code.

5
Recursion Recursion

Reasoning about sum - Proof Obligations (Final)

line 5: The pre-condition of sum must establish the pre-condition of sumAux.


P1 −→ P2 [i 7→ 0]
Slide 7

a 6= null
−→
a 6= null ∧ 0 ≤ 0 ≤ a.length

Drossopoulou & Wheelhouse (DoC) Discrete Mathematics, Logic & Reasoning 7 / 25

There is no reference to the array contents in P1 or P2 , nor has any code run before line
5, so all we need to substitute here are the call values for sumAux on line 5.

6
Recursion Recursion

Reasoning about sum - Proof Obligations (Final)

line 6: The post-condition of sumAux must establish the mid-condition M1 .


(P1 ∧ Q2 [i 7→ 0] ∧ res = r) −→ M1
Slide 8

P
a 6= null ∧ a[..) ≈ a[..)pre ∧ r = a[0..) ∧ res = r
−→ P
a[..) ≈ a[..)pre ∧ res = a[..)

Drossopoulou & Wheelhouse (DoC) Discrete Mathematics, Logic & Reasoning 8 / 25

In general, when we make a method call that could potentially make updates to an array,
we need to carefully distinguish the contents of the array passed into the method (a[..)old )
from the initial array contents (a[..)pre ) and the current array contents (a[..)).
However, in this case no code has actually run before line 5, so we know that the array
contents are not modified before the call to sumAux. Thus we know that a[..)pre passed
into sumAux will be the same as a[..)pre passed into sum.

7
Recursion Recursion

Reasoning about sum - Proof Obligations (Final)

line 7: The mid-condition M1 must establish the post-condition of sum.


(M1 ∧ r = res) −→ Q1
Slide 9

P
a[..) ≈ a[..)pre ∧ res = a[..) ∧ r = res
−→ P
a[..) ≈ a[..)pre ∧ r = a[..)

Drossopoulou & Wheelhouse (DoC) Discrete Mathematics, Logic & Reasoning 9 / 25

The final proof obligation for sum is for a simple return statement. We have already seen
how to deal with such a case.
Recursion Recursion

Recursion - Example

1 int sumAux(int[] a, int i)


2 // PRE: a 6= null ∧ 0 ≤ i ≤ a.length
P (P2 )
3 // POST: a[..) ≈ a[..)pre ∧ r = a[i..) (Q2 )
4 {
Slide 10

5 if (i == a.length) {
6 // MID: a[..) ≈ a[..)pre ∧ i = a.length (M2 )
7 return 0;
8 } else {
9 // MID: a[..) ≈ a[..)pre ∧ a 6= null ∧ 0 ≤ i < a.length
(M3 )
10 int val = a[i] + sumAux(a, i+1); P
11 // MID: a[..) ≈ a[..)pre ∧ val = a[i..) (M4 )
12 return val;
13 }
14 }

Drossopoulou & Wheelhouse (DoC) Discrete Mathematics, Logic & Reasoning 10 / 25

8
Recursion Recursion

The Recursion Graphically


Slide 11

Drossopoulou & Wheelhouse (DoC) Discrete Mathematics, Logic & Reasoning 11 / 25

Recursion Recursion

Reasoning about sumAux - Proof Obs. (Informal)


The correctness of sumAux relies on the following arguments:
line 6: The pre-condition of sumAux must establish the mid-condition M2 .
i.e. P2 ∧ i == a.length −→ M2
line 7: The mid-condition M2 must establish the post-condition of sumAux.
i.e. M2 ∧ return 0; −→ Q2
Slide 12

line 9: The pre-condition of sumAux must establish the mid-condition M3 .


i.e. P2 ∧ ¬(i == a.length) −→ M3
line 10: The mid-condition M3 must establish the pre-condition of sumAux.
i.e. M3 −→ P2
line 11: The post-condition of sumAux must establish the mid-condition M4 .
i.e. M3 ∧ Q2 ∧ val = a[i] + sumAux(a, i+1); −→ M4
line 12: The mid-condition M4 must establish the post-condition of sumAux.
i.e. M4 ∧ return val; −→ Q2
Important: obs. for lines 10 and 11 reference the recursive sumAux call.
Drossopoulou & Wheelhouse (DoC) Discrete Mathematics, Logic & Reasoning 12 / 25

9
Recursion Recursion

Reasoning about sumAux - Proof Obs. (Explicit)


The correctness of sumAux relies on the following arguments:
line 6: The pre-condition of sumAux must establish the mid-condition M2 .
(P2 ∧ a[..) ≈ a[..)pre ∧ i = a.length) −→ M2
line 7: The mid-condition M2 must establish the post-condition of sumAux.
Slide 13

(M2 ∧ r = 0) −→ Q2
line 9: The pre-condition of sumAux must establish the mid-condition M3 .
(P2 ∧ a[..) ≈ a[..)pre ∧ i 6= a.length) −→ M3
line 10: The mid-condition M3 must establish the pre-condition of sumAux.
M3 −→ P2 [i 7→ i+1]
line 11: The post-condition of sumAux must establish the mid-condition M4 .
(M3 [a[..) 7→ a[..)old ] ∧ Q2 [a[..)pre 7→ a[..)old , i 7→ i+1] ∧ val = a[i]old + r) −→ M4
line 12: The mid-condition M4 must establish the post-condition of sumAux.
(M4 ∧ r = val) −→ Q2

Drossopoulou & Wheelhouse (DoC) Discrete Mathematics, Logic & Reasoning 13 / 25

We discuss the development of each of these explicit proof obligations in more detail
below, paying particular attention to the cases involved in the recursive call to sumAux.

10
Recursion Recursion

Reasoning about sumAux - Proof Obs. (Final)

line 6: The pre-condition of sumAux must establish the mid-condition M2 .


(P2 ∧ a[..) ≈ a[..)pre ∧ i = a.length) −→ M2
Slide 14

a 6= null ∧ 0 ≤ i ≤ a.length ∧ a[..) ≈ a[..)pre ∧ i = a.length


−→
a[..) ≈ a[..)pre ∧ i = a.length

Drossopoulou & Wheelhouse (DoC) Discrete Mathematics, Logic & Reasoning 14 / 25

As before, there is no reference to the array contents in P2 , nor has any code modified
the array or its contents before line 6.

11
Recursion Recursion

Reasoning about sumAux - Proof Obs. (Final)

line 7: The mid-condition M2 must establish the post-condition of sumAux.


(M2 ∧ r = 0) −→ Q2
Slide 15

a[..) ≈ a[..)pre ∧ i = a.length ∧ r = 0


−→ P
a[..) ≈ a[..)pre ∧ r = a[i..)

This proof relies on the following property:


P
∀k.[ a[k..k) = 0 ]

Drossopoulou & Wheelhouse (DoC) Discrete Mathematics, Logic & Reasoning 15 / 25

There are a two interesting points to note about the proof obligations above.
Firstly, since i = a.length we can infer from this that a 6= null. This is because
a.length returns a value which is comparable to i, so there cannot have been a null
pointer dereference.
Secondly,
P proving that r has the desired return value relies on recalling from the definition
of that:
X
∀k.[ a[k..k) = 0 ]
That is, the sum of any empty range is always equal to 0 (the identity element of addition).

12
Recursion Recursion

Reasoning about sumAux - Proof Obs. (Final)

line 9: The pre-condition of sumAux must establish the mid-condition M3 .


(P2 ∧ a[..) ≈ a[..)pre ∧ i 6= a.length) −→ M3
Slide 16

a 6= null ∧ 0 ≤ i ≤ a.length ∧ a[..) ≈ a[..)pre ∧ i 6= a.length


−→
a[..) ≈ a[..)pre ∧ a 6= null ∧ 0 ≤ i < a.length

Drossopoulou & Wheelhouse (DoC) Discrete Mathematics, Logic & Reasoning 16 / 25

This proof obligation is set up in the same way as that on line 6.

Recursion Recursion

Reasoning about sumAux - Proof Obs. (Final)

line 10: The mid-condition M3 must establish the pre-condition of sumAux.


M3 −→ P2 [i 7→ i+1]
Slide 17

a[..) ≈ a[..)pre ∧ a 6= null ∧ 0 ≤ i < a.length


−→
a 6= null ∧ 0 ≤ i+1 ≤ a.length

Drossopoulou & Wheelhouse (DoC) Discrete Mathematics, Logic & Reasoning 17 / 25

13
Clearly we could also explicitly denote the substitution on P2 of a 7→ a.
However, recall that we choose to omit unnecessary identity substitutions to ease the
notational burden of our proof obligations.

Recursion Recursion

Reasoning about sumAux - Proof Obs. (Final)

line 11: The post-condition of sumAux must establish the mid-condition M4 .


(M3 [a[..) 7→ a[..)old ] ∧ Q2 [a[..)pre 7→ a[..)old , i 7→ i+1] ∧ val = a[i]old + r) −→ M4
Slide 18

a[..)old ≈ a[..)pre ∧ a 6= null ∧ 0 ≤ i < a.length


P
∧ a[..) ≈ a[..)old ∧ r = a[i+1..) ∧ val = a[i]old + r
−→ P
a[..) ≈ a[..)pre ∧ val = a[i..)

Drossopoulou & Wheelhouse (DoC) Discrete Mathematics, Logic & Reasoning 18 / 25

It is important that the premise of our proof obligation includes the fact that
0 ≤ i < a.length
so that we know the array dereference of a[i] in the code is valid (does not result in an
ArrayOutOfBounds error).
Also note that if line 10 of our program were modified to:
int val = sumAux(a, i+1) + a[i]
then the proof obligation above would have to be slightly modified to track the new order
of execution. In particular, the effect of the code would now be described as:
val = r + a[i]
In this case, when we add the ith element of the array to the sum, we do so from the
array that was modified by the call to sumAux. Of course, our specification for sumAux
actually guarantees that the array is unmodified, so in this case the ordering makes no
difference.

14
Recursion Recursion

Reasoning about sumAux - Proof Obs. (Final)

line 12: The mid-condition M4 must establish the post-condition of sumAux.


(M4 ∧ r = val) −→ Q2
Slide 19

P
a[..) ≈ a[..)pre ∧ val = a[i..) ∧ r = val
−→ P
a[..) ≈ a[..)pre ∧ r = a[i..)

Drossopoulou & Wheelhouse (DoC) Discrete Mathematics, Logic & Reasoning 19 / 25

A lot of what we have covered in this section may feel quite mechanical and it is correct
to have this feeling. Everything we have done so far could be automated. In fact, there
are a number of proof assistant tools that can do just this.
However, when we reason about iterative programs in the next part of the course, we will
see that this requires more intuition, and so this is much harder to automate.

15
Recursion Recursion

Is There Anything We Have Overlooked?

Consider the following Java method sillySum:


1 int sillySum(int[] a)
Slide 20

2 // PRE: a 6= null P (P )
3 // POST: a[..) ≈ a[..)pre ∧ r = a[..) (Q)
4 {
5 return sillySum(a);
6 }
...so what about Termination?

Drossopoulou & Wheelhouse (DoC) Discrete Mathematics, Logic & Reasoning 20 / 25

Note that the sillySum method would actually satisfy its post-condition if it were to
terminate, so the method is partially correct.
However, termination of sillySum is clearly not guaranteed (or in this case even possible),
so the method is definitely not totally correct.

16
Recursion Recursion

Proving Termination - Measure of Progress


Slide 21

Drossopoulou & Wheelhouse (DoC) Discrete Mathematics, Logic & Reasoning 21 / 25

The length of the array that remains to be processed (a.length − i) decreases with each
recursive call to sumAux. We can use this “Measure of Progress” to reason about the
termination of the sumAux method.

17
Recursion Recursion

Proving Termination of sum

To show that sum terminates, we need to prove that

∀a ∈ int[], i ∈ Z.[ 0 ≤ i ≤ a.length −→ sumAux(a,i) terminates ]


Slide 22

which we could tackle using mathematical induction considering the length


of the remaining array to process i.e.

a.length − i

Drossopoulou & Wheelhouse (DoC) Discrete Mathematics, Logic & Reasoning 22 / 25

We prove termination of the sumAux method via mathematical induction on the length
of the remaining array to be processed, i.e. a.length − i.

Base case:
To show:
∀a ∈ int[], i ∈ Z.
[ 0 ≤ i ≤ a.length −→ sumAux(a,i) terminates when a.length − i = 0 ]
Proof:
Take arbitrary a ∈ int[] and i ∈ Z where 0 ≤ i ≤ a.length.
a.length − i = 0 −→ i = a.length
When i = a.length, SumAux enters the first branch of the conditional and termi-
nates on line 7.

18
Inductive Step:
Take arbitrary k ∈ N.
Inductive Hypothesis:
(IH): ∀a ∈ int[], i0 ∈ Z.
[ 0 ≤ i0 ≤ a.length −→ sumAux(a,i0 ) terminates when a.length − i0 = k ]
To show:
∀a ∈ int[], i ∈ Z.
[ 0 ≤ i ≤ a.length −→ sumAux(a,i) terminates when a.length − i = k + 1 ]
Proof:
Take arbitrary a ∈ int[], i ∈ Z where 0 ≤ i ≤ a.length.
a.length − i = k + 1 −→ i = a.length − k − 1
i 6= a.length (since k ∈ N) so sumAux enters the second branch of the conditional.
a.length − i = k + 1 −→ i + 1 = a.length − k
and i + 1 > 0, by choice of i, so applying (IH) (with i0 as i + 1) we know that
sumAux(a,i+1) will terminate.
So, SumAux will then terminate on line 12.

Recursion Recursion

Comparison with Induction


Recall the Haskell function sum and its tail-recursive version sum tr from
earlier in the course:
sum :: [ Int ] -> Int
sum [] = 0
sum i : is = i + sum is
Slide 23

sum_tr :: [ Int ] -> Int -> Int


sum_tr [] k = k
sum_tr ( i : is ) k = sum_tr is ( i + k )
Using Structural Induction we were able to prove:
∀is:[Int]. sum is = sum tr is 0
However, we have just proven a very similar property for our Java method
sumAux, namely that is satisfies the specification:
P
{ a 6= null } int res = sumAux(a,0); { res = a[..) }
but here we did not make use of any form of Induction ... or did we ... ?
Drossopoulou & Wheelhouse (DoC) Discrete Mathematics, Logic & Reasoning 23 / 25

19
Recursion Recursion

Comparison with Induction


The correctness of sumAux relies on the following arguments:
line 6: The pre-condition of sumAux must establish the mid-condition M2 .
(P2 ∧ a[..) ≈ a[..)pre ∧ i = a.length) −→ M2
line 7: The mid-condition M2 must establish the post-condition of sumAux.
(M2 ∧ r = 0) −→ Q2
line 9: The pre-condition of sumAux must establish the mid-condition M3 .
Slide 24

(P2 ∧ a[..) ≈ a[..)pre ∧ i 6= a.length) −→ M3


line 10: The mid-condition M3 must establish the pre-condition of sumAux.
M3 −→ P2 [i 7→ i+1]
line 11: The post-condition of sumAux must establish the mid-condition M4 .
(M3 [a[..) 7→ a[..)old ] ∧ Q2 [a[..)pre 7→ a[..)old , i 7→ i+1] ∧ val = a[i]old + r) −→ M4
line 12: The mid-condition M4 must establish the post-condition of sumAux.
(M4 ∧ r = val) −→ Q2

lines 6 - 7 corresponds to proving the Base Case


lines 9 - 12 correspond to proving the Inductive Step
line 10 corresponds to checking that we can apply the Inductive Hypothesis
line 11 use of Q2 corresponds to use of the Inductive Hypothesis
Drossopoulou & Wheelhouse (DoC) Discrete Mathematics, Logic & Reasoning 24 / 25

Recursion Recursion

Reasoning about Recursion - Conclusions

Reasoning about recursive functions in Java resembles reasoning about


recursive functions in Haskell:
Reasoning about the cases where a function terminates corresponds
Slide 25

to proving the base cases.


Reasoning about the cases where a function calls itself recursively
corresponds to proving the inductive steps.
The point where we establish that the pre-condition of the callee
holds, corresponds to establishing that the inductive hypothesis holds.
The point where we establish that the post-condition of the callee
implies the mid-condition of the caller, corresponds to the conclusion
of the inductive step.

Drossopoulou & Wheelhouse (DoC) Discrete Mathematics, Logic & Reasoning 25 / 25

20

You might also like