252-0027
Einführung in die Programmierung
11.0 Systematisches Programmierung
Thomas R. Gross
Department Informatik
Uebersicht
§ 11.1 Einführung
§ 11.2 Aussagen
§ 11.3 Preconditions und Postconditions
§ 11.4 Hoare Tripel
2
Methoden von List
add(value) appends value at end of list
add(index, value) inserts given value at given index, shifting subsequent values right
clear() removes all elements of the list
indexOf(value) returns first index where given value is found in list (-1 if not found)
get(index) returns the value at given index
remove(index) removes/returns value at given index, shifting subsequent values left
set(index, value) replaces value at given index with given value size() returns the number of elements in list
§ Was fehlt in dieser Zusammenfassung?
a) Einige Methoden fehlen ….
b) Information über Laufzeitkosten
c) Information über Annahmen und Anforderungen (wann kann eine Methode verwendet werden, was sind legale
Parameterwerte)
4
Verantwortlichkeiten
§ Sehen wir uns nochmal die Methode gcd an:
/**
* Calculates the greatest common divisor
* using Euclid's algorithm.
** Returns The greatest common divisor of x and y
*/int gcd(int x, int y) { int r = x % y;
while (r != 0) { x = y;
y = r;
r = x % y;
}
6
int gcd(int x, int y) { int r = x % y;
while (r != 0) { x = y;
y = r;
r = x % y;
} return y;
}
§ Was für Annahmen macht diese Methode?
§ Liefert sie immer das gewünschte Ergebnis?
§ Was für Fälle sollten wir untersuchen?
§ Schreiben Sie die Fälle auf, die Sie für "interessant" halten
§ Simulieren Sie die Ausführung
9
Verantwortlichkeiten
§ Diese Implementation des ggT erwartet
§ x ≥ 0
§ y > 0
§ Wer ist dafür "verantwortlich"?
§ Klient?
§ Die Methode?
Klient
/**
* Creates a new Rational with the value x / y.
*/
public Rational(int x, int y) {
int g = gcd(Math.abs(x), Math.abs(y));
num = x / g;
den = Math.abs(y) / g;
if (y < 0) num = -num;
} 11
Verantwortlichkeiten
§ (Teil)Lösung hier
gcd(Math.abs(x), Math.abs(y));
§ Kommentar wäre nicht verfehlt
§ gcd kann davon ausgehen dass x ≥ 0
§ "x ≥ 0" ist eine Aussage die hier immer gilt
§ Nicht gelöst ist dass y ≠ 0 sein muss
§ Beispiel soll nicht überladen werden
(Logische) Aussagen
§ Aussage ("assertion"): Eine Behauptung die entweder wahr oder falsch ist.
§ Wir fragen dann oft "Ist die Aussage wahr"?
§ Beispiele:
§ x ≥ 0 (hängt von x ab)
§ Zürich ist ein Kanton der Schweiz
§ Maseru ist die Hauptstadt Lesothos
§ 11 ist eine Primzahl
§ 120 ist kleiner als 11
§ x geteilt durch 2 ergibt 8 (hängt von x ab) 13
(Logische) Aussagen
§ Nicht alle Aussagen sind wahr
§ Für einige Aussagen können wir vielleicht nicht sofort entscheiden ob sie wahr oder falsch sind
§ Wichtig ist dass es Sinn macht zu fragen, ob die Aussage wahr oder falsch ist
§ Logisch heisst hier: im Sinne der klassischen Logik
Arbeiten mit Aussagen
§ Nehmen wir dieses Code Beispiel
if (x > 3) {
// Point A } else {x--;
// Point B x++;// Point C }// Point D
§ Was können wir über den Wert von x an den 4 Stellen sagen?
§ Ist x > 3? Immer? Manchmal? Nie?
15
Aussagen und Programme
§ Wir können Aussagen über das Programm machen und fragen ob sie an bestimmten Stellen wahr sind.
§ Gültige Antworten sind IMMER, NIE, oder MANCHMAL.
System.out.print("Type a nonnegative number: ");
double number = console.nextDouble();
// Point A: is number < 0.0 here?
while (number < 0.0) {
// Point B: is number < 0.0 here?
System.out.print("Negative; try again: ");
number = console.nextDouble();
// Point C: is number < 0.0 here?
}
// Point D: is number < 0.0 here?
(MANCHMAL) (IMMER)
(MANCHMAL) (NIE)
Mit Aussagen arbeiten …
§ Direkt nachdem eine Variable initialisiert wurde, ist ihr Wert bekannt: int x = 3;
// is x > 0? IMMER
§ Ueber die Werte von Parametern wissen wir i.A. nichts:
public static void mystery(int a, int b) { // is a == 10? MANCHMAL
§ Innerhalb eines if, while, usw. wissen wir evtl. etwas:
public static void mystery(int a, int b) { if (a < 0) {
// is a == 10? NIE
Aussagen und Schleifen
§ Zu Beginn des Rumpfes einer Schleife muss der Test true , ergeben haben:
while (y < 10) {
// is y < 10? IMMER ...
}
§ Direkt nach einer Schleife muss der Test false sein:
while (y < 10) { ...
}
// is y < 10? NIE
Aussagen und Schleifen
§ Im Rumpf einer Schleife kann der Test false ergeben:
while (y < 10) { y++;
// is y < 10? MANCHMAL }
"Manchmal"
§ Verschiedene Aktivitäten können dazu führen, dass der Wert einer Variable unbekannt ist (diese führen dann zu
"MANCHMAL" Antworten):
§ Eingabe von Scanner lesen
§ Lesen einer Zahl von einem Random Objekt
§ Uebergabe eines Parameters
§ Wenn für eine Stelle im Programm sowohl die Antwort ”ja"
also auch "nein" möglich ist, dann ist die richtige Antwort
"manchmal".
§ Wenn man sich nicht sicher ist dann ist "Manchmal" eine gute Vermutung
Beispiel 1
public static void mystery(int x, int y) { int z = 0;
// Point A
while (x >= y) { // Point B x = x - y;
z++;
if (x != y) { // Point C z = z * 2;
}
// Point D }
x < y x == y z == 0 Point A
Point B Point C Point D Point E
MANCHMAL MANCHMAL IMMER NIE MANCHMAL MANCHMAL
MANCHMAL NIE NIE
MANCHMAL MANCHMAL NIE
IMMER NIE MANCHMAL
Welche Aussagen sind an diesen Stellen wahr?
Möglich sind IMMER, NIE oder MANCHMAL.
26
Beispiel 2
public static int mystery(Scanner console) { int prev = 0;
int count = 0;
int next = console.nextInt();
// Point A
while (next != 0) { // Point B
if (next == prev) { // Point C
count++;
}
prev = next;
next = console.nextInt();
// Point D }
next == 0 prev == 0 next == prev Point A
Point B Point C Point D Point E
MANCHMAL IMMER MANCHMAL
NIE MANCHMAL MANCHMAL
NIE NIE IMMER
MANCHMAL NIE MANCHMAL
IMMER MANCHMAL MANCHMAL
Welche Aussagen sind an diesen Stellen wahr?
Möglich sind IMMER, NIE oder MANCHMAL.
Beispiel 3
// Assumes y >= 0, and returns x^y public static int pow(int x, int y) {
int prod = 1;
// Point A
while (y > 0) { // Point B
if (y % 2 == 0) { // Point C x = x * x;
y = y / 2;
// Point D } else {
// Point E
prod = prod * x;
y--;
// Point F } }
// Point G return prod;
}
y > 0 y % 2 == 0 Point A
Point B Point C Point D Point E Point F Point G
Welche Aussagen sind an diesen Stellen wahr?
Möglich sind IMMER, NIE oder MANCHMAL.
y > 0 y % 2 == 0 Point A MANCHMAL MANCHMAL
Point B IMMER MANCHMAL
Point C IMMER IMMER
Point D IMMER MANCHMAL
Point E IMMER NIE
Point F MANCHMAL IMMER
Point G NIE IMMER
Uebersicht
§ 11.1 Einführung
§ 11.2 Aussagen
§ 11.3 Preconditions und Postconditions
§ 11.4 Hoare Tripel
Ziel: Aussagen über ein Programm herleiten
§ Welche Aussagen gelten (an einer Stelle) im Programm?
§ Ggf. was für Annahmen sind nötig, dass die Aussage wahr ist
§ Warum?
§ Finden "interessanter" Fälle für Test
§ Erstellen der Bedingungen, die Klienten erfüllen müssen
§ Behandeln von Sonderfällen in einer Methode
§ Wenn die Liste am Anfang leer ist, dann muss das 1. Element auf diese Weise eingefügt werden.
31
Hoare Logik
§ Tony Hoare entwickelte in den (19)70gern einen Ansatz wie man über Programme logische Schlüsse ziehen kann.
§ Am Anfang betrachten wir nur Zuweisungen, If-Statements, Schleifen und Variable
§ Fürs erste keine Objekte und Methoden
§ Schritt 1: Vorwärts und rückwärts schliessen
§ Schritt 2: Genauere Definition von Aussagen, Vor- und
Nachbedingungen
Warum
§ Ziel ist es, dass Sie für ein einfaches Programm genau argumentieren können.
§ In der täglichen Programmentwicklung genügt es oft, weniger detailliert zu argumentieren als es die Hoare Logik erforderlich macht.
§ Für einfache Programme ist dieser Ansatz zu aufwändig.
§ Für realistische Progamme wird der Ansatz schnell kompliziert.
§ Wir haben immer noch kein gutes Modell für Objekte und Parallelismus. Aliasing ist eine Herausforderung.
§ Aber manchmal hilft der Ansatz (siehe Fencepost Probleme) 33
§ Aber eine gute Schulung, systematisch zu programmieren
§ Wir können über Zustände (der Ausführung) eines Programms (und später auch eines Objekts) schlussfolgern
§ Wir können den Effekt eines Programms beschreiben
§ Wir können definieren was es heisst dass eine Aussage “weaker”
(schwächer) oder “stronger” (stärker) ist.
§ Wichtig für die Definition von Schnittstellen (zwischen Modulen)
wenn wir entscheiden müssen welche Bedingungen erfüllt sein
Beispiel
§ Wie finden wir Aussagen für Stellen im Programm
§ Uns interessieren nicht irgendwelche Aussagen sondern solche, die das Verhalten der Ausführung beschreiben
35
Beispiel
§ Vorwärts schliessen
§ Vom Zustand vor der Ausführung eines Programm(segments)
§ Nehmen wir an wir wissen (oder vermuten) w > 0 // w > 0
x = 17;
//
y = 42;
//
z = w + x + y;
Beispiel
§ Vorwärts schliessen
§ Vom Zustand vor der Ausführung eines Programm(segments)
§ Nehmen wir an wir wissen (oder vermuten) w > 0 // w > 0
x = 17;
// w > 0 ∧ x == 17 y = 42;
// w > 0 ∧ x == 17 ∧ y == 42 z = w + x + y;
// w > 0 ∧ x == 17 ∧ y == 42 ∧ z > 59
§ Jetzt wissen wir einiges mehr über das Programm, u.a.
z > 5937Beispiel
§ Rückwärts schliessen:
§ Nehmen wir an wir wollen dass z nach Ausführung negativ ist //
x = 17;
//
y = 42;
//
z = w + x + y;
Beispiel
§ Rückwärts schliessen:
§ Nehmen wir an wir wollen dass z nach Ausführung negativ ist // w + 17 + 42 < 0
x = 17;
// w + x + 42 < 0 y = 42;
// w + x + y < 0 z = w + x + y;
// z < 0
§ Dann müssen wir wissen (oder vermuten) dass vor der Ausführung gilt: w < -59
§ Notwendig und hinreichend 39
Vorwärts vs Rückwärts, Teil 1
§ Vorwärts schliessen:
§ Bestimmt was sich aus den ursprünglichen Annahmen herleiten lässt.
§ Sehr praktisch wenn eine Invariante gelten soll
§ Rückwärts schliessen:
§ Bestimmt hinreichende Bedingungen die ein gegebenes Ergebnis garantieren
§ Wenn das Ergebniss erwünscht ist, dann folgt aus den Bedingungen die Korrektheit.
§ Ist das Ergebniss unerwünscht, dann reichen die Bedingungen um
Vorwärts vs Rückwärts, Teil 2
§ Vorwärts schliessen:
§ Simuliert die Ausführung des Programms (für viele “Inputs”
“gleichzeitig”)
§ Oft leicht zu verstehen, erscheint “natürlich”
§ Aber führt dazu dass (viele) Details festgehalten werden, die letztlich irrelevant sind.
41
Vorwärts vs Rückwärts, Teil 3
§ Rückwärts schliessen:
§ Oft von grossem praktischen Nutzen: Sie müssen verstehen (oder festhalten) was jede Anweisung zum Erreichen eines bestimmten Zustandes beiträgt.
§ Ein Programm(segment) ”rückwärts” zu lesen erfordert Uebung aber führt zu einer neuen Sicht auf ein Programm.
If-Statement Muster
// initial assumptions if(…) {
// also know test evaluated to true } else {
// also know test evaluated to false }
// either branch could have executed
43
Terminologie
§ Die Annahme (Aussage), die vor der Ausführung eines Programmsegments gilt, ist die Precondition.
§ Die Aussage, die nach der Ausführung gilt (unter der
Annahme dass die Aussage davor gültig ist), ist die
Postcondition.
Grundidee(n)
1. Die Precondition für den then-part und den else-part (eines if- Statements) beinhaltet das Ergebnis des Tests.
2. Die Postcondition nach dem if-Statement ist die Disjunktion (“oder”) der Postconditions des then- und else-parts.
45
Beispiel (Vorwärts)
Nehmen wir an x >= 0 // x >= 0
z = 0;
// x >= 0 ∧ z == 0 if (x != 0) {
// x >= 0 ∧ z == 0 ∧ x != 0 (also x > 0) z = x;
// … ∧ z > 0 } else {
// x >= 0 ∧ z == 0 ∧ !(x != 0) (also x == 0) z = x + 1;
// … ∧ z == 1 }
Die übliche Notation
§ Statt die Pre/Postconditions in Kommentaren (nach //) schreiben verwenden wir {…}
§ Kein Java mehr …
§ Aber diese Schreibweise hat sich eingebürgert, lange vor Java { w < -59 }
x = 17;
{ w + x < -42 }
47
51
Mehr Terminologie
§ Eine Aussage gilt für den Zustand eines Programms wenn die Auswertung der Aussage mit dem Zustand das Ergebnis true ergibt.
§ Die Auswertung jedes Variable ergibt den Wert der Variable im augenblicklichen Programmzustand.
§ Eine Aussage beschreibt eine Menge von Programmzuständen – genau die, für die die Aussage gilt.