Sie sind auf Seite 1von 6

Module 0046: Pre and post conditions

Tak Auyeung, Ph.D. February 7, 2012

About this module


Prerequisites: 0011, 0012, 0013 Objectives: This module introduces the concepts of pre and post conditions for various types of statements.

What is this crazy math?

So you thought itd be easy becoming a programmer. There are two schools of thought regarding the role of mathematics in programming. One school, led by Edsger Dijkstra, suggests that a programmer should be mathematician rst. At the very least, every programmer should have a solid foundation of mathematics. Furthermore, computer science should be a branch of mathematics. For a more detailed discussion of this topic, visit http://en.wikipedia.org, and search for cruelty computer science. Another school, including Donald Knuth (another giant and pioneer of computer science), claims that with sucient advances of tools, everyone can program computers. This is at least partially true. In the good old days, programs are only written in machine language, consisting of long strings of 0s and 1s. Later on, with symbolic assemblers, writing programs became easier. These days, with Visual Studio and other development environments, the tools actually helps to x problems not only in syntax, but also sometimes the logic of programs. Nonetheless, it is still important for programmers to understand enough logic and mathematics. Programming by intuition is possible, but it only enables a programmer to a certain level. If not for anything else, a solid background of logic and mathematics enables a programmer trouble shoot faulty programs with great eciency. Most programmers who are well trained in mathematics also tend to design more elegant programs that minimize chances of errors to begin with.

Pre and post conditions

A pre condition is simply what we know before the execution of a statement in a program. A post condition is what we know after the execution of a statement in a program. The analysis of the pre and post conditions of a program is essential, because we can prove the correctness of a program (or an algorithm) rigorously. If you consider an algorithm, the pre condition of the entire algorithm is what is given to the program. The post condition of an entire algorithm is the outcome of a program. Being able to mathematically derive the post condition of an algorithm based on its pre condition means we can prove whether an algorithm does what it is supposed to. There is no need to use test cases anymore, because a proof of correctness covers all cases that can be thrown at the algorithm.

3.1

Constant assignment

Lets start with something thats relatively simple to deal with. Algorithm 1 is just a sequence of assignment statements.
1: 2: 3: 4:

x0 y2 x5 y1 Algorithm 1: A sequence of constant assignments. 1

Assuming this algorithm stands alone, there is no pre condition to it. In our notation, pre(1) represents the pre condition of the statement on line 1. Because line 1 is the rst line of code, pre(1) also represents the pre condition of the entire algorithm. Note that a pre condition is a condition. It expresses something that is true. How do we express true when we dont know anything is true? Well, we know something is always true, it is true itself! Consequently, we can simply say that pre(1) = true. What is the post condition of line 1? Since this is a constant assignment statement, we know that variable x must end up with a value of 0. In other words, we know that x = 0 after this statement. Consequently, we add the fact to pre(1) so that post(1) = pre(1) (x = 0) If you look up the denition of conjunction, x true is simply x. We can simplify our post condition so that post(1) = (x = 0). The same argument applies to line 2. Note that pre(2) = post(1) because there is nothing that can change what we know about the program between the lines. What is post(2)? Line 2 changes variable y to a constant of 2. We know that after line 2, y = 2. However, this is not the only thing that we know. What we knew earlier that x = 0 is still true. As a result, post(2) = (x = 0) (y = 2). Here comes the tricky part. What is post(3)? If we simply add the fact that x = 5, then we end up with post(3) = (x = 0) (y = 2) (x = 5). However, this is impossible because x = 0 contradicts x = 5. What do we need to do? What we need is a way to forget some facts because a new fact is in, and the new fact may contradicts some old facts. This is accomplished by a forget operation. let us dene forget(c, v) to mean forgetting all components in condition c that refers to variable v . In our example, we can now derive post(3) = forget(post(2), x) (x = 5). Literally, this means the post condition of line 3 is essentially the same as the post condition of line 2, except that all components referring to x should be forgotten. Then, we add the fact that x = 5. This results in the following derivation: forget(post(2), x) (x = 5) forget((x = 0) (y = 2), x) (x = 5) (y = 2) (x = 5)

