5. Grundlagen von Programmiersprachen - Anweisungen 5.1 Zustandsmodell 5.2 Anweisungen - Zuweisung - Blockstruktur - Fallunterscheidung - Schleifen - Sprunganweisungen Wie gehabt: Folien orientieren sich an denen von K.-P. Löhr, Fehler habe ich zu verantworten. HS Zustandsmodell für imperative Programme z Variablen x1,..xn eines Programms P definieren Zustandsraum z Werte der Variablen zt(xn) definieren einen Zustand zt : Variablen → Werte d. i. eine partielle Abbildung, die die Belegung der Variablen liefert. * z Ausführung von Anweisungen ändert den Zustand "Anweisungen haben einen Effekt" Beachte: Auswertung von Ausdrücken ändert den Zustand nicht * t versteht sich von selbst und wird weggelassen hs / fub - alp2-05 2 Charakterisierung von Zuständen Beispiel Variablen: int x = 3 ; int y = 4; Zustandsraum (x,y) aktueller Zustand (3,4) Einen Zustand kann man durch (im allgemeinen) viele verschiedene Prädikate P charakterisieren. Im Beispiel: P(x,y) ≡ x+y < 10 oder: P'(x,y) ≡ x2+y2 ist Quadratzahl oder: P ≡ x ist prim ∧ x < 5 // über y wird nichts gesagt Forderung "Prädikat P soll gelten" bedeutet: P schränkt die Menge der erlaubten Zustände ein hs / fub - alp2-05 3 Charakterisierung des Effekts von Anweisungen Anweisung s: s: Z → Z mit: Z ist Zustandsmenge des Programms Beispiel: x>3, y==4, s ≡ x = x+1; (*) Charakterisierung des Programmzustands vor und nach der Ausführung der Anweisung s charakterisiert Effekt von s {P} s {Q} Vor Ausführung erfüllt Programmzustand Prädikat P, nach Ausführung Prädikat Q . Im Beispiel? (*) Java bzw. C-Syntax für Zuweisung / Vergleich, nicht mathematisch Schreibweise hs / fub - alp2-05 4 {P} s {Q} Voraussetzung (precondition) Effekt (postcondition) (P,Q) heißt Zusicherung (assertion, eigentlich: Behauptung) Wenn P(z) vor Ausführung von s, dann Q(z'), z' Zustand nach Ausführung von s . Beobachtung (Beispiel): {x == 3 ∧ y==4 } x = x+1 {x==4} ; zu einschränkend x==3 trifft für genau einen Zustand von x zu . y==4 wird nicht benötigt. hs / fub - alp2-05 5 s Z P P' Z P Q P(x) ⇒ P'(x) dann heißt P' schwächer als P Voraussetzungen P: sollen möglichst schwach sein, damit Zustände, in denen s angewendet werden darf, möglichst wenig eingeschränkt sind. Effekte Q: sollen möglichst stark sein, um den neuen Zustand möglichst genau zu charakterisieren. hs / fub - alp2-05 6 Ausdrücke und Prädikate Boole'sche Formeln mit freien Variablen x,y,z…. die für Programmvariablen stehen. z Typprädikate int(x), double(x) und spezielle Prädikate wie prim(x) z quantifizierte Formeln ∀i (F) und ∃i (F), wobei F Formeln sind, in denen i frei vorkommt. i kann, muss nicht Programmvariable sein. Semantik z Werte der freien (Programm-) Variablen einsetzen z (Beachte: erst dadurch wird Ausdruck zum Prädikat!) z Prädikat wie üblich auswerten Beispiele: {x≤0 ∧ z > y} oder {∀i ( 0 ≤ i < n-1 ⇒ a[i] > 0)} abkürzende Schreibweise: {(∀i∈[0..n-1] a[i] > 0)} hs / fub - alp2-05 7 Vereinfachte Notation {x==X ∧ y==Y∧y!=0}z=x/y{x=X ∧ y==Y ∧ z==X/Y} - Großbuchstaben bezeichnen (beliebigen) Wert einer Variablen - was sich nicht ändert muss nicht genannt werden - Typprädikate weglassen - Manchmal sinnvoll: Strichnotation für Wert nach Ausführung der Anweisung {y!=0}x=x/y{x=X ∧ y==Y ∧ x'==x/y} {x==X ∧…} besser, da x initialisiert. Wird oft weggelassen. hs / fub - alp2-05 8 Substitutionen Def: Ist E ein Ausdruck dann bezeichnet Exa den Ausdruck, der aus E durch Substitution aller freien Vorkommen von x durch a hervorgeht. Beispiele: E ≡ x < y ∧ (∀ i∈[0..n-1] a[i] < y) Eyx+y ≡ x < x+y ∧ (∀ i∈[0..n-1] a[i] < x+y) (Eyc*z)z b+u ≡ x < x+c*(b+u) ∧ (∀ i∈[0..n-1] a[i] < x+c*(b+u)) hs / fub - alp2-05 9 Wozu das Ganze?? 1. Laufzeitüberprüfung von Programmzuständen ("hier sollte x > 0 und y gerade sein") Java: assert (x>0) & (y%2 == 0) 2. Spezifikation eines Programms Später 3. Formale Verifikation von Programmen Dazu erforderlich: formale Spezifikation der Semantik von Anweisungen. hs / fub - alp2-05 10 Semantik von Anweisungen Verschiedene Möglichkeiten zur Präzisierung von Anweisung s: (1) (2) (3) Axiomatisch: Charakterisierung durch des Zustands vor und nach Ausführung von s Denotationell: Funktion s. Z → Z charakterisieren z.B. [[i--]] : w → w | iwert(i)-1 Operationell: Beschreibung der ausgeführten Aktionen, meist umgangssprachlich, auch durch ein anderes Maschinenmodell z.B.: i=i+1; erhöhe i um 1 hs / fub - alp2-05 11 5.2 Anweisungen z Einfache (elementare) Anweisungen (simple statements) z z.B. x=y++; Zusammengesetzte Anweisungen (composite statements, control structures) z.B. {i++; k= i*k;} while (i >1) i--; Java: jede elementare Anweisung wird mit ; abgeschlossen, also ";" Teil der Anweisung, im Gegensatz zu Pascal (z.B.) hs / fub - alp2-05 12 Einfachste Anweisung? SimpleStatement: Alternativen untereinander EmptyStatement: EmptyStatement Expressionstatement ; ExpressionStatement: StatementExpression; StatementExpression: AssignmentExpression IncrementExpression Decrementexpression … Warum "expression" und nicht nur "statement" (= Anweisung)? In Java (auch C) haben Anweisungen einen Wert, ..schon bekannt hs / fub - alp2-05 13 Axiomatische Semantik für einfache Anweisungen Leeranweisung {P} ; {P} variable = Expression Zuweisung {PxE} x = E; {P} Ersetze aktuellen Wert von x durch den Wert des Ausdrucks E, Wert der Anweisung ist der von E hs / fub - alp2-05 14 Variablenmodifikation z.B. variable += Expression Anwendung eines dyadischen Operators auf Variable v und Wert des Ausdrucks E ergibt neuen Wert von v: v *= E {Pvv<op>E} v <op>= E ; {P} Auch monadische Inkrement-/ Dekrementoperationen, z.B. ++variable; {Pvv+1} v++ ; {P} hs / fub - alp2-05 15 Verbundanweisung, Block (block) kein ; Folge von Anweisungen und Typvereinbarungen. { [typeDeclaration]0n; statement1 statement2;…} {int i = 1; i = i + myVar; double d = i;} Annahme: myVar ist eine zu int typkompatible Variable, die außerhalb des Blocks deklariert wurde. {P } S1 {Q}, {Q} S2 {R} ___________________ {P} {S1 S2} {R} ..Und analog für mehr als zwei Anweisungen hs / fub - alp2-05 16 Kleiner Exkurs zur Spezifikation der Semantik von Anweisungen {P } S1 {Q}, {Q} S2 {R} ___________________ {P} {S1 S2} {R} ist eine Schlussregel. "Wenn P vor Ausführung von S1 und Q nach Ausführung gilt und Q vor Ausführung von S2 sowie R nach Ausführung von S2, dann darf man ableiten: Wenn P vor Ausführung des Blocks {S1,S2} gilt, dann gilt R danach". (Beachte: die roten Klammern sind Sprachelemente (von Java), die blauen sind metasprachlich. Allgemein: <Prädikat> ________________________ <Semantik einer Anweisung> hs / fub - alp2-05 17 … und warum nicht für z.B.: {PxE} x = E; {P} Die Charakterisierung der Zuweisung gilt immer, unabhängig von anderen irgendwelchen Bedingungen. Genauer wäre: . true ____________ {PxE} x = E; {P} hs / fub - alp2-05 18 Blockstruktur (1) Blöcke können geschachtelt (nested) sein {int i=3; i++;…; {double d; d=i;…;} d*=i; //Syntaxfehler! Undefinierte Variable ...; //"d cannot be resolved! } Namen (Variablen, Konstanten,…), die innerhalb eines Blocks B vereinbart sind, heißen lokal zu dem B, die außerhalb von B vereinbarten heißen nichtlokal. Lokale Namen von B sind außerhalb von B unbekannt. Der Wert einer in B lokalen Variable x ist nach Verlassen von B verloren – x existiert nicht mehr! hs / fub - alp2-05 19 Blockstruktur (2) Gültigkeitbereich (scope): Eine Variable ist in dem Block B gültig, in dem sie definiert wird, ferner von B umfassten Blöcken. {int i=3; i++;…; {double d; d=i;…;} ... } // i ist hier gültig Sichtbarkeitsbereich einer Variable x: in Java identisch mit Gültigkeitsbereich. In anderen Programmiersprachen (z.B. Modula2) können Variablen mit gleichem Namen x in inneren Blöcken definiert werden, die das in einem umschließenden Block definierte x verdecken. hs / fub - alp2-05 20 Sichtbarkeit und Gültigkeit {int i=3; i++;…; for (int i = 1; i<10;i++){ ... } ... } //duplicate local var. Dagegen: for (int i = 1; i<10;i++){ ... } for (int i = 100; i>0, i--) { ... } hs / fub - alp2-05 21 Steuerbefehle (control flow statements) z z z Steuerbefehle haben Einfluß auf die Ausführungsreihenfolge (control flow) von Befehlen Schlechte deutsche Übersetzung: Kontrollfluss … und Kontrollkonstrukte / -befehle. Besser Steuerkonstrukte / -befehle • Fallunterscheidungen (if…else.. • Schleifen (while, for, …) • Sprungbefehle [ Programmbeendigung (halt, exit…)] [ Unterprogrammaufrufe] hs / fub - alp2-05 22 Fallunterscheidungen (1) Bedingte Anweisung if ( BooleanExpr ) statement [else statement]01 Erste Anweisung wird ausgeführt, wenn BooleanExpr Wert true sonst zweite Anweisung. {P∧ C } S1 {Q}, {P ∧ ¬ C} S2 {Q} __________________________ {P} if (C) S1 else S2 {Q} hs / fub - alp2-05 23 if (x> 0) if (x%2 == 0)System.out.print ("gerade"); else System.out.print ("??"); else gehört zum am nächsten stehenden if Besser wäre (wie in Pascal etc.): IF BooleanExpr THEN statementSeq ELSE statementSeq END Abgrenzung der Alternativen und Anweisungsfolgen. hs / fub - alp2-05 24 Veranschaulichung durch Flussdiagramm z z z z Graphische Notation für Programmablauf Wenige visuelle Elemente (Fallunterscheidung, Befehl…) Trägt häufig zur Verwirrung, statt zum Gegenteil bei – deshalb aus der Mode Für kleinere Programmstücke nützlich Fallunterscheidung Bedingung if (Bedingung){ Befehle1 } else { Befehle2 } n j Befehle1 Befehle2 hs / fub - alp2-05 25 Gefährlich: Alternativlose if-Anweisung j B1 n S1 j B2 S2 n Wenn Bi und Bj sich nicht ausschließen, werden Si und Sj ausgeführt! j B3 n if (B1) { S1 }; if (B2) { S2 }; if (B3) { S3 }; S3 if (i%2==0) {S1}; if (i >3) {S2]; hs / fub - alp2-05 26 Paarweise sich ausschließende Alternativen Die erste zutreffende Alternative bestimmt Anweisung: B1 if (B1) { S1 } else if (B2){ S2 } else if (B3) { S3 } else S4 n j B2 n S1 j B2 n S2 j S3 S4 Default wenn kein Bi zutrifft. Guter Programmierstil: Bi ∧ Bj = false für i≠j hs / fub - alp2-05 27 Abschreckendes Beispiel if (i%10 == 1) if (i > 100) {i++;} ; else if (i%10==2 || i%10 == 4) i--; else i = i*2; Ändert sich etwas? if (i%10 == 1) { if (i > 100) {i++;}; else { if (i%10==2 || i%10 == 4) {i--;} else {i = i*2}; Beachte: Layout verändert nicht die Semantik! hs / fub - alp2-05 28 (2) Auswahlanweisung (switch statement) Noch ein abschreckendes Beispiel: if ( monat == 4 ) tage = 30; else if ( monat == 6 ) tage = 30; else if ( monat == 9 ) tage = 30; else if ( monat == 11 ) tage = 30; else if ( monat == 2 ) if (schaltjahr ) tage = 29; else tage = 28; else tage = 31 hs / fub - alp2-05 29 switch( expression ){ case const: statement [case const: statement] 0n [default: statement]01 } (vereinfachte Syntax) Semantik: Vergleiche Wert v von expression nacheinander mit den paarweise verschiedenen case-Marken const bis v=w. Führe alle Anweisungen zu dieser Marke und alle folgenden bis zum Ende des "switch" aus. Wenn v ≠w für alle const, führe die Anweisung hinter default aus. Nebenbedingungen: expression muss den Typ char, byte, short oder int sein, const muss mit diesem Typ ver_ träglich sein. hs / fub - alp2-05 30 Bessere Lösung: switch (monat) { case 4: tage=30; case 6: tage=30; case 9: tage=30; … leider falsch! Beachte: falls case-Marke gefunden, werden alle nachfolgenden Befehle ausgeführt. Hier Ergebnis immer 31 ! case 11:tage=30; case 2: if (schaltjahr) tage=29; else tage=2; default: tage=31; } hs / fub - alp2-05 31 Lösung mit Sprunganweisung: switch (monat) { case 4: tage=30; break; case 6: tage=30; break; case 9: tage=30; break; case 11:tage=30; break: Sprungbefehl, der zur nächsten Anweisung (hier: die switch{…} folgende) springt. break; case 2: if (schaltjahr) tage=29; else tage=2; break; default: tage=31; } ... //Sprungziel hs / fub - alp2-05 32 switch Unterschiede in Programmiersprachen: - Pascal / Modula: nur die Anweisungen der ersten zutreffenden Alternative werden ausgeführt, komplexere Alternativen möglich Case C of // heißt also nicht switch… ’a’,’e’,’i’,’o’,’u’ : WriteLn (’vowel pressed’); ’y’ : WriteLn (’Depends on the language’); else WriteLn (’Consonant pressed’); end; Wie kann man das in Java formulieren? ("fall through") - C# wie Java, aber break; (oder anderer Sprungbefehl) nach einer Alternative wird syntaktisch erzwungen. Kein 'Durchfall' möglich! hs / fub - alp2-05 33 Schleifen (Wiederholungsanweisung, loop) z z z z z Bewirkt wiederholtes Ausführen einer Anweisungsfolge (Iteration) Bekanntlich zentrale Anweisungen der imperativen Programmierung (WHILE-Registermaschine!) Was applikativ durch Rekursion ausgedrückt werden muss, wird imperativ meist iterativ gelöst Rekursion ⇔ Iteration Termination nicht gesichert. Analog zu unendlicher Rekursion nicht abbrechende Schleife Aber: beachte LOOP- versus WHILE-Programme (Register-M) hs / fub - alp2-05 34 Laufanweisung (for-statement, "for-Schleife") for ( [initialization]; [expression]; update) statement System.out.println(" i="+i); initialization: statement, auch mit Variablendeklaration for (int i = 0; ...;...) expression: Boolescher Ausdruck, der die Fortsetzung der Iteration bestimmt – wenn true nächste Iteration for (...; i < 100;...) update: statement, das den Zustand (hoffentlich) so verändert, dass (irgendwann) die Bedingung false wird. for (int i = 0; i < 100; i++) {...} hs / fub - alp2-05 35 Semantik der Laufanweisung formal später , for lässt sich durch while ausdrücken. Beachte: Java-Laufanweisung ist wesentlich stärker als LOOP-Anweisung der LOOP-Maschine. Deshalb: nicht terminierende Iteration möglich! for (int i = 1; i == 100; i +=2) {...} (Noch) offensichtlichere Kuriosität: for (;;); hs / fub - alp2-05 36 Typische Verwendung der Laufanweisung Führe Anweisung für alle Elemente einer Folge oder Menge aus: Pseudocode: FOR ALL (x ∈ X_Set) DO <statementsequence> END int[] a = new ...; ... for (int i = 1; i <= a.length(); i++) {a[i] *=2;} Seit Java 1.5: for (int i : myIntSet) {...} C# : foreach(int x in myIntSet) Später mehr dazu hs / fub - alp2-05 37 while, (abweisende) Schleife while ( expression) Boolesche Bedingung statement Schleifenrumpf 1. Prüfe Bedingung. Wenn falsch weiter mit der while folgenden Anweisung, sonst 2. Führe Schleifenrumpf aus, weiter mit 1 { I ∧ C } S {I} __________________________ {I} while (C) S {I ∧ ¬ C} I heißt Schleifeninvariante hs / fub - alp2-05 38 Beispiel int a,b,akk=0; a=…; b=…; while (b > 0) { if (b%2 == 0)b = b/2; else {b=(b-1)/2; akk=akk+a;} a=a*2; } Ägyptische Multiplikation: Halbieren, verdoppeln, addieren. Leider fehlerhaft…. Ein Programm ist nicht deshalb korrekt, weil es ohne Systemfehler terminiert! Erster Faktor:5 Zweiter Faktor:-4 5*-4=0 hs / fub - alp2-05 39 int a,b,akk=0; a=…; b=…; while (b > 0) { if (b%2 == 0)b = b/2; else {b=(b-1)/2; akk=akk+a;} a=a*2; } { I ∧ C } S {I} __________________________ {I} while (C) S {I ∧ ¬ C} Programm korrekt, wenn der spezifizierte Zustand vor Ausführung der while-Schleife gilt: C ≡ b>0 ∧ I sowie ¬ (b>0) ∧ I nach Verlassen der Schleife. Schleifeninvariante? I ≡ a * b + akk == const hs / fub - alp2-05 40 Damit: for ( I; Expr; U ) Statement Beachte: LOOP von Registermaschinen hat stark eingeschränkte Semantik. äquivalent zu I; while (Bexpr) { Statement U } mit entsprechender Semantik. hs / fub - alp2-05 41 Typische Verwendung der Laufanweisung ("for-Schleife"): (weitgehend) feste Anzahl von Schleifendurchläufen: for(char ch='a'; ch == 'z'; ch++){ System.out.println(ch + ", "+ ch +('A'-'a')); } Achtung: drei Fehler in diesem Codestück (!!) Welche? hs / fub - alp2-05 42 Mehrere Laufvariablen möglich for(int i=0, j=99; j <= i; i++, j--){ System.out.println(i"*"+ j +""="+ i*j); } Geschachtelte Schleifen meist weniger fehleranfällig: for(int i=1, j=1; (i<=9 & j<=9); i++, j++){ System.out.println(i"*"+ j +""="+ i*j); } liefert nicht das Produkt aller Zahlen i,j zwischen 1 und 9, sondern? hs / fub - alp2-05 43 Annehmende Schleife (postchecking loop) do Statement while(Expr) Semantik: wie Statement while (Expr) Statement In manchen Sprachen: repeat statement until expression' mit negierter Semantik der Booleschen Bedingung. hs / fub - alp2-05 44 Sichtbarkeit von Schleifenvariablen for (int i=1; /i<=100; i++){ if ((i*i % 7) ==0) break; } System.out.println("i= "+i); Übersetzungsfehler: Variable undefiniert hs / fub - alp2-05 45 Sprungbefehle (jump statement, goto statement) Fortsetzung der Befehlsausführung an anderer Stelle als beim folgenden Befehl Gilt auch für Schleifen, aber dort Teil der Semantik des Schleifenbefehls "schlechte Sprünge": weiter an beliebiger Stelle im Programm (wie GOTO label in der GOTO-Registermaschine) Gravierendes Problem: Programme werden völlig unübersichtlich ("Spaghetti-Code") hs / fub - alp2-05 46 … 1360 1370 1380 1390 1400 1410 1420 1430 1440 1450 1460 1470 1480 1490 1500 1510 1520 1530 1540 1550 1560 1570 1580 1590 1600 1610 1620 1630 1640 if cox>0 then 1420 if abs(umos(i)/dox)>3E6 then 1390 goto 1150 disp "Spannung zu hoch falls fortgefahren werden soll Cont" cox=cmess(i) pause rserox=rser umosak=umos(i) if val(lcr$[6,14])<.15 then range=range-1 rangestart=range range$="R"&val$(range) output 730;range$ wait 500 trigger 730 enter 730 using 160;lcr$ gosub enter if berum=1 then wait 500 if berum=1 then 1490 cox=cmess mberox=rangestart dox=3.455E-1*f/cox i=0 (Code findet man hier, nach H. Leitner) dox_=dox*1e4 sri=10*dround(dox_,1) if sri<1 then sri=1 u=u-sign*sri if abs(u)>abs(umosak) then 1970 i=i+1 if licht=1 then 1760 .. Schönes Spaghetti-Beispiel: da blickt kein Schwein mehr durch. hs / fub - alp2-05 47 z "Gute Sprünge": systematische Fortsetzung einer abgebrochenen Anweisung (Schleife, Switch) mit nächster Anweisung (z.B.) switch (day) { case 1: print("weekend"); break; case 2:;case 2:; case 3:; case 4:; case 5: print ("weekday"); break; case 6: print ("weekend"); } <nächste Anweisung> hs / fub - alp2-05 48 Sprünge in Java u.a BreakStatement: break [Label] First: for (int x=0; x<100; x++ ) Second: for (int y=0; y <100; y++) for (int z =0; z<100; z++){ …. ; break First; …. } … } … } <nächste Anweisung> Verlässt die Standardausführungsfolge und beendet die Schleife oder den Block; mit Label: beendet den nächsten Block, Schleife, Fallunterscheidung, der/die mit der Marke (label) markiert ist. hs / fub - alp2-05 49 ContiueStatement: continue [Label] First: for (int x=0; x<100; x++ ) Second: for (int y=0; y <100; y++) for (int z =0; z<100; z++){ …. ; continue First; …. } … } … } <nächste Anweisung> Verlässt die Standardausführungsfolge und beginnt nächsten Schleifendurchlauf. mit Label: Beginnt nächsten Schleifendurchlauf der markierten Schleife. hs / fub - alp2-05 50 GotoStatement: goto Label • Nicht in Java (aber goto ist Schlüsselwort) • Aber in C# : goto myLabel; mit Einschränkungen, z.B. nicht in einen Block springen Dafür keine break | continue <label>, wird mit goto realisiert. Sprachentwurf hat offenbar auch mit Geschmack zu tun hs / fub - alp2-05 51 break / continue Beispiele while (true) { int i= kbdInput.readInt("Zahl eingeben, Ende = -1") if (i==-1) {break;}; process(i); // was immer process sein mag... } Sinnvoll: man vermeidet I/O Vorgang vor der Schleife while (condition_1) { if (condition_2) {this_and_that(); continue;}; some_other_code(); } ? Fragwürdig: Bessere Alternative? while (condition_1) { if (condition_2) {this_and_that();} else {some_other_code();} } lesbarer! hs / fub - alp2-05 52 ReturnStatement: return [Expression] Rückkehr aus einem Unterprogramm (einer Methode) <type> myMethod() erfolgt mit return Für alle Rückgabetypen außer dem " leeren" void muss return ein Ausdruck des Typs folgen. Das Programm wird nach der Aufrufstelle fortgesetzt, void main() { ... int i = myMethod(); System.out.println(i); ... } int myMethod() { int j = 10; ... if (..) {return j} else {return 2*j} } hs / fub - alp2-05 53