Grundlagen der Informatik I “Programmierung”
Grundlagen der Informatik I “Programmierung”
Grundlagen der Informatik I “Programmierung”
Sie wollen auch ein ePaper? Erhöhen Sie die Reichweite Ihrer Titel.
YUMPU macht aus Druck-PDFs automatisch weboptimierte ePaper, die Google liebt.
• Die Variante einer rekursiven Routine muß ausdrücken, daß die Routine nur endlich oft aufgerufen<br />
werden kann. Wie bei den Schleifen benötigt man hierzu einen Integer-Ausdruck, <strong>der</strong> stets größer gleich<br />
Null ist und bei jedem rekursiven Aufruf mindestens um eins kleiner wird. An<strong>der</strong>s als bei <strong>der</strong> Schleife<br />
muß diese aber durch einen Ausdruck beschrieben werden, <strong>der</strong> nichts über die inneren Eigenschaften <strong>der</strong><br />
Routine sagt, son<strong>der</strong>n nur von den äußeren Merkmalen abhängt – also den formalen Parametern, dem<br />
untersuchten Objekt und ggf. dem Resultat. Wir müssen die Variante also im Extremfall durch einen<br />
Integer-Ausdruck Var(yr,actual) beschreiben, um alle möglichen Abhängigkeiten zu berücksichtigen.<br />
In unserem Beispiel fak berechnung ist die Variante ein sehr einfacher Ausdruck, nämlich <strong>der</strong> Parameter<br />
n selbst. Aufgrund <strong>der</strong> Vorbedingung n>0 ist die Variante stets größer gleich Null. Bei jedem rekursiven<br />
Aufruf wird sie geringer, da fak berechnung(n-1) <strong>der</strong> einzige rekursive Aufruf ist. In den meisten<br />
rekursiven Routinen ist die Variante zum Glück ähnlich einfach und offensichtlich wie hier.<br />
Eine Komplikation, welche das Aufstellen einer formalen Verifikationsregel erheblich erschwert, ist die Tatsache,<br />
daß eine Routine beliebig viele rekursive Aufrufe haben darf und diese beliebig tief in an<strong>der</strong>en Programmkonstrukten<br />
eingebaut sein dürfen. Da Rekursion aber als eine Art Gegenstück zu Schleifen anzusehen<br />
ist, macht es wenig Sinn, rekursive Aufrufe innerhalb einer Schleife zu plazieren. Auch würde ein Korrektheitsbeweis<br />
dadurch nahezu unmöglich. Daher darf man davon ausgehen, daß rekursive Aufrufe – wie in unserem<br />
Beispiel – nur innerhalb von bedingten Anweisungen o<strong>der</strong> Fallunterscheidungen vorkommen. Somit kann man<br />
zu jedem rekursiven Aufruf eine Bedingung angeben, die besagt, wann dieser Aufruf überhaupt ausgeführt<br />
werden soll. Die Existenz einer solchen Bedingung ist wichtig für einen Terminierungsbeweis, da ohne diese<br />
Bedingung die Routine sich unendlich oft aufrufen würde.<br />
Die in Abbildung 4.15 angegebene Verifikations-“regel” für rekursive Routinen beschreibt eine Beweismethodik,<br />
welche das oben Gesagte etwas stärker präzisiert und sich an <strong>der</strong> Schleifenregel (Abbildung 4.11)<br />
orientiert.<br />
Es sei r eine Routine mit formalen Argumenten yr und abstrakten Vor- und Nachbedingungen<br />
prer bzw. postr. Der Anweisungsteil Br enthalte insgesamt m rekursive Aufrufe <strong>der</strong> Form<br />
entityi.r(Ai(yr)) , wobei jeweils entityi ein Objekt und Ai(yr) Ausdrücke für aktuelle Argumente<br />
beschreibt. Bedi(yr) beschreibe die Bedingung für den i-ten rekursiven Aufruf.<br />
Die Routine r ist genau dann total korrekt, wenn gilt<br />
Partielle Korrektheit: Unter <strong>der</strong> Annahme, daß alle rekursiven Aufrufe die Spezifikation von r<br />
erfüllen, kann mithilfe <strong>der</strong> Verifikationsregeln 38 gezeigt werden<br />
{ prer(yr)} Br { postr(yr)}<br />
Terminierung: Es gibt einen Integer-Ausdruck Var, <strong>der</strong> nur von den Argumenten yr und dem<br />
aktuellen Objekt abhängt und für den gezeigt werden kann<br />
prer(yr) ⇒ Var(yr,Current)≥0<br />
und für jeden rekursiven Aufruf entityi.r(Ai(yr))<br />
prer(yr) ∧ Bedi(yr) ⇒ prer(Ai(yr)) ∧ Var(Ai(yr),entityi)