msgbartop
Gruppe F1
msgbarbottom

26 Nov 08 Ein paar Worte zu ensure und debug…

Das Merkblatt zu FJava sollte sich ja jeder ausdrucken und unters Kopfkissen legen und möglichst auch verstehen, aber ich möchte heute die Aufmerksamkeit auf die Sektion “Hilfsfunktionen” lenken.

Es werden zwei überaus nützliche Funktionen besprochen: ensure() und debug(), die außerdem für zwei klassische Hilfsmittel beim Programmieren stehen´.

debug()

debug kann man dazu verwenden um während der Ausführung etwas auszugeben. Der Name stammt vom Englischen to debug und wird in der Informatik verwendet um den Prozess der Fehlersuche und –korrektur in einem Computerprogramm, das nicht so läuft wie es soll, zu bezeichnen.

Die Ausgabe von Hilfsinformationen während das Programm läuft ist zugleich die älteste und die universellste Art Fehler zu suchen und den Ablauf des Programmes zu verfolgen. Praktisch alle Programmiersprachen und Systeme unterstützen eine Ausgabe von Daten zum Debuggen und der Programmierer ist frei in der Art und Weise, wann und wo er solche zusätzlichen Ausgaben hinzufügt.

Später im Verlauf der Vorlesung werden wir sicher noch weitere und mächtigere Mittel kennenlernen, die einem die Fehlersuche erleichtern, aber diese Mittel sind dann im Allgemeinen spezieller und aufwändiger.

Im Merkblatt wird im Beispiel kurz darauf eingegangen, wie man auch Variablen ausgibt, aber man kann auch beliebigen Ausrücken ausgeben.

debug( seq );

debug( "seq: " + seq );

debug( "first(seq): " + first(seq) );

Falls der Code nicht so funktioniert, wie er soll, und man die Ursache durch Lesen des Codes findet, kann man einfach in die Funktion ein paar debug()-Aufrufe einstreuen und sich dann beim Ablauf anschauen, wie sich die Werte verändern – dies ist besonders bei rekursiven Funktionen nützlich.

ensure()

ensure ist ein anderes sehr mächtiges Werkzeug, das von der Idee auch bei der Programmverifikation eingesetzt wird. In den meisten Programmiersprachen wird die Anweisung mit assert bezeichnet, aber ensure ist vom Sinn genauso verständlich. ensure stellt sicher, dass eine Bedingung zur Laufzeit gilt, ansonsten wird eine Fehlermeldung ausgegeben.

Man kann ensure benutzen um einerseits seinen Code gegen “falsche” Aufrufe zu sichern, als Beispiel:

int sub(int a, int b) {
 ensure( b >= 0 );
 if( b == 0 ) {
   return a;
 }
 else {
   return sub(a, b - 1) - 1;
 }
}

Ohne ensure könnte man sub(1, -1) aufrufen und das Programm würde in einer Endlosrekursion abstürzen ohne das man eine Ahnung hat wieso – in diesem Beispiel ist es noch einfach, aber spätestens bei größeren Funktionen, die sich gegenseitig aufrufen, kann’s unangenehm werden.

Man kann hier ensure benutzen um seinen Code gegen Missstände von außen abzusichern und gleichzeitig um deutlich zu machen, unter welchen Bedingungen man erwarten kann, dass die Funktion funktioniert :-)

Ein Vorteil von ensure gegenüber einfachen Kommentaren im Quellcode ist, dass ensure eine Anweisung ist, die auch ausgeführt wird und deswegen auch immer aktuell ist – im Gegensatz zu Kommentaren, die schon mal veraltet sein können..

Auch bei ensure kann man wieder eine Fehlermeldung ausgeben, die man selbst wählen kann – mit den gleichen Mittel wie bei debug:

ensure( !isEmpty( seq ), "Sequenz darf nicht leer sein!" );

ensure( i > 5, "i > " + i );

ensure( first(seq) > 0, "first(seq): " + first(seq) + " > 0" );

Man kann aber auch nur die Bedingung in ensure beschreiben und den Text weglassen:

ensure( true );

ensure( i > 0);

ensure( first(seq) > 0 );

Noch ein Beispiel zur “Programmverifikation”:

int sub(int a, int b) {
 ensure( b >= 0 );
 if( b == 0 ) {
   return a;
 }
 else {
   final int result = sub(a, b - 1) - 1;
   ensure( result == a - b );
   return result;
 }
}

Damit kann man bei jedem Aufruf sicherstellen, dass sub auch wirklich subtrahiert. Das ist natürlich kein Beweis für die Korrektheit der Funktion oder auch nur richtige Programmverifikation, aber es reicht auf jeden Fall aus um auch bei komplizierteren Funktionen zu testen, ob die Funktion sich in den Testfällen verhält, wie erwartet – weiterführend in dieser Richtung ist der Ansatz Design by Contract, bei dem bei jeder Funktion genau die Vor- und Nachbedingungen festgelegt werden (siehe oben die ensure-Bedingungen bei sub).

Also…

debug ist nützlich, wenn man das Programm besser verstehen will und/oder die Fehlerstelle einkreisen will. ensure kann man beim Entwickeln und allgemein nutzen um sicherzustellen, dass bestimmte Bedingungen zur Laufzeit erfüllt sind.
Die beiden Konzepte sind nicht orthogonal zu einander, aber man zumindest sagen, dass debug eher verwendet wird, wenn man Fehler sucht und zusätzliche Informationen braucht (also beim Testen und Debuggen) und ensure schon beim Schreiben des Codes eingesetzt wird um die Absichten des Autors einerseits und Beschränkungen des Codes andererseits deutlicher zu kennzeichnen.

Wie immer sind Kommentare und Feedback erwünscht :)

Grüße,
Andreas

Tags: , , , ,