Prinzipien von Programmiersprachen Vorlesung
Wintersemester 2007, 08, 09, 10, 11, 12
Johannes Waldmann, HTWK Leipzig
28. Januar 2013
Beispiel: mehrsprachige Projekte
ein typisches Projekt besteht aus:
I Datenbank: SQL
I Verarbeitung: Java
I Oberfl ¨ache: HTML
I Client-Code: Java-Script
und das ist noch nicht die ganze Wahrheit:
nenne weitere Sprachen, die ¨ublicherweise in einem solchen Projekt vorkommen
Sprache
I wird benutzt, um Ideen festzuhalten/zu transportieren (Wort, Satz, Text, Kontext)
I wird beschrieben durch
I Lexik
I Syntax
I Semantik
I Pragmatik
I nat ¨urliche Sprachen / formale Sprachen
Konzepte
I Hierarchien (baumartige Strukturen)
I zusammengesetzte (arithmetische, logische) Ausdr ¨ucke
I zusammengesetzte Anweisungen (Bl ¨ocke)
I Klassen, Module
I Typen beschreiben Daten
I Namen stehen f ¨ur Werte, Wiederverwendung
I Flexibilit ¨at durch Parameter (Unterprogramme, Polymorphie)
Paradigmen
I imperativ
Programm ist Folge von Befehlen (=Zust ¨ands ¨anderungen)
I deklarativ (Programm ist Spezifikation)
I funktional (Gleichungssystem)
I logisch (logische Formel ¨uber Termen)
I Constraint (log. F. ¨uber anderen Bereichen)
I objektorientiert (klassen- oder prototyp-basiert)
I nebenl ¨aufig (nichtdeterministisch, explizite Prozesse)
I (hoch) parallel (deterministisch, implizit)
Ziele der LV
Arbeitsweise: Methoden, Konzepte, Paradigmen
I isoliert beschreiben
I an Beispielen in (bekannten und unbekannten) Sprachen wiedererkennen
Ziel:
I verbessert die Organisation des vorhandenen Wissens
I gestattet die Beurteilung und das Erlernen neuer Sprachen
I hilft bei Entwurf eigener (anwendungsspezifischer) Sprachen
Beziehungen zu anderen LV
I Grundlagen der Informatik, der Programmierung:
strukturierte (imperative) Programmierung
I Softwaretechnik 1/2:
objektorientierte Modellierung und Programmierung, funktionale Programmierung und OO-Entwurfsmuster
I Compilerbau: Implementierung von Syntax und Semantik Sprachen f ¨ur bestimmte Anwendungen, mit bestimmten Paradigmen:
I Datenbanken, Computergrafik, k ¨unstliche Intelligenz, Web-Programmierung, parallele/nebenl ¨aufige
Programmierung
Organisation
I Vorlesung Mo (u+g) 15:30 Li 211
I Ubungen (alle in Z423)¨
I Di (u+g) 9:30 (MIM)
I Do(u) 11:15+Do(g) 9:30 (INM)
I Di(u) 13:45+Di(g) 11:15
Ubungsgruppe w ¨ahlen:¨ https://autotool.imn.
htwk-leipzig.de/cgi-bin/Super.cgi
I Pr ¨ufungszulassung: regelm ¨aßiges und erfolgreiches Bearbeiten von ¨Ubungsaufgaben
I Klausur: 120 min, ohne Hilfsmittel
Literatur
I http://www.imn.htwk-leipzig.de/˜waldmann/
edu/ws12/pps/folien/pps/
I Robert W. Sebesta: Concepts of Programming Languages, Addison-Wesley 2004, . . .
Zum Vergleich/als Hintergrund:
I Abelson, Sussman, Sussman: Structure and Interpretation of Computer Programs, MIT Press 1984
http://mitpress.mit.edu/sicp/
I Turbak, Gifford: Design Concepts of Programming Languages, MIT Press 2008
http://mitpress.mit.edu/catalog/item/
default.asp?ttype=2&tid=11656
Inhalt
(nach Sebesta: Concepts of Programming Languages)
I Methoden: (3) Beschreibung von Syntax und Semantik
I Konzepte:
I (5) Namen, Bindungen, Sichtbarkeiten
I (6) Typen von Daten, Typen von Bezeichnern
I (7) Ausdr ¨ucke und Zuweisungen, (8) Anweisungen und Ablaufsteuerung, (9) Unterprogramme
I Paradigmen:
I (12) Objektorientierung ( (11) Abstrakte Datentypen )
I (15) Funktionale Programmierung
Ubungen ¨
1. Anwendungsgebiete von Programmiersprachen, wesentliche Vertreter
zu Skriptsprachen: finde die Anzahl der"*.java"-Dateien unter$HOME/workspace, die den BezeichnerString enthalten. (Benutze eine Pipe aus drei Unix-Kommandos.) L ¨osungen:
find workspace/ -name "*.java" | xargs grep -l String | wc -l find workspace/ -name "*.java" -exec grep -l String {} \; | wc -l 2. Maschinenmodelle (Bsp: Register, Turing, Stack, Funktion)
funktionales Programmieren in Haskell (http://www.haskell.org/) ghci
:set +t
length $ takeWhile (== ’0’) $ reverse $ show $ product [ 1 .. 100 ] Kellermaschine in PostScript.
42 42 scale 7 9 translate .07 setlinewidth .5 setgray/c{arc clip fill setgray}def 1 0 0 42 1 0 c 0 1 1{0 3 3 90 270 arc 0 0 6 0 -3 3 90 270 arcn 270 90 c -2 2 4{-6 moveto 0 12 rlineto}for -5 2 5{-3 exch moveto 9 0 rlineto}for stroke 0 0 3 1 1 0 c 180 rotate initclip}for showpage Mitgvoderkghostviewansehen (Options: watch file). Mit
Editor Quelltext ¨andern. Finden Sie den Autor dieses Programms!
(L ¨osung: John Tromp, siehe auch
http://www.iwriteiam.nl/SigProgPS.html) 3.http://99-bottles-of-beer.net/(top rated . . . )
Ubung: Beispiele f ¨ur ¨ ¨ Ubersetzer
Java:
javac Foo.java # erzeugt Bytecode (Foo.class) java Foo # f¨uhrt Bytecode aus (JVM) Einzelheiten der ¨Ubersetzung:
javap -c Foo # druckt Bytecode C:
gcc -c bar.c # erzeugt Objekt(Maschinen)code (bar.o) gcc -o bar bar.o # linkt (l¨adt) Objektcode (Resultat: bar) ./bar # f¨uhrt gelinktes Programm aus
Einzelheiten:
gcc -S bar.c # erzeugt Assemblercode (bar.s) Aufgaben:
I geschachtelte arithmetische Ausdr ¨ucke in Java und C:
vergleiche Bytecode mit Assemblercode
I vergleiche Assemblercode f ¨ur Intel und Sparc (einloggen auf kain, dann gcc wie oben)
gcc f ¨ur Java (gcj):
gcj -c Foo.java # erzeugt Objektcode
gcj -o Foo Foo.o --main=Foo # linken, wie oben
I Assemblercode ansehen, vergleichen
gcj -S Foo.java # erzeugt Assemblercode (Foo.s)
I Kompatibilit ¨at des Bytecodes ausprobieren zwischen Sun-Java und GCJ (beide Richtungen)
gcj -C Foo.java # erzeugt Class-File (Foo.class)
Programme als B ¨aume
I ein Programmtext repr ¨asentiert eine Hierarchie (einen Baum) von Teilprogrammen
I Die Semantik des Programmes wird durch Induktion ¨uber diesen Baum definiert.
I In den Knoten des Baums stehen Token,
I jedes Token hat einen Typ und einen Inhalt (eine Zeichenkette).
I diese Prinzip kommt aus der Mathematik (arithmetische Ausdr ¨ucke, logische Formeln)
Token-Typen
Token-Typen sind ¨ublicherweise
I reservierte W ¨orter (if, while, class, . . . )
I Bezeichner (foo, bar, . . . )
I Literale f ¨ur ganze Zahlen, Gleitkommazahlen, Strings, Zeichen
I Trennzeichen (Komma, Semikolon)
I Klammern (runde: paren(these)s, eckige: brackets, geschweifte: braces) (jeweils auf und zu)
I Operatoren (=, +, &&, . . . )
alle Token eines Typs bilden eineformale Sprache
Formale Sprachen
I einAlphabetist eine Menge von Zeichen,
I einWortist eine Folge von Zeichen,
I eineformale Spracheist eine Menge von W ¨ortern.
Beispiele:
I AlphabetΣ ={a,b},
I Wortw=ababaaab,
I SpracheL=Menge aller W ¨orter ¨uberΣgerader L ¨ange.
I Sprache (Menge) aller Gleitkomma-Konstanten in C.
Spezifikation formaler Sprachen
man kann eine formale Sprache beschreiben durch:
I algebraisch(Sprach-Operationen) Bsp: regul ¨are Ausdr ¨ucke
I Generieren(Grammatik), Bsp: kontextfreie Grammatik,
I Akzeptanz(Automat), Bsp: Kellerautomat,
I logisch(Eigenschaften),
w| ∀p,r :
(p<r ∧w[p] =a∧w[r] =c)
⇒ ∃q : (p<q∧q<r ∧w[q] =b)
Sprach-Operationen
Aus SprachenL1,L2konstruiere:
I Mengenoperationen
I VereinigungL1∪L2,
I DurchschnittL1∩L2, DifferenzL1\L2;
I VerkettungL1·L2 = {w1·w2|w1∈L1,w2∈L2}
I Stern (iterierte Verkettung)L∗1 = S
k≥0Lk1
Def:Spracheregul ¨ar:⇐⇒ kann durch diese Operationen aus endlichen Sprachen konstruiert werden.
Satz:Durchschnitt und Differenz braucht man dabei nicht.
Regul ¨are Sprachen/Ausdr ¨ucke
Die MengeE(Σ)derregul ¨aren Ausdr ¨ucke
¨uber einem Alphabet (Buchstabenmenge)Σ ist die kleinste MengeE, f ¨ur die gilt:
I f ¨ur jeden Buchstabenx ∈Σ :x ∈E (autotool: Ziffern oder Kleinbuchstaben)
I das leere Wort∈E (autotool:Eps)
I die leere Menge∅ ∈E (autotool:Empty)
I wennA,B∈E, dann
I (Verkettung)A·B∈E(autotool:*oder weglassen)
I (Vereinigung)A+B∈E(autotool:+)
I (Stern, H ¨ulle)A∗ ∈E(autotool:ˆ*)
Jeder solche Ausdruck beschreibt eineregul ¨are Sprache.
Beispiele/Aufgaben zu regul ¨aren Ausdr ¨ucken
Wir fixieren das AlphabetΣ ={a,b}.
I alle W ¨orter, die mitabeginnen und mitbenden:aΣ∗b.
I alle W ¨orter, die wenigstens dreiaenthaltenΣ∗aΣ∗aΣ∗aΣ∗
I alle W ¨orter mit gerade vielenaund beliebig vielenb?
I Alle W ¨orter, die einaaoder einbbenthalten:
Σ∗(aa∪bb)Σ∗
I (Wie lautet das Komplement dieser Sprache?)
Bemerkung zu Reg. Ausdr.
Wie beweist manw ∈L(X)?
(Wortw geh ¨ort zur Sprache eines regul ¨aren AusdrucksX)
I wennX =X1+X2:
beweisew ∈L(X1)oderbeweisew ∈L(X2)
I wennX =X1·X2:
zerlege w =w1·w2undbeweisew1∈L(X1)undbeweise w2∈L(X2).
I wennX =X1∗:
w ¨ahleeinen Exponentenk ∈Nundbeweisew ∈L(X1k) (nach vorigem Schema)
Beispiel:w =abba,X = (ab∗)∗.
w=abb·a=ab2·ab0∈ab∗·ab∗ ⊆(ab∗)2⊆(ab∗)∗.
Ubungen Reg. Ausdr. ¨
I (Σ∗,·, )ist Monoid
I . . . aber keine Gruppe, weil man im Allgemeinen nicht dividieren kann. Welche Relation ergibt sich als
”Teilbarkeit“:u|w :=∃v :u·v =w
I Zeichne Hasse-Diagramme der Teilbarkeitsrelation
I auf nat ¨urlichen Zahlen{0,1, . . . ,10},
I auf W ¨ortern{a,b}≤2
I (Pow(Σ∗),∪,·, . . . , . . .)ist Halbring.
Beispiel f ¨ur Distributivgesetz?
Welches sind jeweils die neutralen Elemente der Operationen?
(vgl. oben) Welche Relation auf Sprachen (Mengen) ergibt sich als
”Teilbarkeit“ bzgl.∪?
I Damitab+c=ab·acimmer gilt, muß mana0wie definieren?
I Block-Kommentare und weitere autotool-Aufgaben
I regul ¨are Ausdr ¨ucke f ¨ur Tokenklassen in der Standard-Pascal-Definition
http://www.standardpascal.org/iso7185.
html#6.1Lexicaltokens
Welche Notation wird f ¨ur unsere Operatoren+und Stern benutzt? Was bedeuten die eckigen Klammern?
Wort-Ersetzungs-Systeme
Berechnungs-Modell (Markov-Algorithmen)
I Zustand (Speicherinhalt): Zeichenfolge (Wort)
I Schritt: Ersetzung eines Teilwortes RegelmengeR⊆Σ∗×Σ∗
Regel-Anwendung:
u→R v ⇐⇒ ∃x,z ∈Σ∗,(l,r)∈R:u=x·l·z∧x·r ·z =v. Beispiel: Bubble-Sort:{ba→ab,ca→ac,cb→bc}
Beispiel: Potenzieren:ab→bba
Aufgaben: gibt es unendlich lange Rechnungen f ¨ur:
R1={1000→0001110},R2={aabb→bbbaaa}?
Grammatiken
Grammatik Gbesteht aus:
I Terminal-AlphabetΣ ( ¨ublich: Kleinbuchst., Ziffern)
I Variablen-AlphabetV ( ¨ublich: Großbuchstaben)
I StartsymbolS∈V
I Regelmenge
(Wort-Ersetzungs-System) R⊆(Σ∪V)∗×(Σ∪V)∗
Grammatik { terminale
= mkSet "abc"
, variablen
= mkSet "SA"
, start = ’S’
, regeln = mkSet [ ("S", "abc") , ("ab", "aabbA") , ("Ab", "bA") , ("Ac", "cc") ]
}
vonGerzeugte Sprache:L(G) ={w |S →∗R w∧w ∈Σ∗}.
Formale Sprachen: Chomsky-Hierarchie
I (Typ 0) aufz ¨ahlbare Sprachen (beliebige Grammatiken, Turingmaschinen)
I (Typ 1) kontextsensitive Sprachen (monotone Grammatiken, linear beschr ¨ankte Automaten)
I (Typ 2) kontextfreie Sprachen (kontextfreie Grammatiken, Kellerautomaten)
I (Typ 3) regul ¨are Sprachen (rechtslineare Grammatiken, regul ¨are Ausdr ¨ucke, endliche Automaten)
Tokenklassen sind meist regul ¨are Sprachen.
Programmiersprachen werden kontextfrei beschrieben (mit Zusatzbedingungen).
Typ-3-Grammatiken
(=rechtslineare Grammatiken) jede Regel hat die Form
I Variable→Terminal Variable
I Variable→Terminal
I Variable→
(vgl. lineares Gleichungssystem) Beispiele
I G1= ({a,b},{S,T},S,{S→,S→aT,T →bS})
I G2= ({a,b},{S,T},S,{S→,S→aS,S →bT,T → aT,T →bS})
S ¨atze ¨uber regul ¨are Sprachen
F ¨ur jede SpracheLsind die folgenden Aussagen ¨aquivalent:
I es gibt einen regul ¨aren AusdruckX mitL=L(X),
I es gibt eine Typ-3-GrammatikGmitL=L(G),
I es gibt einen endlichen AutomatenAmitL=L(A).
Beweispl ¨ane:
I Grammatik↔Automat (Variable=Zustand)
I Ausdruck→Automat (Teilbaum=Zustand)
I Automat→Ausdruck (dynamische Programmierung) LA(p,q,r) =alle Pfade vonpnachr ¨uber Zust ¨ande≤q.
Kontextfreie Sprachen
Def (Wdhlg):Gist kontextfrei (Typ-2), falls
∀(l,r)∈R(G) :l ∈V.
geeignet zur Beschreibung von Sprachen mit hierarchischer Struktur.
Anweisung -> Bezeichner = Ausdruck
| if Ausdruck then Anweisung else Anweisung Ausdruck -> Bezeichner | Literal
| Ausdruck Operator Ausdruck Bsp: korrekt geklammerte Ausdr ¨ucke:
G= ({a,b},{S},S,{S→aSbS,S→}).
Bsp: Palindrome:
G= ({a,b},{S},S,{S→aSa,S →bSb,S →).
Bsp: alle W ¨orterw ¨uberΣ ={a,b}mit|w|a=|w|b
Klammer-Sprachen
Abstraktion von vollst ¨andig geklammerten Ausdr ¨ucke mit zweistelligen Operatoren
(4*(5+6)-(7+8))⇒(()())⇒aababb H ¨ohendifferenz:h:{a,b}∗ →Z:w7→ |w|a− |w|b Pr ¨afix-Relation:u≤w :⇐⇒ ∃v :u·v =w
Dyck-Sprache:D={w |h(w) =0∧ ∀u ≤w :h(u)≥0}
CF-Grammatik:G= ({a,b},{S},S,{S→,S→aSbS}) Satz:L(G) =D. Beweis (Plan):
L(G)⊆DInduktion ¨uber L ¨ange der Ableitung D⊆L(G)Induktion ¨uber Wortl ¨ange
Ubungen ¨
I Beispiele Wort-Ersetzung (ab→baa, usw.)
I Dyck-Sprache: BeweisL(G)⊆D
(Induktionsbehauptung? Induktionsschritt?)
I Dyck-Sprache: BeweisD⊆L(G)
I CF-Grammatik f ¨ur{w |w ∈ {a,b}∗,|w|a=|w|b}
I CF-Grammatik f ¨ur{w |w ∈ {a,b}∗,2· |w|a=|w|b}
(erweiterte) Backus-Naur-Form
I Noam Chomsky: Struktur nat ¨urlicher Sprachen (1956)
I John Backus, Peter Naur: Definition der Syntax von Algol (1958)
Backus-Naur-Form (BNF)≈kontextfreie Grammatik
<assignment> -> <variable> = <expression>
<number> -> <digit> <number> | <digit>
Erweiterte BNF
I Wiederholungen (Stern, Plus)<digit>ˆ+
I Auslassungen
if <expr> then <stmt> [ else <stmt> ] kann in BNF ¨ubersetzt werden
Ableitungsb ¨aume f ¨ur CF-Sprachen
Def: ein geordneter BaumT mit Markierung m:T →Σ∪ {} ∪V ist Ableitungsbaum f ¨ur eine CF-GrammatikG, wenn:
I f ¨ur jeden inneren Knotenk vonT giltm(k)∈V
I f ¨ur jedes BlattbvonT giltm(b)∈Σ∪ {}
I f ¨ur die Wurzelw vonT giltm(w) =S(G)(Startsymbol)
I f ¨ur jeden inneren Knotenk vonT mit Kindernk1,k2, . . . ,kn gilt(m(k),m(k1)m(k2). . .m(kn))∈R(G)(d. h. jedes m(ki)∈V∪Σ)
I f ¨ur jeden inneren Knotenk vonT mit einzigem Kindk1= gilt(m(k), )∈R(G).
Ableitungsb ¨aume (II)
Def: derRandeines geordneten, markierten Baumes(T,m)ist die Folge aller Blatt-Markierungen (von links nach rechts).
Beachte: die Blatt-Markierungen sind∈ {} ∪Σ, d. h.
Terminalw ¨orter der L ¨ange 0 oder 1.
F ¨ur Bl ¨atter: rand(b) =m(b), f ¨ur innere Knoten:
rand(k) =rand(k1)rand(k2). . .rand(kn)
Satz:w ∈L(G) ⇐⇒ existiert Ableitungsbaum(T,m)f ¨urGmit rand(T,m) =w.
Eindeutigkeit
Def:Gheißteindeutig, falls∀w ∈L(G)genau ein Ableitungsbaum(T,m)existiert.
Bsp: ist{S→aSb|SS|}eindeutig?
(beachte: mehrere AbleitungenS →∗R wsind erlaubt, und wg.
Kontextfreiheit auch gar nicht zu vermeiden.) Die naheliegende Grammatik f ¨ur arith. Ausdr.
expr -> number | expr + expr | expr * expr ist mehrdeutig (auszweiGr ¨unden!)
Auswege:
I Transformation zu eindeutiger Grammatik (benutzt zus ¨atzliche Variablen)
I Operator-Assoziativit ¨aten und -Pr ¨azedenzen
Assoziativit ¨at
I Definition: Operation istassoziativ
I Bsp: Plus ist nicht assoziativ (f ¨ur Gleitkommazahlen) ( ¨U)
I f ¨ur nicht assoziativen Operatormuß man festlegen, wasxy z bedeuten soll:
(3+2) +4=? 3+2+4=? 3+ (2+4) (3−2)−4=? 3−2−4=? 3−(2−4) (3∗ ∗2)∗ ∗4=? 3∗ ∗2∗ ∗4=? 3∗ ∗(2∗ ∗4)
I . . . und dann die Grammatik entsprechend einrichten
Assoziativit ¨at (II)
X1 + X2 + X3auffassen als(X1 + X2) + X3 Grammatik-Regeln
Ausdruck -> Zahl | Ausdruck + Ausdruck ersetzen durch
Ausdruck -> Summe
Summe -> Summand | Summe + Summand Summand -> Zahl
Pr ¨azedenzen
(3+2)∗4=? 3+2∗4=? 3+ (2∗4) Grammatik-Regel
summand -> zahl erweitern zu
summand -> zahl | produkt produkt -> ...
(Assoziativit ¨at beachten)
Zusammenfassung Operator/Grammatik
Ziele:
I Klammern einsparen
I trotzdem eindeutig bestimmter Syntaxbaum Festlegung:
I Assoziativit ¨at:
bei Kombination eines Operators mit sich
I Pr ¨azedenz:
bei Kombination verschiedener Operatoren Realisierung in CFG:
I Links/Rechts-Assoziativit ¨at⇒Links/Rechts-Rekursion
I verschiedene Pr ¨azedenzen⇒verschiedene Variablen
Ubung Operator/Grammatik ¨
Ubung:¨
I Verh ¨altnis von plus zu minus, mal zu durch?
I Klammern?
I un ¨are Operatoren (Pr ¨afix/Postfix)?
Statische und dynamische Semantik
Semantik=Bedeutung
I statisch (kann zur ¨Ubersetzungszeit gepr ¨uft werden) Beispiele:
I Typ-Korrektheit von Ausdr ¨ucken,
I Bedeutung (Bindung) von Bezeichnern Hilfsmittel: Attributgrammatiken
I dynamisch (beschreibt Ausf ¨uhrung des Programms) operational, axiomatisch, denotational
Attributgrammatiken (I)
I Attribut: Annotation an Knoten des Syntaxbaums.
A:Knotenmenge→Attributwerte (Bsp:N)
I Attributgrammatik besteht aus:
I kontextfreier GrammatikG(Bsp:{S→e|mSS})
I f ¨ur jeden Knotentyp (Terminal+Regel)
eine Menge (Relation) von erlaubten Attribut-Tupeln (A(X0),A(X1), . . . ,A(Xn))
f ¨ur KnotenX0mit Kindern[X1, . . . ,Xn]
S→mSS,A(X0) +A(X3) =A(X2);
S→e,A(X0) =A(X1);
Terminale:A(e) =1,A(m) =0
Attributgrammatiken (II)
ein Ableitungsbaum mit Annotationen ist
korrekt bez ¨uglich einer Attributgrammatik, wenn
I zur zugrundeliegenden CF-Grammatik paßt
I in jedem Knoten das Attribut-Tupel (von Knoten und Kindern) zur erlaubten Tupelmenge geh ¨ort
Plan:
I Baum beschreibt Syntax, Attribute beschreiben Semantik Ursprung: Donald Knuth: Semantics of Context-Free
Languages, (Math. Systems Theory 2, 1968)
technische Schwierigkeit: Attributwerte effizient bestimmen.
(beachte: (zirkul ¨are) Abh ¨angigkeiten)
Donald E. Knuth
I The Art Of Computer Programming (1968, . . . ) (Band 3: Sortieren und Suchen)
I TEX, Metafont, Literate Programming (1983, . . . ) (Leslie Lamport: LATEX)
I Attribut-Grammatiken
I die Bezeichnung
”NP-vollst ¨andig“
I . . .
http://www-cs-faculty.stanford.edu/˜uno/
Arten von Attributen
I synthetisiert:
h ¨angt nur von Attributwerten in Kindknoten ab
I ererbt (inherited)
h ¨angt nur von Attributwerten in Elternknoten und (linken) Geschwisterknoten ab
Wenn Abh ¨angigkeiten bekannt sind, kann man Attributwerte durch Werkzeuge bestimmen lassen.
Attributgrammatiken–Beispiele
I Auswertung arithmetischer Ausdr ¨ucke (dynamisch)
I Bestimmung des abstrakten Syntaxbaumes
I Typpr ¨ufung (statisch)
I Kompilation (f ¨ur Kellermaschine) (statisch)
Konkrete und abstrakte Syntax
I konkreter Syntaxbaum=der Ableitungsbaum
I abstrakter Syntaxbaum=wesentliche Teile des konkreten Baumes
unwesentlich sind z. B. die Knoten, die zu Hilfsvariablen der Grammatik geh ¨oren.
abstrakter Syntaxbaum kann als synthetisiertes Attribut konstruiert werden.
E -> E + P ; E.abs = new Plus(E.abs, P.abs) E -> P ; E.abs = P.abs
Regeln zur Typpr ¨ufung
. . . bei geschachtelten Funktionsaufrufen
I Funktionf hat TypA→B
I AusdruckX hat TypA
I dann hat Ausdruckf(X)den TypB Beispiel
String x = "foo"; String y = "bar";
Boolean.toString (x.length() < y.length()));
(Curry-Howard-Isomorphie)
Ubung Attributgrammatiken/SableCC ¨
I SableCC:http://sablecc.org/
SableCC is a parser generator for building compilers, interpreters . . . , strictly-typed abstract syntax trees and tree walkers
I Syntax einer Regel
linke-seite { -> attribut-typ }
= { zweig-name } rechte-seite { -> attribut-wert }
I Quelltexte:http://www.imn.htwk-leipzig.de/
˜waldmann/edu/ws12/pps/code/rechner/
Benutzung:make ; make test ; make clean
I Aufgaben:
Subtraktion, Multiplikation, Klammern, Potenzen
Kommentar: in Java fehlen: algebraische Datentypen, Pattern Matching, Funktionen h ¨oherer Ordnung. Deswegen muß SableCC das simulieren — das sieht nicht sch ¨on aus. Die
”richtige“ L ¨osung sehen Sie sp ¨ater im Compilerbau.
Abstrakter Syntaxbaum, Interpreter:
http://www.imn.htwk-leipzig.de/˜waldmann/edu/
ws11/cb/folien/main/node12.html, Kombinator-Parser:
http://www.imn.htwk-leipzig.de/˜waldmann/edu/
ws11/cb/folien/main/node70.html
Ausdr ¨ucke → Kellermaschine
Beispiel:
3∗x+1⇒push 3, push x, mal, push 1, plus
I Code f ¨ur Konstante/Variablec :push c;
I Code f ¨ur Ausdruck x op y:code(x); code(y); op;
I Ausf ¨uhrung eines Operators:
holt beide Argumente vom Stack, schiebt Resultat auf Stack
Der erzeugte Code ist synthetisiertes Attribut!
Beispiele: Java-Bytecode (javac, javap), CIL (gmcs, monodis)
Ubungen (Stackmaschine) ¨
Schreiben Sie eine Java-Methode, deren Kompilation genau diesen Bytecode erzeugt: a)
public static int h(int, int);
Code:
0: iconst_3 1: iload_0 2: iadd 3: iload_1 4: iconst_4 5: isub 6: imul 7: ireturn b)
public static int g(int, int);
Code:
0: iload_0 1: istore_2 2: iload_1
3: ifle 17
6: iload_2 7: iload_0 8: imul 9: istore_2 10: iload_1 11: iconst_1 12: isub 13: istore_1
14: goto 2
17: iload_2 18: ireturn
Dynamische Semantik
I operational:
beschreibt Wirkung von Anweisungen durch ¨Anderung des Programmzustandes
I denotational:
ordnet jedem (Teil-)Programm einen Wert zu, Bsp: eine Funktion (h ¨oherer Ordnung).
Beweis von Programmeigenschaften durch Term-Umformungen
I axiomatisch (Bsp: Hoare-Kalk ¨ul):
enth ¨alt Schlußregeln, um Aussagen ¨uber Programme zu beweisen
Bsp: Operationale Semantik
Schleife while (B) A
wird ¨ubersetzt in Sprungbefehle if (B) ...
(vervollst ¨andige!)
Aufgabe: ¨ubersetzefor(A; B; C) Din while!
Denotationale Semantik
Beispiele
I jedes (nebenwirkungsfreie)Unterprogrammist eine Funktion von Argument nach Resultat
I jedeAnweisungist eine Funktion von Speicherzustand nach Speicherzustand
Vorteile denotationaler Semantik:
I Bedeutung eines Programmes=mathematisches Objekt
I durch Term beschreiben, durch ¨aquivalente Umformungen verarbeiten (equational reasoning)
Vorteil deklarativer Programierung:
ProgrammierspracheistBeschreibungssprache
Beispiele Denotationale Semantik
I jeder arithmetische Ausdruck (aus Konstanten und Operatoren)
beschreibt eine Zahl
I jeder aussagenlogische Ausdruck (aus Variablen und Operatoren)
beschreibt eine Funktion (von Variablenbelegung nach Wahrheitswert)
I jeder regul ¨are Ausdruck
beschreibt eine formale Sprache
Beispiel: Semantik von Unterprogr.
Unterprogramme definiert durch Gleichungssysteme. Sind diese immer eindeutig l ¨osbar?
Geben Sie geschlossenen arithmetischen Ausdruck f ¨ur:
f (x) = if x > 52 then x - 11
else f (f (x + 12)) t (x, y, z) =
if x <= y then z + 1 else t ( t (x-1, y, z)
, t (y-1, z, x) , t (z-1, x, y) )
Axiomatische Semantik
Notation f ¨ur Aussagen ¨uber Programmzust ¨ande:
{ V } A { N }
I f ¨ur jeden Zustands, in dem VorbedingungV gilt:
I wenn Anweisung A ausgef ¨uhrt wird,
I und Zustandterreicht wird, dann gilt dort Nachbedingung N
Beispiel:
{ x >= 5 } y := x + 3 { y >= 7 } G ¨ultigkeit solcher Aussagen kann man
I beweisen (mit Hoare-Kalk ¨ul)
I pr ¨ufen (testen)
Eiffel
Bertrand Meyer,http://www.eiffel.com/
class Stack [G] feature count : INTEGER
item : G is require not empty do ... end empty : BOOLEAN is do .. end
full : BOOLEAN is do .. end put (x: G) is
require not full do ...
ensure not empty item = x
count = old count + 1
Beispiel sinngem ¨aß aus: B. Meyer: Object Oriented Software Construction, Prentice Hall 1997
Hoare-Kalk ¨ul
Kalk ¨ul: f ¨ur jede Anweisung ein Axiom, das die schw ¨achste Vorbedingung (weakest precondition) beschreibt.
Beispiele
I { N[x/E] } x := E { N }
I { V und B } C { N } und { V und not B } D { N }
=> { V } if (B) then C else D { N }
I Schleife . . . ben ¨otigt Invariante
Axiom f ¨ur Schleifen
wenn { I and B } A { I },
dann { I } while (B) do A { I and not B } Beispiel:
Eingabe int p, q;
// p = P und q = Q int c = 0;
// inv: p * q + c = P * Q while (q > 0) {
???
}
// c = P * Q
Moral: erst Schleifeninvariante (Spezifikation), dann Implementierung.
Ubungen (Invarianten) ¨
Erg ¨anze das Programm:
Eingabe: nat¨urliche Zahlen a, b;
// a = A und b = B int p = 1; int c = ???;
// Invariante: cˆb * p = AˆB while (b > 0) {
???
b = abrunden (b/2);
}
Ausgabe: p; // p = AˆB
Warum Typen?
I Typ ist Menge von Werten mit Operationen
I f ¨ur jede eigene Menge von Werten (Variablen) aus dem Anwendungsbereichbenutze eine eigenen Typ
I halte verschiedene Typen sauber getrennt, mit Hilfe der Programmiersprache
I der Typ einer Variablen/Funktion ist ihre beste Dokumentation
Historische Entwicklung
I keine Typen (alles ist int)
I vorgegebene Typen (Fortran: Integer, Real, Arrays)
I benutzerdefinierte Typen (algebraische Datentypen;
Spezialf ¨alle: enum, struct, class)
I abstrakte Datentypen (interface)
Uberblick ¨
I einfache (primitive) Typen
I Zahlen, Wahrheitswerte, Zeichen
I benutzerdefinierte Aufz ¨ahlungstypen
I Teilbereiche
I zusammengesetzte (strukturierte) Typen
I Produkt (records)
I Summe (unions)
I rekursive Typen
I Potenz (Funktionen: Arrays, (Tree/Hash-)Maps, Unterprogramme)
I Verweistypen (Zeiger)
Aufz ¨ahlungstypen
k ¨onnen einer Teilmenge ganzer Zahlen zugeordnet werden
I vorgegeben: int, char, boolean
I nutzerdefiniert (enum) typedef enum {
Mon, Tue, Wed, Thu, Fri, Sat, Sun } day;
Designfragen:
I automatisch nachintumgewandelt?
I automatisch vonintumgewandelt?
I eine Konstante in mehreren Aufz ¨ahlungen m ¨oglich?
Keine Aufz ¨ahlungstypen
das ist nett gemeint, aber vergeblich:
#define Mon 0
#define Tue 1 ...
#define Sun 6 typedef int day;
int main () { day x = Sat;
day y = x * x;
}
Aufz ¨ahlungstypen in C
im wesentlichen genauso nutzlos:
typedef enum {
Mon, Tue, Wed, Thu, Fri, Sat, Sun } day;
int main () { day x = Sat;
day y = x * x;
}
Ubung: was ist in C++ besser?¨
Aufz ¨ahlungstypen in Java
enum Day {
Mon, Tue, Wed, Thu, Fri, Sat, Sun;
public static void main (String [] argv) { for (Day d : Day.values ()) {
System.out.println (d);
} } }
verh ¨alt sich wie Klasse
(genauer: Schnittstelle mit 7 Implementierungen) siehe ¨Ubung (jetzt oder bei Objekten)
Teilbereichstypen in Ada
with Ada.Text_Io;
procedure Day is
type Day is ( Mon, Tue, Thu, Fri, Sat, Sun );
subtype Weekday is Day range Mon .. Fri;
X, Y : Day;
begin
X := Fri; Ada.Text_Io.Put (Day’Image(X));
Y := Day’Succ(X); Ada.Text_Io.Put (Day’Image(Y));
end Day;
mit Bereichspr ¨ufung bei jeder Zuweisung.
einige Tests k ¨onnen aber vom Compiler statisch ausgef ¨uhrt werden!
Abgeleitete Typen in Ada
procedure Fruit is subtype Natural is
Integer range 0 .. Integer’Last;
type Apples is new Natural;
type Oranges is new Natural;
A : Apples; O : Oranges; I : Integer;
begin -- nicht alles korrekt:
A := 4; O := A + 1; I := A * A;
end Fruit;
Natural, ¨Apfel und Orangen sind isomorph, aber nicht zuweisungskompatibel.
Sonderfall: Zahlenkonstanten geh ¨oren zu jedem abgeleiteten Typ.
Zusammengesetzte Typen
Typ=Menge, Zusammensetzung=Mengenoperation:
I Produkt (record, struct)
I Summe (union, case class)
I Rekursion
I Potenz (Funktion)
Produkttypen (Records)
R=A×B×C
Kreuzprodukt mit benannten Komponenten:
typedef struct { A foo;
B bar;
C baz;
} R;
R x; ... B x.bar; ...
erstmalig in COBOL (≤1960)
Ubung: Record-Konstruktion (in C, C++)?¨
Summen-Typen
R=A∪B∪C
disjunkte (diskriminierte) Vereinigung (Pascal) type tag = ( eins, zwei, drei );
type R = record case t : tag of eins : ( a_value : A );
zwei : ( b_value : B );
drei : ( c_value : C );
end record;
nicht diskriminiert (C):
typedef union {
A a_value; B b_value; C c_value;
}
Vereinigung mittels Interfaces
Irepr ¨asentiert die Vereinigung vonAundB:
interface I { }
class A implements I { int foo; } class B implements I { String bar; }
Notation daf ¨ur in Scala (http://scala-lang.org/) abstract class I
case class A (foo : Int) extends I case class B (bar : String) extends I Verarbeitung durchPattern matching
def g (x : I): Int = x match { case A(f) => f + 1
case B(b) => b.length() }
Maßeinheiten in F#
physikalische Gr ¨oße=Maßzahl×Einheit.
viele teure Softwarefehler durch Ignorieren der Einheiten.
in F# (Syme, 200?), aufbauend auf ML (Milner, 197?) [<Measure>] type kg ;;
let x = 1<kg> ;;
x * x ;;
[<Measure>] type s ;;
let y = 2<s> ;;
x * y ;;
x + y ;;
http://msdn.microsoft.com/en-us/library/
dd233243.aspx
Rekursiv definierte Typen
Haskell (http://haskell.org/) data Tree a = Leaf a
| Branch ( Tree a ) ( Tree a ) data List a = Nil | Cons a ( List a ) Java
interface Tree<A> { }
class Leaf<A> implements Tree<A> { A key } class Branch<A> implements Tree<A>
{ Tree<A> left, Tree<A> right } das ist einalgebraischer Datentyp,
die Konstruktoren (Leaf, Nil) bilden die Signatur der Algebra, die Elemente der Algebra sind Terme (B ¨aume)
Potenz-Typen
BA:={f :A→B}(Menge aller Funktionen vonAnachB) ist sinnvolle Notation, denn|B||A|=
BA spezielle Realisierungen:
I Funktionen (Unterprogramme)
I Wertetabellen (Funktion mit endlichem Definitionsbereich) (Assoziative Felder, Hashmaps)
I Felder (Definitionsbereich ist Aufz ¨ahlungstyp) (Arrays)
I Zeichenketten (Strings)
die unterschiedliche Notation daf ¨ur (Beispiele?) ist bedauerlich.
Felder (Arrays)
Design-Entscheidungen:
I welche Index-Typen erlaubt? (Zahlen? Aufz ¨ahlungen?)
I Bereichspr ¨ufungen bei Indizierungen?
I Index-Bereiche statisch oder dynamisch?
I Allokation statisch oder dynamisch?
I Initialisierung?
I mehrdimensionale Felder gemischt oder rechteckig?
Felder in C
int main () {
int a [10][10];
a[3][2] = 8;
printf ("%d\n", a[2][12]);
}
statische Dimensionierung, dynamische Allokation, keine Bereichspr ¨ufungen.
Form: rechteckig, Adress-Rechnung:
int [M][N];
a[x][y] ==> *(&a + (N*x + y))
Felder in Java
int [][] feld =
{ {1,2,3}, {3,4}, {5}, {} };
for (int [] line : feld) { for (int item : line) {
System.out.print (item + " ");
}
System.out.println ();
}
dynamische Dimensionierung und Allokation, Bereichspr ¨ufungen. Nicht notwendig rechteckig.
Felder in C#
Unterschiede zwischen
I int [][] a
I int [,] a in
I Benutzung (Zugriff)
I Initialisierung durch Array-Literal
Nicht rechteckige Felder in C?
Das geht:
int a [] = {1,2,3};
int b [] = {4,5};
int c [] = {6};
e = {a,b,c};
printf ("%d\n", e[1][1]);
aber welches ist dann der Typ vone?
(es ist nichtint e [][].)
Dynamische Feldgr ¨oßen
Designfrage: kann ein Feld (auch: String) seine Gr ¨oße ¨andern?
(C: wird sowieso nicht gepr ¨uft, Java: nein, Perl: ja)
in Java: wenn man das will, dann will man statt Array eine LinkedList, statt String einen StringBuffer.
wenn man mit Strings arbeitet, dann ist es meist ein Fehler:
benutze StringszwischenProgrammen, aber niemalsinnerhalbeines Programms.
ein einem Programm: benutze immer anwendungsspezifische Datentypen.
. . . deren externe Syntax spiel ¨uberhaupt keine Rolle
Kosten der Bereichs ¨uberpr ¨ufungen
es wird oft als Argument f ¨ur C (und gegen Java) angef ¨uhrt, daß die erzwungene Bereichs ¨uberpr ¨ufung bei jedem Array-Zugriff so teuer sei.
sowas sollte man erst glauben, wenn man es selbst gemessen hat.
modernen Java-Compiler sindsehr cleverund k ¨onnen theorem-prove away (most) subscript range checks das kann man auch in der Assembler-Ausgabe des JIT-Compilers sehen.
Verweistypen
I TypT, Typ der Verweise aufT.
I Operationen: new, put, get, delete
I ¨ahnlich zu Arrays (das Array ist der Hauptspeicher) explizite Verweise in C, Pascal
implizite Verweise:
I Java: alle nicht primitiven Typen sind Verweistypen, De-Referenzierung ist implizit
I C#: class ist Verweistyp, struct ist Werttyp
Verweis- und Wertsemantik in C#
I f ¨ur Objekte, deren Typclass ...ist:
Verweis-Semantik (wie in Java)
I f ¨ur Objekte, deren Typstruct ...ist:
Wert-Semantik Testfall:
class s {public int foo; public string bar;}
s x = new s(); x.foo = 3; x.bar = "bar";
s y = x; y.bar = "foo";
Console.WriteLine (x.bar);
und dannclassdurchstructersetzen
Algebraische Datentypen in Pascal, C
Rekursion unter Verwendung von Verweistypen Pascal:
type Tree = ˆ Node ;
type Tag = ( Leaf, Branch );
type Node = record case t : Tag of Leaf : ( key : T ) ;
Branch : ( left : Tree ; right : Tree );
end record;
C: ¨ahnlich, benutze typedef
Ubung Typen ¨
I Teilbereichstypen und abgeleitete Typen in Ada (Vergleich mit dimensionierten Typen in F#)
I Arrays in C (Assemblercode anschauen)
I rechteckige und geschachtelte Arrays in C#
I Wert/Verweis (struct/class) in C#
Variablen
vereinfacht: Variable bezeichnet eine (logische) Speicherzelle genauer: Variable besitzt Attribute
I Name
I Adresse
I Wert
I Typ
I Lebensdauer
I Sichtbarkeitsbereich
Bindungen dieser Attributestatischoderdynamisch
Namen in der Mathematik
I ein Name bezeichnet einen unver ¨anderlichen Wert e=X
n≥0
1
n!, sin= (x 7→X
n≥0
(−1)n x2n+1 (2n+1)!)
I auchnundx sind dabei lokale Konstanten (werden aber gern”Variablen“ genannt)
I auch die
”Variablen“ in Gleichungssystemen sind (unbekannte) Konstanten{x+y =1∧2x+y =1}
in der Programmierung:
I Variable ist Name f ¨ur Speicherstelle (=konstanter Zeiger)
I implizite Dereferenzierung beim Lesen und Schreiben
I Konstante: Zeiger auf schreibgesch ¨utzte Speicherstelle
Namen
I welche Buchstaben/Zeichen sind erlaubt?
I reservierte Bezeichner?
I Groß/Kleinschreibung?
I Konvention:long_nameoderlongName(camel-case) (Fortran:long name)
im Zweifelsfall: Konvention der Umgebung einhalten
I Konvention: Typ im Namen (schlecht, weil so Implementierungsdetails verraten werden) schlecht:myStack = ...
besser:Stack<Ding> rest_of_input = ...
Typen f ¨ur Variablen
I dynamisch (Wert hat Typ)
I statisch (Name hat Typ)
I deklariert (durch Programmierer)
I inferiert (durch ¨Ubersetzer) z. B.varin C#3
Vor/Nachteile: Lesbarkeit, Sicherheit, Kosten
Dynamisch getypte Sprachen
Daten sind typisiert, Namen sind nicht typisiert.
LISP, Clojure, PHP, Python, Perl, Javascript, . . .
<html><body><script type="text/javascript">
var bar = true;
var foo =
bar ? [1,2] : function(x) {return 3*x;};
document.write (foo[0]);
</script></body></html>
Statisch getypte Sprachen
Daten sind typisiert, Namen sind typisiert
I Programmierer muß Typen von Namen deklarieren:
C, Java, . . .
I Compiler inferiert Typen von Namen:
ML, F#, Haskell, C# (var)
Typinferenz in C#
public class infer {
public static void Main (string [] argv) { var arg = argv[0];
var len = arg.Length;
System.Console.WriteLine (len);
} }
Beachte: dasvarin C# ist nicht dasvaraus Javascript.
Typdeklarationen
im einfachsten Fall (Java, C#):
Typname Variablenname [ = Initialisierung ] ; int [] a = { 1, 2, 3 };
Func<double,double> f = (x => sin(x));
gern auch komplizierter (C): dort gibt es keine Syntax f ¨ur Typen, sondern nur f ¨ur Deklarationen von Namen.
double f (double x) { return sin(x); } int * p;
double ( * a [2]) (double) ; Beachte:*und[]werden
”von außen nach innen “ angewendet
U: Syntaxb ¨aume zeichnen,¨ abenutzen
Konstanten
=Variablen, an die genau einmal zugewiesen wird
I C: const (ist Attribut f ¨ur Typ)
I Java: final (ist Attribut f ¨ur Variable) Vorsicht:
class C { int foo; }
static void g (final C x) { x.foo ++; }
Merksatz: alle Deklarationen so lokal und so konstant wie m ¨oglich!
(D. h. Attributeimmutableusw.)
Lebensort und -Dauer von Variablen
I statisch (global, aber auch lokal:) int f (int x) {
static int y = 3; y++; return x+y;
}
I dynamisch
I Stack{ int x = ... }
I Heap
I explizit (new/delete, malloc/free)
I implizit
Sichtbarkeit von Namen
=Bereich der Anweisungen/Deklarationen, in denen ein Name benutzt werden kann.
I global
I lokal: Block (und Unterbl ¨ocke)
Ublich ist: Sichtbarkeit beginnt¨ nachDeklaration und endet am Ende des umgebenden Blockes
Uberdeckungen ¨
Namen sind auch in inneren Bl ¨ocken sichtbar:
int x;
while (..) { int y;
... x + y ...
}
innere Deklarationen verdecken ¨außere:
int x;
while (..) { int x;
... x ...
}
Sichtbarkeit und Lebensdauer
. . . stimmen nicht immer ¨uberein:
I static-Variablen in C-Funktionen
sichtbar: in Funktion, Leben: Programm
I lokale Variablen in Unterprogrammen
sichtbar: innere Bl ¨ocke, Leben: bis Ende Unterpr.
Einleitung
I Ausdruck hatWert(Zahl, Objekt, . . . ) (Ausdruck wirdausgewertet)
I Anweisung hatWirkung( ¨Anderung des Programm/Welt-Zustandes)
(Anweisung wirdausgef ¨uhrt) Vgl. Trennung (in Pascal, Ada)
I Funktion (Aufruf ist Ausdruck)
I Prozedur (Aufruf ist Anweisung)
Einleitung (II)
I in allen imperativen Sprachen gibt es Ausdr ¨ucke mit Nebenwirkungen
(n ¨amlich Unterprogramm-Aufrufe)
I in den rein funktionalen Sprachen gibt es keine (Neben-)Wirkungen, also keine Anweisungen (sondern nur Ausdr ¨ucke).
I in den C- ¨ahnlichen Sprachen ist=ein Operator, (d. h. die Zuweisung ist syntaktisch ein Ausdruck)
Designfragen f ¨ur Ausdr ¨ucke
I Pr ¨azedenzen (Vorrang)
I Assoziativit ¨aten (Gruppierung)
I Ausdr ¨ucke d ¨urfen (Neben-)Wirkungen haben?
I in welcher Reihenfolge treten die auf?
I welche impliziten Typumwandlungen?
I explizite Typumwandlungen (cast)?
I kann Programmierer Operatoren definieren? ¨uberladen?
Syntax von Ausdr ¨ucken
I einfache Ausdr ¨ucke : Konstante, Variable
I zusammengesetzte Ausdr ¨ucke:
I Operator-Symbol zwischen Argumenten
I Funktions-Symbol vor Argument-Tupel wichtige Spezialf ¨alle f ¨ur Operatoren:
I arithmetische, relationale, boolesche Wdhlg: Syntaxbaum, Pr ¨azedenz, Assoziativit ¨at.
Syntax von Konstanten
Was druckt diese Anweisung?
System.out.println ( 12345 + 5432l );
dieses und einige der folgenden Beispiele aus: Joshua Bloch, Neil Gafter:Java Puzzlers, Addison-Wesley, 2005.
Der Plus-Operator in Java
. . . addiert Zahlen und verkettet Strings.
System.out.println ("foo" + 3 + 4);
System.out.println (3 + 4 + "bar");
Uberladene Operatornamen ¨
aus praktischen Gr ¨unden sind arithmetische und relationale Operatornamen ¨uberladen
(d. h.: ein Name f ¨ur mehrere Bedeutungen)
Uberladung wird aufgel ¨ost durch die Typen der Argumente.¨ int x = 3; int y = 4; ... x + y ...
double a; double b; ... a + b ...
String p; String q; ... p + q ...
Automatische Typanpassungen
in vielen Sprachen postuliert man eine Hierarchie von Zahlbereichstypen:
byte⊆int⊆float⊆double
im allgemeinen ist das eine Halbordnung.
Operator mit Argumenten verschiedener Typen:
(x :: int) + (y :: float)
beide Argumente werden zu kleinstem gemeinsamen Obertyp promoviert, falls dieser eindeutig ist (sonst statischer Typfehler) (Halbordnung→Halbverband)
Implizite/Explizite Typumwandlungen
Was druckt dieses Programm?
long x = 1000 * 1000 * 1000 * 1000;
long y = 1000 * 1000;
System.out.println ( x / y );
Was druckt dieses Programm?
System.out.println ((int) (char) (byte) -1);
Moral: wenn man nicht auf den ersten Blick sieht, was ein Programm macht, dann macht es wahrscheinlich nicht das, was man will.
Explizite Typumwandlungen
sieht gleich aus und heißt gleich (cast), hat aber verschiedene Bedeutungen:
I Datum soll in anderen Typ gewandelt werden, Repr ¨asentation ¨andert sich:
double x = (double) 2 / (double) 3;
I Programmierer weiß es besser (als der Compiler), Repr ¨asentation ¨andert sich nicht:
List books;
Book b = (Book) books.get (7);
. . . kommt nur vor, wenn man die falsche
Programmiersprache benutzt (n ¨amlich Java vor 1.5)
Der Verzweigungs-Operator
Absicht: statt
if ( 0 == x % 2 ) { x = x / 2;
} else {
x = 3 * x + 1;
}
lieber
x = if ( 0 == x % 2 ) { x / 2
} else { 3 * x + 1 } ;
historische Notation daf ¨ur
x = ( 0 == x % 2 ) ? x / 2 : 3 * x + 1;
?/: isttern ¨arerOperator
Verzweigungs-Operator(II)
(... ? ... : ... )in C, C++, Java Anwendung im Ziel einer Zuweisung (C++):
int main () {
int a = 4; int b = 5; int c = 6;
( c < 7 ? a : b ) = 8;
}
Relationale Operatoren
kleiner, gr ¨oßer, gleich,. . .
Was tut dieses Programm (C? Java?)
int a = -4; int b = -3; int c = -2;
if (a < b < c) {
printf ("aufsteigend");
}
Logische (Boolesche) Ausdr ¨ucke
I und&&,||oder, nicht!, gleich, ungleich, kleiner, . . .
I nicht verwechseln mit Bit-Operationen&,| (in C gef ¨ahrlich, in Java ungef ¨ahrlich—warum?)
I verk ¨urzte Auswertung?
int [] a = ...; int k = ...;
if ( k >= 0 && a[k] > 7 ) { ... } ( ¨U: wie sieht das in Ada aus?)
Noch mehr Quizfragen
I System.out.println ("H" + "a");
System.out.println (’H’ + ’a’);
I char x = ’X’; int i = 0;
System.out.print (true ? x : 0);
System.out.print (false ? i : x);
Erkl ¨aren durch Verweis auf Java Language Spec.
Der Zuweisungs-Operator
Syntax:
I Algol, Pascal: Zuweisung:=, Vergleich=
I Fortran, C, Java: Zuweisung=, Vergleich==
Semantik der Zuweisunga = b:
Ausdr ¨ucke links und rechts werden verschieden behandelt:
I bestimme Adresse (lvalue)pvona
I bestimme Wert (rvalue)v vonb
I schreibev aufp
Weitere Formen der Zuweisung
(in C- ¨ahnlichen Sprachen)
I verk ¨urzte Zuweisung:a += b
entsprechend f ¨ur andere bin ¨are Operatoren
I lvaluepvonawird bestimmt (nur einmal)
I rvaluev vonbwird bestimmt
I Wert auf Adresse§pwird umv erh ¨oht
I Inkrement/Dekrement
I Pr ¨afix-Version++i, --j: Wert ist der ge ¨anderte
I Suffix-Versioni++, j--: Wert ist der vorherige
Ausdr ¨ucke mit Nebenwirkungen
(side effect; falsche ¨Ubersetzung: Seiteneffekt)
in C- ¨ahnlichen Sprachen: Zuweisungs-Operatoren bilden Ausdr ¨ucke, d. h. Zuweisungen sind Ausdr ¨ucke und k ¨onnen als Teile von Ausdr ¨ucken vorkommen.
Wert einer Zuweisung ist der zugewiesene Wert
int a; int b; a = b = 5; // wie geklammert?
Komma-Operator zur Verkettung von Ausdr ¨ucken (mit Nebenwirkungen)
for (... ; ... ; i++,j--) { ... }
Auswertungsreihenfolgen
Kritisch: wenn Wert des Ausdrucks von Auswertungsreihenfolge abh ¨angt:
int a; int b = (a = 5) + (a = 6);
int d = 3; int e = (d++) - (++d);
I keine Nebenwirkungen: egal
I mit Nebenwirkungen:
I C, C++: Reihenfolge nicht spezifiziert, wenn Wert davon abh ¨angt, dann ist Verhaltennicht definiert
I Java, C#: Reihenfolge genau spezifiziert (siehe JLS)
Auswertungsreihenfolge in C
Sprachstandard (C99, C++) benutzt Begriffsequence point (Meilenstein):
bei Komma, Fragezeichen,&&und||
die Nebenwirkungen zwischen Meilensteinen m ¨ussen unabh ¨angig sein (nicht die gleiche Speicherstelle betreffen), ansonsten ist das Verhalten undefiniert (d.h., der Compiler darf machen, was er will)
int x = 3; int y = ++x + ++x + ++x;
vgl. Aussagen zu sequence points in http://gcc.gnu.org/readings.html
Gurevich, Huggins: Semantics of C,http://citeseerx.
ist.psu.edu/viewdoc/summary?doi=10.1.1.29.6755
Definition
Semantik: Anweisung hatWirkung(Zustands ¨anderung), die bei Ausf ¨uhrung eintritt.
abstrakte Syntax:
I einfache Anweisung:
I Zuweisung
I Unterprogramm-Aufruf
I zusammengesetzte Anweisung:
I Nacheinanderausf ¨uhrung (Block)
I Verzweigung (zweifach: if, mehrfach: switch)
I Wiederholung (Sprung, Schleife)
Programm-Ablauf-Steuerung
Ausf ¨uhren eines Programms im von-Neumann-Modell:
Was? (Operation) Womit? (Operanden) Wohin? (Resultat) Wie weiter? (n ¨achste Anweisung)
strukturierte Programmierung:
I Nacheinander
I außer der Reihe (Sprung, Unterprogramm, Exception)
I Verzweigung
I Wiederholung
engl.control flow, falsche ¨Ubersetzung: Kontrollfluß;
to control=steuern,to check=kontrollieren/pr ¨ufen
Bl ¨ocke
Folge von (Deklarationen und) Anweisungen Designfrage: Bl ¨ocke
I explizit (Klammern, begin/end)
I implizit (if . . . then . . . end if) Designfrage: Deklarationen gestattet
I am Beginn des (Unter-)Programms (Pascal)
I am Beginn des Blocks (C)
I an jeder Stelle des Blocks (C++, Java)
Verzweigungen (zweifach)
in den meisten Sprachen:
if Bedingung then Anweisung1 [ else Anweisung2 ] Designfragen:
I was ist als Bedingung gestattet (gibt es einen Typ f ¨ur Wahrheitswerte?)
I dangling else
I gel ¨ost durch Festlegung (else geh ¨ort zu letztem if)
I vermieden durch Block-Bildung (Perl, Ada)
I tritt nicht auf, weil man else nie weglassen darf (vgl. ?/:) (Haskell)
Mehrfach-Verzweigung
switch (e) { case c1 : s1 ; case c2 : s2 ; [ default : sn; ] }
Designfragen:
I welche Typen f ¨ure?
I welche Werte f ¨urci?
I Wertebereiche?
I was passiert, wenn mehrere F ¨alle zutreffen?
I was passiert, wenn kein Fall zutrifft (default?)
I (effiziente Kompilation?)
Switch/break
das macht eben in C, C++, Java nicht das, was man denkt:
switch (index) { case 1 : odd ++;
case 2 : even ++;
default :
printf ("wrong index %d\n", index);
}
C#: jeder Fallmußmit break (oder goto) enden.
Kompilation
ein switch (mit vielen cases) wird ¨ubersetzt in:
I (naiv) eine lineare Folge von bin ¨aren Verzweigungen (if, elsif)
I (semi-clever) einen balancierter Baum von bin ¨aren Verzweigungen
I (clever) eine Sprungtabelle Ubung:¨
I einen langen Switch (1000 F ¨alle) erzeugen (durch ein Programm!)
I Assembler/Bytecode anschauen
Pattern Matching
I Fallunterscheidung nach dem Konstruktor
I Bindung von lokalen Namen
abstract class Term // Scala case class Constant (value : Int)
extends Term
case class Plus (left: Term, right : Term) extends Term
def eval(t: Term): Int = { t match {
case Constant(v) => v
case Plus(l, r) => eval(l) + eval(r) } }
Wiederholungen
I Maschine, Assembler: (un-)bedingter Sprung
I strukturiert: Schleifen Designfragen f ¨ur Schleifen:
I wie wird Schleife gesteuert? (Bedingung, Z ¨ahler, Daten, Zustand)
I an welcher Stelle in der Schleife findet Steuerung statt (Anfang, Ende, dazwischen, evtl. mehreres)
Schleifen steuern durch. . .
I Z ¨ahler
for p in 1 .. 10 loop .. end loop;
I Daten
map (\x -> x*x) [1,2,3] ==> [1,4,9]
Collection<String> c
= new LinkedList<String> ();
for (String s : c) { ... }
I Bedingung
while ( x > 0 ) { if ( ... ) { x = ... } ... }
I Zustand (Iterator, hasNext, next)
Z ¨ahlschleifen
Idee: vor Beginn steht Anzahl der Durchl ¨aufe fest.
richtig realisiert ist das nur in Ada:
for p in 1 .. 10 loop ... end loop;
I Z ¨ahlerpwird implizit deklariert
I Z ¨ahler ist im Schleifenk ¨orper konstant Vergleiche (beide Punkte) mit Java, C++, C