post(3)

= = =

(1) (2) (3)

Now, onto the last statement. We only have to reapply what we learned in the previous step: forget(post(3), y) (y = 1) forget((y = 2) (x = 5), y) (y = 1) (x = 5) (y = 1)

post(4)

= = =

(4) (5) (6)

Are you surprised at the result? Let us consider the general case:
1:

xe Assuming e does not refer to variable x itself, then post(1) = forget(pre(1), x) (x = e).

3.2

Resolving references to variables to constants

If there are any chainable equalities, you should try to get everything resolved to constants whenever possible. For example, let us consider the following post condition: post(1) = (x < y) (y = 10) Here, we already know, for sure, that y has a value of 10. This means that the inequality (x < y ) can now be resolved as (x < 10). The main purpose to do this is to decouple variables from each other. In other words, we want to lessen the impact of a change to one variable to conditions that involve other variables. In this example, a later assignment can change the value of y . However, that will not impact the fact that we already know that (x < 10).

1: 2: 3: 4:

x0 y2 xx+3 y y1 Algorithm 2: A sequence with self referencing assignment.

3.3

Simple self referencing assignments

Now that we can handle constant assignments and a sequence, lets move on to something slightly more interesting. Let us consider algorithm 2. Note that the rst two statements of algorithm 2 are the same as the rst two statements of algorithm 1. Lets start with the rst statement that is dierent. We know that pre(3) = (x = 0) (y = 2). What is the post(3)? Let us examine what we are doing on line 3. In this assignment statement, we rst evaluate the right hand side. x = 0 at this point, so x + 3 = 3. Then, we take the value of 3, and use that to update x. In other words, x = 3 after the statement. In the analysis of post conditions, we have a more obsure way to come to the same conclusion. First, let us dene a function f (x) = x + 3. This is really just a notation so that f (3) = 6, f (2) = 1 and etc. When some functions, we can also dene an inverse function. In this case, the inverse function is f 1 (x) = x 3. What this really means is simply that f 1 (f (x)) = x. In other words, f 1 undoes whatever f does. Next, let us dene a substitution operation. sub(c, x, y) means that in condition c, replace all occurances of x with y . For example, sub(x + y = z, y, y 1) = x + (y 1) = z. With these new tools, we can get back to work. Let us keep the denition that f (x) = x + 3 and f 1 (x) = x 3. Then we have the following derivation: sub(pre(3), x, f 1 (x)) sub((x = 0) (y = 2), x, x 3) ((x 3) = 0) (y = 2) (x = 3) (y = 2)

post(3)

= = = =

(7) (8) (9) (10)

Of course, this doesnt really come as a surprise. However, we now know why it is the case! On to the last statement, we dene g (y ) = y 1. The inverse function is g 1 (y ) = y + 1 so that g 1 (g (y )) = y . Given this, we can nish the derivation: sub(post(3), y, g1 (y)) sub((x = 3) (y = 2), y, y + 1) (x = 3) ((y + 1) = 2) (x = 3) (y = 1)

post(4)

= = = =

(11) (12) (13) (14)

This is yet another earth-breaking discovery (not!). Although it may seem that we could have used intuition (sometimes called eyeball proof) to come to the same conclusion, the tools that we have used in this section are very general and can be applied to much more dicult problems. We can now derive the general case as follows:
1:

x f (x) Assume that f (x) is a function of x that has an inverse function f 1 (x), then post(1) = sub(pre(1), x, f 1 (x)).

3.4

Revisiting the Forget operation

The forget operation is applicable for assignment statements whenever the substitution operation cannot be performed. This means that the following statement should use the forget operation:
1:

xy

This is because the RHS of the assignment does not refer to the LHS. There is no inverse function to reverse the new value of x back to its previous value before the assignment statement. As a result, you can use the substitution rule if and only if the following conditions are all true for an assignment statement x e (e is an expression):
The RHS (e) refers to the LHS. If so, let us dene f (x) = e There is an inverse function f (x) such that f (f (x)) = x

For all other assignment statements, the forget rule should be used. This include the following statement:
1:

