Übungsblock B (Lösung)

Werbung
TECHNISCHE UNIVERSITÄT MÜNCHEN
FAKULTÄT FÜR INFORMATIK
Studentische Repetitorien für Informatik
Stefan Berktold ([email protected])
WS 2016/17
12.04.2017
Repetitorium zu Einführung in die Informatik 1
Übungsblock B: Syntax
1. KONTEXTFREIE GRAMMATIKEN & REGULÄRE AUSDRÜCKE
Reguläre Ausdrücke (engl. regular expression, kurz: „regex“) werden durch reguläre
Grammatiken – das sind eingeschränkte kontextfreie Grammatiken – erzeugt. Die Einschränkung ist im Besonderen, dass reguläre Ausdrücke eigentlich tatsächlich Ausdrücke sind und somit in einer Zeile, ohne Definition bzw. Verwendung jeglicher
Nichtterminale, geschrieben werden.
Beispiel:
pdigit ::= 1 | ... | 9
digit ::= 0 | pdigit
number ::= -? pdigit digit* | 0
ist eine kontextfreie Grammatik, welche in erweiterter Backus-Naur-Form (EBNF)
angegeben ist und sich als regulärer Ausdruck schreiben lässt:
-? (1 | ... | 9) (0 | 1 | ... | 9)* | 0
Gegenbeispiel:
word ::= (a word b)*
lässt sich nicht ohne Nichtterminale (hier: word) darstellen und ist daher zwar eine
kontextfreie Grammatik, aber kein regulärer Ausdruck.
Relevante Symbole:
a
Terminalzeichen (a)
x | y
Alternative (entweder x oder y)
a | ... | z
Alternativen (eines der Zeichen von a bis z)
x*
Iteration (keinmal, einmal oder beliebig oft)
x+
Iteration (mindestens einmal)
x y
Konkatenation (Zusammenfügen)
x?
Option (keinmal oder einmal)
①
Verwenden Sie für folgende Teilaufgaben abgesehen von letter ::= a|...|z
keine Definition. Nennen Sie einen regulären Ausdruck für Textmuster, ...
a) die weder a noch z enthalten:
(b | ... | y)*
b) die eine gerade Anzahl (möglicherweise auch 0) an Buchstaben enthalten:
(letter letter)*
c) die nicht mit a beginnen und entweder mit b oder mit a enden:
(b | ... | z) letter* (b | a)
|
b
d) die mit a enden, sofern sie auch mit a beginnen:
a letter* a
|
a
|
(b | ... | z) letter*
e) die genau zwei oder genau vier Zeichen lang sind:
letter letter (letter letter)?
f)
die genau zwei a’s enthalten, wobei diese aufeinander folgen:
(b | ... | z)*
aa
(b | ... | z)*
g) die mit beliebig vielen a’s beginnen dürfen, sonst aber nicht zwei oder
mehr aufeinanderfolgende a’s enthalten:
a* ((b | ... | z) a?)*
Übungsblock B (Lösung) – Stefan Berktold
Seite 2 von 13
②
Vermeiden Sie im Folgenden Redundanz und verwenden Sie als Literale/Terminalsymbole keine (zusammengesetzten) Zahlen, sondern Ziffern. Leerzeichen müssen nicht explizit angegeben werden.
Geben Sie eine Grammatik an für ...
a) Datumsangaben, wobei ein Datum aus einer zweistelligen Tag- (01-31) und
einer zweistelligen Monatsangabe (01-12) besteht, jeweils gefolgt von einem Punkt (.) (z. B. 30.03.). Im Anschluss kann noch eine zwei- oder vierstellige Jahresangabe folgen (z. B. 01.01.00 oder 01.12.2017).
pdigit ::= 1 | ... | 9
digit ::= 0 | pdigit
tag ::= 0 pdigit | (1|2) digit | 3 (0|1)
monat ::= 0 pdigit | 1 (0|1|2)
jahr ::= (digit digit)? digit digit
datum ::= tag . monat . jahr?
b) Uhrzeiten im 24-Stunden-Format, wobei eine Uhrzeit aus einer ein- oder
zweistelligen Stundenzahl gefolgt von einem Trennzeichen (Punkt oder
Doppelpunkt) und einer zweistelligen Minutenzahl besteht (z. B. 16:00,
04:59, 0:00 oder 5.43). Optional kann „Uhr“ angehängt werden (z. B. 16:00
Uhr). Alternativ können Uhrzeiten nur mit der Stundenzahl (ohne führende Null) gefolgt von „Uhr“ angegeben werden (z. B. 7 Uhr).
digit ::= 0 | ... | 9
stundeOhneNull ::= digit | 1 digit | 2 (0|...|3)
stunde ::= stundeOhneNull | 0 digit
minute ::= 0 digit | (1|...|5) digit
name ::= U h r
uhrzeit ::= stunde (.|:) minute name? | stundeOhneNull name
Übungsblock B (Lösung) – Stefan Berktold
Seite 3 von 13
2. SYNTAXBÄUME
Mit Syntaxbäumen wird Code syntaktisch in Einheiten gegliedert und so baumartig
dargestellt. Die Grammatik ist vorgegeben und kann beispielsweise der nachfolgenden entsprechen, welche (lange) nicht alle Möglichkeiten abdeckt, aber für MiniJava
genügt. Deklarationen können laut dieser Grammatik bspw. nur ganz am Anfang eines jeden Codes stehen. Außerdem werden keine Auswertungsreihenfolgen (d. h. Bindungsstärken) definiert, was mehrere Darstellungsmöglichkeiten zulässt.
Vorgehen (grobe Erklärung): Das oberste Statement ist immer program – hier starten
wir. Wir betrachten nun den gesamten Quelltext und gliedern ihn gedanklich in Deklarationen und Statements. Jeder Knoten (<Nichtterminal>) wird nun einzeln betrachtet und weiter unterteilt, bis alle Terminalsymbole zugeordnet wurden. Der Code
wird von links nach rechts durchlaufen.
Für nachfolgende Aufgaben gültige MiniJava-Grammatik (in der Klausur geg.):
<program>
::=
<decl>* <stmt>*
<decl>
::=
<type> <name> (, <name>)*;
<stmt>
::=
|
|
|
|
|
|
|
;
{ <stmt>* }
<name> = <expr> ;
<name> = read();
write( <expr> );
if ( <cond> ) <stmt>
if ( <cond> ) <stmt> else <stmt>
while (<cond>) <stmt>
<expr>
::=
|
|
|
|
<number>
<name>
( <expr> )
<unop> <expr>
<expr> <binop> <expr>
<cond>
::=
|
|
|
|
|
true
false
( <cond> )
<expr> <comp> <expr>
<bunop> <cond>
<cond> <bbinop> <cond>
<comp>
<unop>
<binop>
<bbinop>
<bunop>
::=
::=
::=
::=
::=
== | != | <= | < | >= | >
-|+|*|/|%
&& | ||
!
<type>
<name>
<number>
::=
::=
::=
int
letter ( letter | digit )*
digit digit*
Übungsblock B (Lösung) – Stefan Berktold
Seite 4 von 13
②
Zeichnen Sie den Syntaxbaum zu folgenden Codeauszügen. Die Terminalsymbole wurden bereits für Sie eingezeichnet – Sie finden die Vorlagen auf den folgenden Seiten.
a) int a;
a = 0;
while (a < 10)
a = read();
if (a > 100) {
write(10);
}
b) int x, y;
y = 1;
while (y > 0) {
x = read();
y = y - x;
}
write(y);
c) int a, b;
a = 1;
b = -2;
if ((a%b) <= -a || b == 2) {
a = 0;
} else {
if (true)
write(b+1);
};
Übungsblock B (Lösung) – Stefan Berktold
Seite 5 von 13
type
Übungsblock B (Lösung) – Stefan Berktold
name
number
expr comp expr
number
name
cond
stmt
expr
stmt
name
stmt
name
expr
expr
number
comp
cond
stmt
number
expr
stmt
stmt
int a ; a = 0 ; while ( a < 10 ) a = read ( ) ; if ( a > 100 ) { write ( 10 ) ; }
name
decl
program
zu ② a):
Seite 6 von 13
type
Übungsblock B (Lösung) – Stefan Berktold
name
name
number
expr comp expr
number
name
cond
expr
stmt
name
stmt
stmt
name
stmt
expr
name
binop
name
expr
expr
stmt
name
expr
stmt
int x , y ; y = 1 ; while ( y > 0 ) { x = read ( ) ; y = y - x ; } write ( y ) ;
name
decl
program
zu ② b):
Seite 7 von 13
Übungsblock B (Lösung) – Stefan Berktold
name
name
expr
number
name
stmt
expr
name
name
binop
expr
cond
name
unop
expr
expr
name
number
expr
comp
cond
expr
bbinop
cond
comp
expr
expr
number
expr
expr
unop
name
stmt
number
expr
stmt
stmt
name
stmt
cond
stmt
stmt
name
expr
number
expr
binop
expr
stmt
stmt
int a , b ; a = 1 ; b = - 2 ; if ( ( a % b ) <= - a || b == 2 ) { a = 0 ; } else { if ( true ) write ( b + 1 ) ; } ;
type
decl
program
zu ② c):
Seite 8 von 13
3. KONTROLLFLUSSDIAGRAMME
Mit Kontrollflussdiagrammen wird veranschaulicht, wie bzw. in welcher Reihenfolge
einzelne Programmstücke ausgeführt werden. Es kann bspw. nachvollzogen werden,
ob und unter welchen Umständen ein Programm terminiert. Es werden im Grunde
alle Statements (außer Deklarationen!) in einzelnen Knoten gezeichnet, wobei die Einzeichnung von Statements wie break oder continue uneinheitlich gehandhabt wird.
So kann das break-Statement als separater Knoten gezeichnet werden (Empfehlung)
oder lediglich als Beschriftung eine Kante dienen.
Ein Kontrollflussgraph (Synonym zu Kontrollflussdiagramm) zu einem Codeauszug
oder einer bestimmten Methode verfügt über genau einen Startknoten und beliebig
viele Endknoten (keiner, bestenfalls einer oder beliebig viele). Soll eine ganze Methode/Funktion dargestellt werden, so wird entsprechend auch der Startknoten für
Funktionen (siehe unten) und als Endknoten ein (ggf. leeres) return-Statement benutzt. Bei mehreren Funktionen existiert dann pro Funktion ein Startknoten.
Knotenarten:
start
stop
Startknoten
Endknoten
a=read()
yes
a>5
Kantenzusammenlauf
no
a += 1
Ein-/Ausgabe
bedingte Verzweigung
„normale“ Anweisung
f(a,b)
return a
b=g(7)
Startknoten der Funktion f
Endknoten einer Funktion
Funktionsaufruf (g(7))
Weiteres:
Das Semikolon (;) kann ebenfalls eingezeichnet werden. Beim Endknoten genügt eine
einfache Umrandung. Start-/Endknoten auch in Rechteck mit abgerundeten Ecken
möglich. Mehrere „normale“ Anweisungen (bspw. a++ und --b) können zu einem Knoten zusammengefasst werden. Statt „yes“ und „no“ kann auch „true“ und „false“,
„wahr“ und „falsch“, „ja“ und „nein“ o. Ä. geschrieben werden. Wo die Kanten einen
Verzweigungsknoten verlassen oder ggf. wieder erreichen spielt keine Rolle.
Übungsblock B (Lösung) – Stefan Berktold
Seite 9 von 13
③
Geben Sie die Kontrollflussdiagramme zu folgenden Codeauszügen an. Auf den
nächsten Seiten ist Platz für Ihre Ausführungen.
a) int x = 1;
while (x < 5) {
int y = x - 1;
if (y > 3) {
write("x")
} else
break;
x = x * 2;
}
b) int x;
x = read();
while (x < 0 || x > 100) {
write("Wrong Input");
x = read();
}
c) public int f() {
int a = read();
if (a > 5)
a = g(5);
return a;
}
int g(int x) {
if (x <= 5)
return x-1;
return 5;
}
d) for (int i = 1; i < 10; i *= 2) {
if (i % 3 == 0)
write(i);
}
e) public int fun(int x) {
switch(x) {
case 0:
x = 5; break;
case 1: case 3:
x = 2;
case 2:
return 2;
default:
write(x);
}
return 1;
}
Übungsblock B (Lösung) – Stefan Berktold
Seite 10 von 13
zu ③ a):
zu ③ b):
Übungsblock B (Lösung) – Stefan Berktold
Seite 11 von 13
zu ③ c):
zu ③ d):
Übungsblock B (Lösung) – Stefan Berktold
Seite 12 von 13
zu ③ e):
Das switch-Statement ist nicht eindeutig definiert, vermutlich aber nicht klausurrelevant. Man könnte beispielsweise auch „switch(x)“ in einen Knoten packen und
in den bedingten Verzweigungen dann bspw. „Case 0“ schreiben. Das „break“Statement kann auch auf den Pfeil geschrieben werden, wie es bei dem „default“Schlüsselwort gemacht wurde. Wichtig ist es, dass break-Statements beachtet werden (vgl. Unterschied Case 0 und Case 3).
Übungsblock B (Lösung) – Stefan Berktold
Seite 13 von 13
Herunterladen