xxx

This is because even though the RHS refers to the LHS in this case, it is not reversible. Once variable x becomes zero after this statement, there is no way to recover its previous value before the assignment statement.

3.5

Conditional statements

Lets consider algorithm 3.5 that makes z the maximum of x and y .


1: 2: 3: 4: 5:

if x > y then zx else zy end if

Let us express the pre and post conditions of each statement. In this case, let us not to assume any values in the variables. In other words, pre(1) = true The pre condition of line 2 is not just true. This is because the only way that we get to line 2 is that the condition x > y must be true. Consequently, pre(2) = (x > y). Using the same argument, the only way we get to line 4 is that (x > y ), which is equivalent to saying x y . As a result, pre(4) = (x y). The post condition of line 2 is simply post(2) = pre(2) (z = x) = (x > y) (z = x). Similarly, the post condition of line 4 is post(4) = pre(4) (z = y) = (x y) (z = y). Here comes the trickly part: what is post(5)? In other words, after the entire conditional statement, what can we conclude? There are two ways to get to line 5. We can get there from line 2, or from line 4. Consequently, the post condition of line 5 is the post condition of line 2 or the post condition of line 4. In other words, post(2) post(4) ((x > y ) (z = x)) ((x y ) (z = y ))

post(5)

= =

(15) (16)

Does this make sense? Here is the general conditional statement: if c then then-block else else-block 5: end if
1: 2: 3: 4:

pre(2) = pre(1) c pre(4) = pre(1) c post(2) has no general form, it depends on the actual code of then-block. Note that even c may not true after the code of then-block.

post(4) has no general form, it depends on the actual code of else-block. Note that c may actually be true after the code of else-block. post(5) = post(2) post(4)

3.6

Loops

We are now on our nal stretch! Let us consider algorithm 3.


1: 2: 3: 4:

x0 while x < 3 do xx+1 end while Algorithm 3: A sample prechecking (while) loop for analysis.

Lets begin with pre(2). After the initialization of x, we know that pre(2) = (x = 0). However, the post condition of line 2 is not as simple. This is because there are actually two ways to get to line 2. In the rst iteration, we enter from line 1. However, in subsequent iterations, we enter from line 3. On the other hand, no matter which way we come from, we know that the condition (x < 3) must be true if we enter the loop. As a result, all we say at this time is that post(2) = (x < 3) ((x = 0) post(3)). Note that pre(3) = post(2). The statement on line 3 looks familiar. It involves a function of x. In this case, the inverse function is x 1. We can then derive the following: post(3) = = = sub(pre(3), x, x 1) ((x 1) < 3) (((x 1) = 0) post(3)) (x 3) ((x = 1) post(3)) (17) (18) (19)

This is interesting because the same notation appears on the left and the right hand side of the equation. The solution is to nd a denition that works in this equation. Choosing post(3) = (x 3) works. This is because (x = 1) (x 3) simplies to x 3 because it includes the case of x = 1. Furthermore, (x 3) (x 3) can also simplify to just x 3. In conclusion, we can dene post(3) = (x 3). How about post(4)? In theory, we can get there using two means. First, it is possible not to enter the loop at all. Second, we can go through the loop at least once, then exit the loop. Regardless of whether we get into the loop or not, one thing is for sure: x < 3 must be false. As a result, post(4) = (x < 3) (pre(2) post(3)) = = = (x 3) ((x = 3) (x 3)) (x 3) (x 3) (x = 3) (20) (21) (22) (23)

In other words, when we exit the loop, we know that x = 3. In its general form, a prechecking loop is as follows:
1: 2: 3:

while c do block end while

post(1) = pre(2) = c (pre(1) post(2)) post(3) = c (pre(1) post(2)) The trick is to nd a good denition of post(2)! post(2) is also called the loop invariant.

The general form of a postchecking loop is as follows: 5

1: 2: 3:

repeat block until c


pre(2) = pre(1) post(2) post(3) = c post(2) Again, the trick is to nd a good denition for post(2), it is also called the loop invariant.

Das könnte Ihnen auch gefallen