Programmiersprachen Konzepte und Realisationen

Größe: px
Ab Seite anzeigen:

Download "Programmiersprachen Konzepte und Realisationen"

Transkript

1 Programmiersprachen Konzepte und Realisationen Version vom 29. Januar 2008 Thomas Letschert Fachhochschule Gießen Friedberg

2 Inhaltsverzeichnis 1 Sprachkonzepte und Implementierungen Syntax Syntax und Syntaxbeschreibungen Lexikalische Analyse Implementierung eines Scanners Syntaxtische Analyse Implementierung eines Parsers Traversierung Abstrakter Syntaxbäume Namensbindung und Umgebungen Namen und Bindungen Umgebungsmodell und Laufzeitorganisation Umgebungsmodelle von Python, Scheme und Java Typen Typen und Typ Bindungen Polymorphe Typsysteme Covarianz und Contravarianz Multimethoden Typkonvertierungen Genrizität und parametrischer Polymorphismus Parametrischer Polymorphismus Parametrischer Polymorphismus in C Generizität in Java Funktionale Programme Grundlagen Definitionen und Ausdrücke Funktionen und ihre Definition Auswertung von Funktionen Funktionale Programmierung Algorithmen und Algorithmenschemata Funktionale Programmierung in OO Sprachen

3 2 Thomas Letschert FH Giessen Friedberg 2.3 Funktionale Programmiertechniken Datenabstraktion Map und Reduce Induktion Rekursion Fortsetzungsfunktionen Selbstbezug, Selbstreproduktion und Selbstbetrachtung Iteratoren und Generatoren Iteratoren Beispiel: Baumdurchlauf Generatoren Implementierung von Generator Relationale Programme Suche, Nichtdeterminismus, Relationen Nichtdeterministische Programme Mustererkennung Logik Programme Prolog Unifikation Prolog Interpreter

4 Kapitel 1 Sprachkonzepte und Implementierungen 3

5 4 Thomas Letschert FH Giessen Friedberg 1.1 Syntax Syntax und Syntaxbeschreibungen Syntax und Semantik Programmiersprachen sind durch ihre Syntax und Semantik charakterisiert. Die Syntax einer (Programmier ) Sprache ist die Beschreibung aller Zeichenfolgen die als Programme zulässig sind. Die Semantik gibt an, was diese Programme zu bedeuten haben. Die beiden Begriffe kommen aus der Linguistik, wo sie sich auf den Satzbau und die Bedeutung von Sätzen einer natürlichen Sprache beziehen. Im Deutschen beispielsweise kann man sagen, dass Spricht grün Hund. kein korrekter Satz ist, da die Folge Verb, Adjektiv, Substantiv in keinem Satz erlaubt ist. Dagegen entspricht Der sprechende Hund ist grün. der Syntax des Deutschen und seine Bedeutung (Semantik) ist, dass das Objekt vom Typ Hund mit Eigenschaft sprechend, auch die Eigenschaft grün hat. In der Welt der Programmiersprachen spielen Programme die Rolle der Sätze, und die Bedeutung eines Programms sind die Aktionen/Berechnungen, die vom Programm gefordert werden. Grammatik Bei den natürlichen Sprachen definiert man eine Grammatik um korrekte von nicht korrekten Sätzen unterscheiden zu können. Die Grammatik des Deutschen sagt, einfache Sätze nach dem Schema Subjekt Prädikat Objekt gebildet werden. Subjekt, Prädikat und Objekt sind dabei syntaktische Kategorien die auf unterschiedliche Art gebildet werden können. Hans ist ein Subjekt, aber auch der kleine gelbe Feutel, der nachts immer brummt. Die Vielfalt der Möglichkeiten wird durch Regeln zum Ausdruck gebracht: Satz ::= Subjekt Prädikat Objekt Subjekt ::= Name Nominalphrase Nominalphrase ::=... Objekt ::= Verb... Das bedeutet, dass ein Satz eine Folge von Subjekt, Prädikat und Objekt ist und ein Subjekt entweder ein Name oder eine Nominalphrase (Der Strich steht für oder ). Ein Name ist ein einfaches Wort, das in der Grammatik nicht weiter beschrieben wird, Nominalphrase und Objekt sind etwas komplexere Gebilde, die durch weitere Regeln zu beschreiben sind. Eine Äußerung, die zu den Regeln einer Grammatik passt ist syntaktisch korrekt. Das heißt im Allgemeinen, aber nicht unbedingt, dass sie auch eine Bedeutung hat. Beispielsweise ist Das Mondkalb fliegt im Kontext. syntaktisch korrekt, aber unsinnig, da Mondkälber eventuell fliegen können, aber nichts und niemand in einem Kontext fliegen kann. Produktionen, Terminale und Nichtterminale Eine Grammatik besteht aus Produktionen (auch (Ableitungs ) Regeln genannt), beispielsweise: Subjekt ::= Name Nominalphrase Eine Produktion 1 hat eine linke Seite (Subjekt) und eine rechte Seite (Name Nominalphrase). Ein Subjekt kann entsprechend dieser Regel abgeleitet entweder in einen Namen oder eine Nominalphrase. 1 Grammantiken sind hier stets kontextfreie Grammatiken

6 Programmiersprachen Konzepte und Implementierungen 5 Subjekt ist also ableitbar, es ist ein Nichtterminal (auch Nonterminal ( Symbol)). Symbole die nicht weiter ableitbar sind, nennt man Terminale (Terminalsymbole) Lexikalische Analyse Syntaxbeschreibung und Erkennung in zwei Stufen Eine der herausragenden Eigenschaften des Menschen ist seine Fähigkeit Muster und Strukturen zu erkennen. Das gilt auch für textuelle Strukturen. Für Wesen, die von der Evolution darauf trainiert wurden, den Schatten eines Raubiers im Gestrüpp innerhalb von Bruchteilen einer Sekunde zu erkennen, ist es eine Kleinigkeit eine einfache Struktur wie while ( i < 5 ) { alpha = alpha*2; i = i+1; mit einem Blick zu erfassen und mit der Struktur des Textes sehen wir die Bedeutung, die von ihr getragen wird. Das Layout des Textes ist dabei sehr hilfreich. Aber auch, wenn wir den Text als lineare Sequenz vorfinden, while ( i < 5 ) { alpha = alpha*2; i = i+1; sind wir recht schnell in der Lage seine Struktur und damit seine Bedeutung zu erfassen. Der Erkennungsprozess findet ähnlich wie beim Erfassen eines Satzes in einer natürlichen Sprache in verschiedenen Ebenen statt: Wir erkennen die Zeichen (a, i, ;, ( etc.), die Worte, (while, i) die aus den Zeichen zusammengesetzt sind und dann die groberen Strukturen (Zuweisung, Bedingung, Schleife) die von den Worten gebildet werden. Entsprechend diesem Erkennungsprozess in unterschiedlichen Ebenen beschreibt man die Syntax einer Programmiersprache auch in zwei Stufen: Zeichen Worte: Welche Zeichenfolgen sind die erlaubten Worte der Sprache. Worte Sätze: Welche Wortkombinationen sind erlaubte Sätze (Programme) der Sprache. Für beide Ebenen gibt man jeweils eine Grammatik an, die die erlaubte Konstrukte definiert. Die Zweistufigkeit ist nicht unbedingt notwendig, aber sie entspricht dem natürlichen Erkennungsprozess, erlaubt seine effiziente Implementierung und macht die Beschreibung einfacher und übersichtlicher. Token Der erste Erkennungsprozess ist also der Schritt von den Zeichen zu den Worten. In Anlehnung an die Linguistik nennt man die Worte eine Programmiersprache Lexeme und den Prozess ihres Erkennens Lexikalische Analyse. Statt den Begriff Lexem zu gebrauchen ist es allerdings üblicher von Token zu reden. In unserem Beispiel haben wir es mit 20 Tokens (Lexemen) zu tun: while, (, i, <, 5, ), {, alpha, =, alpha, *, 2, ;, i, =, i, +, 1, ;, Wir sehen, viele Tokens bestehen nur aus einem einzigen Zeichen und der Prozess ihres Erkennens beschränkt sich im Wesentlichen darauf, Unwesentliches wie Leerzeichen und Zeilenvorschübe zu eliminieren und zusammengehörige Zeichenfolgen zu identifizieren. Die Tokens sind üblicherweise in Klassen eingeteilt:

7 6 Thomas Letschert FH Giessen Friedberg Klasse Beispiel Tokens Schlüsselwort (Keyword) while if else return Literal true, false Operator + - *! > < && == Einfach (Simple)., ; = Klammer (Bracket) ( ) { Bezeichner (Identifier) i alpha Bezeichner (Identifier) sind Namen, die typischerweise durch eine Definition eingeführt werden. Sie identifizieren (bezeichnen) etwas beispielsweise eine Variable, eine Funktion, oder einen Typ. In einem Programm an einer bestimmten Stelle bezeichnen sie das eine, an einer anderen Stelle und erst recht in einem anderen Programmen etwas anderes. void x( int y) { String x;... x... // x ist eine String-Variable... x... // x ist eine void-methode Literale dagegen sind Worte mit einer festen, unabänderlichen, in allen Programmen der gleichen Sprache immer gleichen Bedeutung. true bedeutet in Java immer wahr und 17 immer siebzehn. Reguläre Ausdrücke Um die Menge der Tokens genau zu spezifizieren, kann man Grammatiken benutzen. Allerdings ist es üblich die einfachere Notation der regulären Ausdrücke zu verwenden, um lexikalische Elemente (Tokens) zu spezifizieren. Die Menge der Zeichenketten, die ganze Zahlen bezeichnen kann beispielsweise mit folgendem regulären Ausdruck beschrieben werden: ( )( )* Der senkrechte Strich bezeichnet Alternativen (a b ist a oder b), der Stern eine beliebige Wiederholung und die Klammern dienen der Strukturierung. Der reguläre Ausdruck oben spezifiziert damit die Menge der Zeichenketten, die mit einer der Ziffern 1 bis 9 starten und von beliebig vielen Ziffern gefolgt werden. Dieser Ausdruck generiert die Sprache 1, 2,... 9, 10, 11, 12,... 99, 100, 101,... Die definierte Sprache ist also die Menge aller Zeichenketten, die ganze Zahlen bezeichnen. Das Gleiche kann auch mit einer Grammatik ausgedrückt werden: digit ::= 0 posdigit posdigit ::= digitsequenz ::= digit digit digitsequenz number ::= posdigit posdigit digitsequenz Durch die Einführung von Namen für reguläre Ausdrücke oder deren Teilausdrücke kann das Ganze noch strukturiert werden: posdigit = digit = number = posdigit digit* Das sieht dann aus wie eine Grammatik, ist aber nicht das Gleiche! In einem, mit Namen strukturierten regulären Ausdruck kommen Namen vor und Namen sind keine Nichtterminale. Die Namen werden nur zur Übersicht eingesetzt. Es muss also möglich sein, die Definition eines regulären Ausdrucks mit Namen in einen ohne Namen zu transformieren. Das ist dann gegeben, wenn die Namen nicht direkt oder indirekt rekursiv eingesetzt werden, wenn in einem Ausdruck also nur vorher vollständig definierte Namen vorkommen. Um die Namen der Ausdrücke besser von Zeichen unterscheiden können werden sie gelegentlich in spitze Klammern eingefügt:

8 Programmiersprachen Konzepte und Implementierungen 7 <letter> = a b c d e f g h i j k l m n o p q r s t u v w x y z <digit> = <identifier> = <letter> (<digit> <letter>)* Rekursive Definitionen sind nicht grundsätzlich verboten, sondern nur dann, wenn die Rekursion nicht aufgelöst werden kann. Beispielsweise ist <A> = b a <A> erlaubt da es in <A> = a* b umgeformt werden kann. Dagegen ist nicht möglich <A> = c a <A> b so umzuformen, dass kein Name mehr vorkommt. Der Ausdruck <A> = a* c b* ist nicht äquivalent, da die Zahl der a s und b s hierbei jeweils beliebig groß sein kann, in der Definition vorher aber muss deren Zahl gleich sein. aaaaacb ist beispielsweise ein Wort der durch a*cb* definierten Sprache. Es ist aber kein Wort der durch den rekursiven Ausdruck definierten Sprache. Die Ausdruckskraft regulärer Ausdrücke ist also beschränkt. Jeder reguläre Ausdruck hat eine äquivalente Grammatik aber nicht umgekehrt. Es ist mit ihnen nicht möglich Mengen von Zeichenketten mit einer verschachtelten Struktur zu definieren in der Form, dass einer bestimmten Menge von öffnenden Klammern irgendwann die gleiche Anzahl von schließenden Klammern folgen muss. Zur Beschreibung der lexikalischen Elemente einer Programmiersprache sind sie trotz ihrer Beschränkungen bestens geeignet. Die lexikalischen Elemente sind so einfach, dass ein komplexerer Beschreibungmechanismus nicht benötigt wird und der Erkennungsmechanismus ist den einfachen Möglichkeiten entsprechend einfach. Für regulärer Ausdrücke existieren diverse Notations Varianten auf die wir hier nicht weiter eingehen wollen Implementierung eines Scanners Scanner Die erste Komponente, die bei der Verarbeitung eines Programms zum Einsatz kommt, ist der Scanner. Er ist für die lexikalische Analyse zuständig und kommt dieser Aufgabe nach, indem er die Zeichenfolge in eine Sequenz von Tokens umwandelt. Tokens können durch reguläre Ausdrücke beschrieben werden 2 und ein Scanner hat dem entsprechend die Aufgabe, aus einem Strom von Zeichen die Folgen heraus zu fischen, die dem Muster eines regulären Ausdrucks entsprechen. Ein klassisches Werkzeug zur Implementierung eines Scanners ist Lex. Lex ist ein Scannergenerator. Er akzeptiert eine Beschreibung der Tokens in Form regulärer Ausdrücke und erzeugt daraus eine Funktion, die diese Tokens erkennt. Es gibt daneben noch andere Scanner Generatoren und natürlich kann man auch auf Generatoren verzichten und den Scanner direkt implementieren. In Java hat man dabei eine effektive Unterstützung durch die Klasse Scanner. (Die Klasse Scanner ist noch kein Scanner in unserem Sinne, sie unterstützt die Konstruktion eines Scanners.) Token Implementierung Am Anfang jeder Scannerimplementierung steht die Definition der Tokens. Angenommen wir haben eine Sprache Klassen von Tokens, unter anderem Bezeichner (Identifier) und Schlüsselworte (Keyword), deren Struktur jeweils durch einen regulären Ausdruck angeben wird: 2 Es ist Konvention und allgemeine Praxis, dass Tokens so einfach sind, dass sie durch reguläre Ausdrücke beschrieben werden können. Selbstverständlich ist es nicht verboten bei den Tokens mit komplexeren Strukturen zu arbeiten, aber es ist nicht sinnvoll und niemand tut es.

9 8 Thomas Letschert FH Giessen Friedberg Letter = a A b B... z Z Digit = Identifier = Letter (Letter Digit)* KeyWord = if while Diese Definition ist äquivalent zu folgender in der abgekürzter Schreibweise: Identifier = [a-za-z][a-za-z0-9]* KeyWord = if while Für den Scanner ist das Programm eine Folge von Tokens: Program = Token* Token = Identifier Keyword... Wir definieren dazu eine Klasse Token die in ihrem statischen Anteil die Menge der möglichen Tokens definiert und ihre syntaktische Struktur in Form regulärer Ausdrücke festlegt: public class Token { public static enum TokenClass { OPERATOR, SIMPLE, BRACKET, INTLITERAL, BOOLEANLITERAL, KEYWORD, IDENTIFIER ; static String[] patternstr = new String[TokenClass.values().length]; static String alltokenpatternstr; static { patternstr[tokenclass.identifier.ordinal()] = "([a-za-z][a-za-z0-9]*)"; patternstr[tokenclass.keyword.ordinal()] = "(program var while if else)"; patternstr[tokenclass.operator.ordinal()] = "([\\+\\-\\*\\/\\&\\<\\>] ==!=)"; patternstr[tokenclass.intliteral.ordinal()] = "([0-9][1-9]*)"; patternstr[tokenclass.booleanliteral.ordinal()] = "(true false)"; patternstr[tokenclass.simple.ordinal()] = "[\\.\\,\\;\\=]"; patternstr[tokenclass.bracket.ordinal()] = "[\\ (\\{ \\) \\]"; alltokenpatternstr = "("; for (int i = 0; i < TokenClass.values().length - 1; i++) { alltokenpatternstr = alltokenpatternstr + patternstr[i] + " "; alltokenpatternstr = alltokenpatternstr + patternstr[tokenclass.values().length - 1] + ")"; // private TokenClass private String tokenclass; tokenvalue; public Token (TokenClass tc, String tokenvalue){ this.tokenclass = tc; this.tokenvalue = tokenvalue; public String tostring() { return tokenclass + "<" + tokenvalue + ">"; public boolean equals(object obj) { if (! (obj instanceof Token) ) return false; Token other = (Token)obj; return tokenclass.equals(other.tokenclass)

10 Programmiersprachen Konzepte und Implementierungen 9 && tokenvalue.equals(other.tokenvalue); Zuerst werden die Tokenklassen definiert. Jeder Tokenklasse wird ein entsprechender regulärer Ausdruck (ein Pattern) zugeordnet und in dem Feld patternstr gespeichert. Die Variable alltokenpatternstr enthält einen regulären Ausdruck, der alle Tokenklassen erkennt. Es ist einfach eine Verkettung aller regulären Ausdrücke mit. Mit diesem Muster kann nach irgendeinem Token gesucht werden. Scanner Implementierung Eine einfache Scanner Implementierung würde den Text lesen und eine Sequenz von Tokens erzeugen die dann vom Parser weiter verarbeitet werden. Scanner und Parser stellen dann jeweils eine Compiler Phase dar: Zeichen Sequenz Scanner Token Sequenz Parser Syntaxbaum Üblicherweise will man es aber vermeiden den vollständigen Token Strom zu erzeugen. Es ist wesentlich effizinter, wenn die Token so erzeugt werden, wie der Parser sie benötigt. Der Scanner arbeitet dann als Filter der den Zeichenstrom in einen Tokenstrom transformiert. Ein solcher Filter kann als Iterators realisiert werden. Eine nette Implementierung eines Scanners wäre damit: public class TokenScanner<Token> implements Iterator<Token> Der Generizitätsmechanismus von Java erlaubt diese schöne Lösung leider nicht: In der generischen Klasse TokenScanner kann kein Bezug zu einer Eigenschaft von Token hergestellt werden. Wir müssten eine Basisklasse oder eine Schnittstelle TokenBase definieren, um auf Attribute und/oder Methoden zugreifen zu können, die in TokenBase definiert sind: public class TokenScanner<Token extends TokenBase> implements Iterator<Token> Nun sind aber Token und TokenBase Typen und der Scanner muss auf ihre Eigenschaften als Typen zugreifen, beispielsweise auf den in Token definierten Aufzählunsgtyp der Tokenklassen oder die diesen Klassen zugeordneten regulären Ausdrücke. Diese statischen Elemente von Token bzw. TokenBase lassen sich aber nicht in den Vererbungs Mechnanismus packen, der in dem Generizitätskonzept von Java steckt. Entweder wir machen aus den statischen Elementen Objekt Eigenschaften, 3 oder wir verzichten darauf, die Abhängigkeit von TokenScanner vom Typ der Token in einen expliziten Mechanismus der Generizität zu fassen. Ein Scanner, dessen Abhängigkeit vom Typ der Tokens nur implizit ist, könnte als public class TokenScanner implements Iterator<Token> definiert werden. Der Typ TokenScanner hängt dabei immer von Vom Typ Token ab, aber diese Ahängigkeit kann und wird nicht über eine Instanzierung aufgelöst, sondern steckt implizit in einer Abhängigkeit der Klassendefinition. Um den Java Scanner public final class java.util.scanner implements Iterator<String> in dieser Klasse nutzen zu können, kann man definieren: public class TokenScanner implements Iterator<Token> { private Scanner private String private Pattern private Pattern[] private boolean scanner; nexttokenstr; alltokenpattern; tokenpattern = new Pattern[TokenClass.values().length]; eof = false; 3 beispielseise indem wir eine Klasse TokenTypeDefinition definieren mit den TokenTypen als Instanzen dieser Klasse

11 10 Thomas Letschert FH Giessen Friedberg public TokenScanner(Scanner scanner) { this.scanner = scanner; alltokenpattern = Pattern.compile(Token.allTokenPatternStr); for (TokenClass tc : Token.TokenClass.values()){ tokenpattern[tc.ordinal()] = Pattern.compile(Token.patternStr[tc.ordinal()]); public boolean hasnext() { if ( eof ) return false; else return true; public Token next() { if ( eof ) return new Token(TokenClass.SIMPLE, " "); if (!scanner.hasnext()) { eof = true; return new Token(TokenClass.SIMPLE, " "); nexttokenstr = scanner.findinline( alltokenpattern ); for (TokenClass tc : Token.TokenClass.values()){ if ( (tokenpattern[tc.ordinal()]).matcher(nexttokenstr).lookingat() ) { System.out.println(new Token(tc, nexttokenstr)); return new Token(tc, nexttokenstr); return new Token(TokenClass.ERRORTOKEN, nexttokenstr); public void remove() { throw new UnsupportedOperationException(); Der Token Scanner beruht im Wesentlichen auf der Funktionalität der Java Klassen Scanner und Pattern mit denen im Eingabestrom Zeichenkette gefunden werden, die einem durch einen regulären Ausdruck beschrieben Muster entsprechen. Eine Verwendung dieser Klasse ist: public static void main(string[] args) throws FileNotFoundException { String programfilename = args[0]; Scanner scanner = new Scanner(new File(programFilename)); TokenScanner tokenscanner = new TokenScanner(scanner); while ( tokenscanner.hasnext() ) { System.out.println( tokenscanner.next() ); Schnittstelle zum Parser Der Scanner wird vom Parser genutzt um das jeweils nächste Token zu produzieren. Im Prinzip reichen dazu die Routinen next und hasnext. Um den Umgang mit dem Scanner etwas bequemer zu gestalten definieren wir eine Klasse BufferedScanner. In dieser Klasse werden jeweils zwei Tokens vorgehalten, das aktuelle und das nächste. Der Parser greift auf diese Zeichen mit match und check Routinen zu. Wenn beispielsweise das aktuelle Token das Schlüsselwort while sein muss, dann ruft der Parser match(token.tokenclass.keyword, "while"); Wenn er prüfen will, ob das nächste Token ein while Token ist, dann verwendet er:

12 Programmiersprachen Konzepte und Implementierungen 11 if ( Token.checkAhead(Token.TokenClass.KEYWORD, "while") )... public class BufferedTokenScanner { private TokenScanner scanner; private Token akttoken; private Token lookaheadtoken; public BufferedTokenScanner(TokenScanner scanner) { this.scanner = scanner; akttoken = scanner.next(); if ( scanner.hasnext() ) lookaheadtoken = scanner.next(); else lookaheadtoken = new Token(Token.TokenClass.SIMPLE, " "); void scan() { akttoken = lookaheadtoken; if ( scanner.hasnext() ) lookaheadtoken = scanner.next(); // ELSE lookaheadtoken == eof-token und bleibt es public String match(token.tokenclass tc) { if ( akttoken.tokenclass.equals(tc) ) { String res = akttoken.tokenvalue; scan(); return res; System.err.println(aktToken+" Token " + tc.tostring() + " erwartet"); return null; public String match(token.tokenclass tc, String tv) { if ( akttoken.tokenclass.equals(tc) && akttoken.tokenvalue.equals(tv) ) { String res = akttoken.tokenvalue; scan(); return res; System.err.println(aktToken+" Token " + tc.tostring() + " erwartet"); return null; public boolean check(token.tokenclass tc) { return ( akttoken.tokenclass.equals(tc) ); public boolean check(token.tokenclass tc, String tv) { Token t = akttoken; return ( t.tokenclass.equals(tc) && t.tokenvalue.equals(tv) ); public boolean checkahead(token.tokenclass tc) { return lookaheadtoken.tokenclass.equals(tc); public boolean checkahead(token.tokenclass tc, String tv){ return ( lookaheadtoken.tokenclass.equals(tc) && lookaheadtoken.tokenvalue.equals(tv) );

13 12 Thomas Letschert FH Giessen Friedberg Syntaxtische Analyse EBNF Lexikalische Strukturen (die Tokens) werden mit regulären Ausdrücken beschrieben, einem, im Vergleich zu Grammatiken bequemeren aber auch audrucksärmeren Formalismus. Für die Beschreibung der Syntax 4 verwendet man einen Formalismus, der sich EBNF (Extended Backus Naur Form) nennt. EBNF ist eine erweiterte Notationsform für Grammatiken, die deren Ausdrucksmächtigkeit aber nicht beeinflusst. Jede Grammatik in EBNF Form kann in eine äquivalente einfache Grammatik umgeformt werden. Ein Beispiel für eine Grammatik in EBNF Form ist: Programm ::= {Statement Statement ::= Declaration WhileStat AssignStat Block Declaration ::= Type Identifier [Initialization] ; Initialization ::= = Expression WhileStat ::= while ( Expression ) Statement AssignStat ::= Identifier = Expression ; Block ::= { {Statement Type ::= int boolean Die Terminalsymbole haben wir in Hochkommas gesetzt. Elemente in geschweiften Klammern können beliebig oft wiederholt werden und Elemente in eckigen Klammern sind optional, können also vorhanden sein oder wegfallen. Eine EBNF Grammatik ist einfach eine Grammatik mit Produktionen deren rechte Seiten reguläre Ausdrücke sind. Die Notationsvarianten für EBNF ergeben sich aus denen für reguläre Ausdrücke. Ableitungsbäume und das Erkennen eines Ausdrucks Arithmetische Ausdrücke können mittels einer Grammatik beschrieben werden. Die Sache wird allerdings etwas komplexerer als zunächst erwartet, wenn Operatorprioritäten in Spiel kommen (Punkt vor Strichrechnung). Ein Beispiel ist: Expression ::= AddExpr [ ( < > ) AddExpr ] AddExpr ::= MultExpr [ ( + - ) AddExpr ] MultExpr ::= UnaryExpr [ ( * / ) MultExpr ] UnaryExpr ::= Literal Identifier Der Ausdruck 2+3*4 entspricht dieser Grammatik. Er entspricht allerdings auch der wesentlich einfacheren Grammatik Expression ::= Literal Identifier ( Expression ) Expression Operator Expression Operator ::= + - * / Der Unterschied besteht darin, dass eine Ableitung nach der zweiten Grammatik die Priorität der Multiplikation vor der Addition nicht zum Ausdruck bringt: Expression -> AddExpr -> MultExpr + AddExpr -> UnaryExpr + MultExpr -> Literal + AddExpr -> 2 + MultExpr -> 2 + UnaryExpr * MultExpr -> 2 + Literal * UnaryExpr -> * Literal -> * 4 Um den Ausdruck abzuleiten muss die Produktion für die Addition AddExpr ::= MultExpr [ ( + - ) AddExpr ] 4 Genaugenommen ist die Unterscheidung in lexikalische und syntaktische Strukturen nicht korrekt. Die lexikalischen Elemente gehören ebenfalls zur Syntax. Man unterteilt also die Syntaxbeschreibung in die Beschreibung syntaktischer und lexikalischer Strukturen.

14 Programmiersprachen Konzepte und Implementierungen 13 zuerst angewendet werden. Sie steht damit im Ableitungsbaum weiter oben und bindet ihre Elemente darum schwächer als tiefere Ableitungen. In einem Ableitungsbaum sieht man dies noch deutlicher (siehe Abbildung 1.1). Expression Expression Expression * Expression Expression + Expression Literal Literal Expression + Expression Expression * Expression 4 2 Literal Literal Literal Literal Abbildung 1.1: Zwei Ableitungsbäume für 2+3*4 Nach der einfacheren Grammatik sind zwei Ableitungen möglich. Eine nach der + weiter unten steht (stärker bindet), als auch eine nach der * stärker bindet. Das Ergebnis, die erzeugte Zeichenfolge, ist in beiden Fällen gleich. Trotzdem ist es nicht das Selbe. Eine Grammatik hat nicht nur die Aufgabe alle korrekten Sätze zu produzieren, sie muss auch deren innere Struktur kenntlich machen. Die innere Struktur ergibt sich aus den Produktionen der Grammatik, die zur Ableitung des Satzes (des Ausdrucks) angewendet wurden. Beim Erkennen (Verstehen) werden diese dann umgekehrt wieder rekonstruiert. Diese Rekonstruktion sollte eindeutig sein und das Gemeinte korrekt wiedergeben. Für natürliche Sprachen gilt das gleiche Prinzip: Wir verstehen Sätze oder Satzteile nur dann, wenn die Produktion, nach der sie abgeleitet wurden, eindeutig festliegen. Hört man beispielsweise wir fliegen..., dann ist erst einmal unklar, ob es sich um eine Nominalphrase ( Wir Fliegen ) handelt, oder um eine Nominalphrase ( Wir ) plus Verb ( fliegen ). Folgt ein Verb, dann wird die Sache klar: In wir fliegen summen laut ist wir fliegen... eindeutig eine Nominalphrase. Sätze mit zwei Verben sind nicht korrekt, die Interpretation von fliegen als Verb scheidet aus, die Struktur des Satzes ist jetzt eindeutig. Parser, abstrakte Syntax, AST Ein Parser hat erstens die Aufgabe zu entscheiden, ob ein vorgelegter Programmtext korrekt ist und wenn ja dann soll er zweitens die Struktur des Textes offen legen. Dazu muss er herausfinden ob und wie der Text nach den Regeln der Grammatik aus einem Start Nonterminal abgeleitet wurde. Üblicherweise setzt der Parser auf die Vorarbeit eines Scanners auf. Er analysiert also nicht direkt die Zeichenfolge des Quelltextes, sondern eine Folge von Tokens die ihm der Scanner liefert. Als Ergebnis eines erfolgreichen Erkennungsprozesses kann der Parser den Ableitungsbaum liefern. Üblicherweise lässt man den Parser aber einen vereinfachten Ableitungsbaum produzieren, einen sogenannten abstrakten Syntaxbaum (abstract Syntax Tree, oder kurz AST). Der Begriff abstrakt bezieht sich darauf, das gegenüber einem vollständigen Ableitungsbaum diverse Details weggelassen ( weg abstrahiert ) werden. So ist es für die weitere Verarbeitung nicht notwendig alle Kommas, Semikolons, Klammern, etc. aufzuheben. Im Quelltext muss dieses syntaktische Rauschen (manche nennen es syntaktischen Zucker ) vorhanden sein, um den linearen Text korrekt und eindeutig erkennen zu können, danach ist es aber nicht mehr relevant. Das Gleiche gilt für komplizierte Grammatikregeln in den Operatorprioritäten zum Ausdruck kommen. So reicht es beispielsweise völlig aus, wenn der Parser uns zu dem Text while ( a < b ) { a = a * b + 4;

15 14 Thomas Letschert FH Giessen Friedberg einen Baum nur mit den essentiellen Informationen, den AST, liefert (siehe Abbildung 1.2); ohne Klammern, Satzzeichen oder komplizierten Ableitungsketten mit MultExpr, AddExpr, UnaryExpr und so weiter. while less assign Id: a Id: b plus mult Literal: 4 id: a Id: b Abbildung 1.2: Abstrakter Syntaxbaum (AST) Der abstrakte Syntaxbaum ist ein Baum. Wir beschreiben die Menge aller möglichen Bäume durch eine Baumgrammatik. Ein Beispiel ist: Expression = UnaryExpr BinaryExpr LiteralExp IdentfierExpr UnaryExpr :: operator:op Expression BinaryExpr :: operator:op left:expression right:expression LiteralExpr :: Literal IdentfierExpr :: Identifier Op = add sub mult div Damit soll ausgedrückt werden, dass der Knotentyp Expression vier Varianten hat: UnaryExpr, BinaryExpr, und so weiter. In der Variante BinaryExpr besteht ein Knoten aus drei Komponenten, einem operator genannten OP Konten und zwei Expression Knoten mit den Namen left und right. Man kann eine Grammatik auch als implizite Definition einer Baumgrammatik betrachten: Einer Baumgrammatik die die Menge aller möglichen Ableitungsbäume festlegt. Beispielsweise werden mit N ::= anb c Texte generiert deren zugeordneten Ableitungsbäumen durch die Baumgrammatik N = nt-n t-n nt-n :: a N b t-n :: c beschrieben werden. Die Syntaxanlyse, Scanner und Parser, haben also die Aufgabe aus einem linearen Text, dessen Struktur durch die konkrete Syntax (Grammatik, Tokendefinition) beschrieben wird, einen abstrakten Syntaxbaum (AST) entsprechend der abstrakten Syntax (Baumgrammatik) zu konstruieren. Expilzite oder meist implizite Zwischenstation ist dabei der Ableitungsbaum (konkreter Syntaxbaum): konkrete Syntax (Grammatik) Syntaxanalyse Abstraktion abstakte Syntax (Baumgrammatik) Text = Ableitungsbaum = AST

16 Programmiersprachen Konzepte und Implementierungen 15 Rekursiver Abstieg Die einfachste Methode einen Parser zu implementieren, ist Methode des rekursiven Abstiegs (engl. recursive decent). Man vergleicht dabei einfach den Text mit den in Frage kommenden Produktionen. Beginnt der Text an einer Stelle, an der eine Anweisung Stmt ::= whilestmt ifstmt... whilestmt ::= while ( Expression ) Stmt ifstmt ::= if ( Expression ) Stmt else Stmt... erwartet wird, etwa mit einem while, dann ist unter allen möglichen Produktionen sicher die für whilestmt die richtige. Es kann ein while und ein ( Token erwartet werden, dann muss der Text (die Tokenfolge) mit den Produktionen von Expression verglichen werden und so weiter. Allgemein wird für jedes Nicht Terminal eine Funktion definiert. Diese sucht nach einer passenden Produktion in dem der Text mit den Terminalen der Produktion verglichen wird. Für jedes Nicht Terminal auf der rechten Seite wird die entsprechende Funktion aufgerufen. Jede Funktion liefert als Ergebnis einen Teil des abstrakten Syntaxbaums. Für unser Beispiel ergibt sich etwa eine Funktion wie: void StmtAST parsestmt(){ if ( akttoken == whiletoken ) { akttoken = nexttoken(); if ( akttoken!= klammerauftoken ) ERROR; akttoken = nexttoken(); exprast = parseexpression(); if ( akttoken!= klammerzutoken ) ERROR; stmtast = parsestmt() return make_while_stmt_ast(exprast, stmtast); if akttoken == iftoken ) { LL(1) Grammatik Dies Methode des rekursiven Abstiegs funktioniert nur bei Grammatiken, deren Produktionen so beschaffen sind, dass man möglichst schon am ersten Zeichen erkennen kann, welche Produktion gewählt wurde. Beispielsweise nutzt es bei der Grammatik Stmt ::=... ifstmt ifelsestmt... ifstmt ::= if ( Expression ) Stmt ifelsestmt ::= if ( Expression ) Stmt else Stmt... wenig wenn klar ist, dass der Text mit einem if beginnt. Es ist trotzdem nicht bekannt, welche Produktion zu wählen ist. Grammatiken mit der gewünschten Eigenschaft, also solche bei denen das erste Terminalsymbol jedes Unterausdrucks genügend Informationen enthält um zu entscheiden, welche alternative Produktion gewählt wird, nennt man LL(1) Grammatiken. LL(k) Grammatiken sind solche, bei denen die nächsten k Symbole genügend Informationen für die Auswahl einer Produktion enthalten. Die Menge aller Zeichen (Tokens), mit denen eine Produktion P beginnen kann, nennt man First(P), die First Menge von P. In einer LL(1) Grammatik sind die First Mengen aller Produktionen eines Nicht Terminals disjunkt. Das ist der Idealfall für einen rekursiven Abstieg. Ist dies nicht gegeben, dann kann man eventuell mit einem oder zwei weiteren Zeichen Vorausschau eine Produktion auswählen. Beispielsweise sind die First Mengen der Produktionen für assignstmt und callstmt in Stmt ::=... assigstmt callstmt... assigstmt ::= Identifier = Expression ; callstmt ::= Identifier ( Expression ) ;...

17 16 Thomas Letschert FH Giessen Friedberg nicht disjunkt. Trotzdem kann leicht eine Entscheidung getroffen werden, indem man einfach auf das nächste Zeichen schaut. Es muss ein Zuweisungszeichen sein (assigstmt) oder eine Klammer (callstmt). Linksrekursionen Ein weiteres Problem für Parser, die mit rekursiven Abstieg arbeiten, sind Linksrekursionen. Ein einfaches Beispiel ist: Expr ::= Expr Operator Term Term Linksrekursionen führen zu Endlosrekursionen beim rekursiven Abstieg: Die Funktion parseexpr ruft als erstes parseexpr. Das Problem kann man lösen, indem die Grammatik mit Linksrekursion in eine mit Rechtsrekursion umgeschrieben wird: Expr ::= Term [ExprRest] ExprRest ::= Operator Expr Im allgemeinen gilt: Wenn X ::= X Xrest T eine linksrekursive Regel ist, bei der T nicht mit X beginnt, dann wissen wir, dass dies Ausdrücke der Form T (Xrest)* erzeugt. Diese Ausdrücke können also äquivalent ohne Linksrekursion als X ::= T (Xrest)* beschrieben werden. Nach diesem Muster können linksrekursive Grammatiken in solche ohne Linksrekursion umgeformt werden Implementierung eines Parsers Abstrakte Syntax Ein Parser ist eine Funktion, die einen Strom von Tokens in einen Baum der abstrakten Syntax abbildet. Als eine der ersten Entwurfsentscheidungen eines Parsers ist darum festzulegen, wie abstrakte Syntaxbäume implementiert werden. Abstrakte (und konkrete) Syntaxbäume einer formalen Sprache können durch Baumgrammatiken beschrieben werden. Eine solche Grammatik hat zwei Arten von Regeln: Vereinigungen von Alternativen (= Regeln) und Produktionen (:: Regeln). Diese Aufteilung entspricht einer Aufteilung der üblichen ::= Regeln in jeweils explizit benannte Alternativen und die zugehörigen Expansionen. Die Namen der Alternativen werden als Knoten interpretiert und die Expansion als deren Nachfolger. Die Produktionen definieren Knoten Strukturen und deren Vereinigung zu einer Sorte von Knoten. Beispielsweise definiert die Baumgrammatik Cmd = IfCmd AssignCmd IfCmd :: cond:expr then:cmd else:cmd AssignCmd :: left:var right:expr Expr = UnaryExp BinaryExpr VarExp ConstExpr UnaryExpr :: sign:op exp:simpleexpr BinaryExpr :: left:expr op:op right:expr VarExpr :: Var ConstExpr :: Const die Knoten Sorten Cmd und Exp deren Elemente jeweils auf zwei unterschiedliche Arten aufgebaut sein können. Bei einer Implementierung wird dies in das Typ /Klassen System einer OO Sprache abgebildet. Es ist naheliegend den Knoten Sorten einen (Klassen ) Typ der OO Sprache zuzuordnen und die Alternativ Regeln mit Subtypen (Ableitungungen) auszudrücken. Z.B.:

18 Programmiersprachen Konzepte und Implementierungen 17 public abstract class Cmd {... public class IfCmd extends Cmd {... public class AssignCmd extends Cmd { private Expr cond; private Cmd then; private Cmd else;... public abstract class Expr {... public class UnaryExpr extends Expr {... public class BinaryExpr extends Expr { etc.... Man beachte, dass es sich bei dieser Abbildung Baum OO um eine von vielen möglichen Implementierungsentscheidungen handelt. 5 Die Klassen, die einer = Regel entsprechen, sind abstrakt. Es handlet sich ja lediglich um eine Zusammenfassung von Knotentypen zu einem Vereinigungstyp. Ein solcher Vereinigungstyp hat keine Instanzen. Die :: Regeln werden in konkrete Klassen umgesetzt deren Instanzen einen Teil eines abstrakten Syntaxbaums repräsentieren. Konkrete Syntax Ein rekursiv absteigender Parser ordnet einer Grammatik in systematischer Form Erkennungsroutinen zu. Eine kontextfreie Grammatik in BNF Form kann wie die abstrakten Syntax in ein objektorientiertes Typsystem abgebildet werden und die Erkennungsroutinen können in die dabei entstehenden Klassen eingebettet werden. Expr ::= Term Term + Faktor führt zu: public abstract class Expr {... public class Term extends Expr { public Expr parse(..) {... public class TermAddFaktor extends Expr { public Expr parse(..) { etc.... Die Zahl der Regeln und Nonterminale in der konkreten Syntax ist normalerweise wesentlich größer als die der abstrakten Syntax. Ein entsprechendes Vorgehen wie bei der abstrakten Syntax mag da eventuell einem OO Ideal entsprechen, führt aber zu einer Flut von Klassen, die jeweils bestenfalls nur aus einer einzigen Routine bestehen. Wir verzichten darum darauf der konkrete Syntax in ein objektorientiertes Äquivalent zuzuordnen. Der Parser wird statt dessen auf eine Klasse konzentriert, die aus einem Satz rekursiver Funktionen besteht: Für jedes Nonterminal eine Funktion, die die Produktionen dieses Nonterminals erkennt: public class Parser { private BufferedTokenScanner scanner; Parser(BufferedTokenScanner scanner) { this.scanner = scanner; public Prog parseprog() { scanner.match(tokenclass.keyword, "program"); Cmd cmd = parseblock(); return new Prog(cmd); private Cmd parsecmd() { 5 Bäumen sind ein Konzept der Anwendungsebene das zunächst einmal nichts mit Objektorientierung zu tun hat. Es ist Missverständnis zu glauben, dass alle Phänomene auf eine natürliche und offensichtliche Art in eine OO Terminologie überführt werden können oder gar müssen. Abbildungen aus der Anwendungswelt in das Typsystem einer OO Sprache basieren immer auf Entwurfsentscheidungen, die fast immer auch anders getroffen werden könnten.

19 18 Thomas Letschert FH Giessen Friedberg if ( scanner.check(tokenclass.keyword, "var") ) { return parsevardef(); if ( scanner.check(tokenclass.keyword, "if") ) { return parseif(); if ( scanner.check(tokenclass.keyword, "while") ) { return parsewhile(); if ( scanner.check(tokenclass.bracket, "{") ) { return parseblock(); if ( scanner.check(tokenclass.identifier) ) { if ( scanner.checkahead(tokenclass.simple, "=") ) { return parseassignment(); if ( scanner.checkahead(tokenclass.simple, ".") ) { return parsecall(); return null;... etc Traversierung Abstrakter Syntaxbäume Der abstrakte Syntaxbaum (AST) ist die Basis der weiteren Verarbeitung des Programms. Der AST wird dazu mehrfach durchlaufen werden. Um die Verarbeitung der Informationen im Baum von dessen konkreter Datenstruktur zu entkoppeln, nutzen wir ein allgemeines Muster zur Traversierung einer Datenstruktur: das Besuchermuster. Ein Besucher ist ein Objekt mit einer Methode visit die auf die Knoten der Struktur angewendet wird. Die Datenstruktur führt dabei den Besucher zu seinen Knoten und lässt sie dort arbeiten. Das Besucher Muster (Visitor Pattern) umfasst die zu besuchenden Objekte Besucher Objekte (diverse Verarbeiter der Knoten). Die zu besuchenden Objekte, die Knoten des Baums, implementieren alle jeweils eine Besuchsmethode, die den Besucher auf diesesn Knoten anwendet. Nehmen wir als Beispiele die abstrakte Klasse Cmd das Zuweisungskommando AssignCmd: public abstract class Cmd { public abstract void visit (CmdVisitor v) throws Exception; public class AssignCmd extends Cmd {... public void visit (CmdVisitor v) throws Exception{ return v.visit(this); Wir unterscheiden Besucher von Anweisungen (Cmd und seine Ableitungen) und Besucher von Ausdrücken (Expr und seine Ableitungen). Wenn jeder Besucher eine jeweils eine überladene visit Methode für eine Knotenvariante hat: public interface CmdVisitor { public void visit( AssignCmd cmd) throws Exception;

20 Programmiersprachen Konzepte und Implementierungen 19 public void visit( BlockCmd... etc.... cmd) throws Exception; dann wird bei einem Besuch durch einen konkreten Besucher public IrgendEinCmdVisitor implements CmdVisitor { public void visit( AssignCmd cmd ) throws Exception {... cmd.right.visit(...) cmd.right.visit(...)... public void visit( IfCmd cmd ) throws Exception { etc.... eine überladene visit Methode für jeden Typ von Knoten implementiert. Um die Möglichkeit zu haben, einem Besucher einen Parameter mitzugeben und ihn ein Ergebnis erzeugen zu lassen, machen wir die Besucher generisch im Typ der Argumente (TA) und im Typ des Resultats (TR): public interface CmdVisitor<TA, TR> { public TR visit( AssignCmd cmd, TA arg) throws Exception; public TR visit( BlockCmd cmd, TA arg) throws Exception; public TR visit( CallCmd cmd, TA arg) throws Exception; public TR visit( IfCmd cmd, TA arg) throws Exception; public TR visit( WhileCmd cmd, TA arg) throws Exception; public TR visit( VarDefCmd cmd, TA arg) throws Exception; public TR visit( Prog cmd, TA arg) throws Exception; Die Besucherempfangsroutinen der Knoten werden entsprechend angepasst. Beispielsweise beim Zuweisungsknoten: public class AssignCmd extends Cmd {... public <TA, TR> TR visit (CmdVisitor<TA, TR> v, TA arg) throws Exception{ return v.visit(this, arg); Nehmen wir einen Pretty Printer als Beispiel eines Besuchers. Ein Pretty Printer druckt den AST in möglichst schöner Form aus. In unserer Besucher Implementierung besteht er aus zwei Klassen: Die erste Klasse public class PrettyPrinterCmd implements CmdVisitor<Integer, String> erzeugt eine Textdarstellung von Anweisungsknoten. Der Integer Parameter gibt die Einrückungstiefe an und das String Ergebnis ist der erzeugte Text. Die zweite Klasse ist public class PrettyPrintExpr implements ExprVisitor<Integer, String> Sie erzeugt eine Textdarstellung von Ausdrucksknoten. Als Beispiel zeigen wir PrettyPrinterCmd: public class PrettyPrinterCmd implements CmdVisitor<Integer, String> { final static private int indentstep = 2; private static String whitespace(int n) { String result = new String(); for (; n > 0; n--) result += " "; return result; private static ExprVisitor<Integer, String> ppe = new PrettyPrintExpr(); public String visit(assigncmd cmd, Integer indent) throws Exception { String res = whitespace(indent); res = res + cmd.left + " = " + cmd.right.visit(ppe, 0) + ";\n"; return res;

21 20 Thomas Letschert FH Giessen Friedberg public String visit(blockcmd cmd, Integer indent) throws Exception { String res = whitespace(indent) + "{\n"; for (Cmd stmt : cmd.stmts) { res = res + stmt.visit(this, indent + indentstep); res = res + whitespace(indent) + "\n"; return res; public String visit(callcmd cmd, Integer indent) throws Exception { return whitespace(indent) + cmd.callexpr.visit(ppe, 0)+";\n"; public String visit(ifcmd cmd, Integer indent) throws Exception { String res = whitespace(indent) + "if (" + cmd.cond.visit(ppe, 0) + ")\n" + cmd.thencmd.visit(this, indent + indentstep); if (cmd.elsecmd!= null) res = res + whitespace(indent) + "else\n" + cmd.elsecmd.visit(this, indent + indentstep); return res; public String visit(whilecmd cmd, Integer indent) throws Exception { return whitespace(indent) + "while (" + cmd.cond.visit(ppe, 0) + ")\n" + cmd.body.visit(this, indent + indentstep); public String visit(vardefcmd cmd, Integer indent) throws Exception { return whitespace(indent) + "var " + cmd.id + " = " + cmd.value.visit(ppe, 0) + ";\n"; public String visit(prog cmd, Integer indent) throws Exception { String res = whitespace(indent) + "program\n" + cmd.blockcmd.visit(this, indent + indentstep); return res;

22 Programmiersprachen Konzepte und Implementierungen Namensbindung und Umgebungen Namen und Bindungen Namen Jede ernsthafte Programmiersprache verfügt über Mechanismen mit denen Programmierer neue Variablen, Funktionen, Typen und anderes in ein Programm einführen können. Dabei wird das neue Konstrukt mit einem Namen versehen. Mit der Funktionsdefinition int f(int x) { int y = x+1; return y*x; in einem C Programm werden drei Namen eingeführt und an Bedeutungen gebunden. f ist der Name einer Funktion, x der eines formalen Parameters und y der einer lokalen Variablen. Gelegentlich ist es auch möglich anonyme Konstrukte zu definieren. Beispielsweise wird in einem C Programm mit int[3][4] a; nicht nur eine Variable a sondern auch der anonyme Typ der int[3][4] Felder definiert. Einen Namen ist also ein Konstrukt, ein Ding zugeordnet: Ein Wert, eine Variable, eine Funktion, ein Typ, eine Klasse oder was auch immer. Wir reden ganz allgemein von der Bedeutung des Namens. Namen sind in der Regel Bezeichner. In manchen Sprachen können Namen sich auch aus mehreren Bezeichnern zusammensetzen. Beispielsweise sind java.util.arraylist und std::cout Namen in Java bzw. C++ die aus mehreren Bezeichnern zusammengesetzt sind. Das soll hier nicht besonders beachtet werden. Bezeichner ist ein syntaktischer Begriff, der sich auf eine bestimmte Art von Tokens bezieht. Hier interessieren wir uns mehr für die Semantik und bevorzugen darum den Begriff Name, egal wie diese sich im einzelnen zusammensetzen. Die komplexen Programme von heute führen eine Vielzahl von Namen und Dingen als ihre Bedeutung ein, hunderte oder oft auch tausende von Namen mit jeweils einer speziellen Bedeutung, die fast immer auf einen bestimmten Bereich des Programmtextes und/oder eine bestimmte Zeitspanne während der Programmausführung beschränkt ist. Um den Programmentwicklern den Umgang mit dieser Flut von Namen mit welchselnder Bedeutung zur erleichtern, bieten die Sprachen Konzepte, die es erlauben in der Namensflut den Überblick zu behalten. Typisch und weit verbreitet sind: Gültigkeitsbereiche Abdeckungs (Verschattungs ), Import, Exportregeln Überladung Polymorphe Redefinition Statische (Namens ) Bindung Jeder Name, der an irgendeiner Stelle im Programmtext vorkommt, muss mit einer Bedeutung assoziiert werden. Ist das nicht möglich, dann ist das Programm nicht korrekt. In gängigen Sprachen wie Java, C++, etc. wird die Bedeutung eines Namens durch eine Definition bestimmt, die einen bestimmten textuellen Gültigkeitsbereich hat. Innerhalb dieses Gültigkeitsbereichs, einem eventuell verstreuten Stück Quelltext, bezieht sich jedes Vorkommen des Namens auf die diese eine Definition. In den C Text for ( int i = 0; i < 10; i++ ) a = a+i; wird der Name i an die Definition int i gebunden. Die For Anweisung ist also der Gültigkeitsbereich (auch Sichtbarkeitsbereich oder engl. Scope) dieser Definition. Der Name i ist in diesem Bereich an diese Definition gebunden. Programmiersprachen haben meist ein hierarchisches Konzept von Gültigkeitsbereichen, bei denen diese beliebig in einander verschachtelt sein können. In Java etwa haben wir Pakete, in den Paketen Klassendefiniten, darin Methodendefinitionen und in diesen wiederum Blöcke in denen weitere Blöcke autreten können. Wenn die Bindung von von einem Namen zu seiner Definition durch den Quelltext festgelegt ist und schon vom Compiler verfolgt werden kann, dann spricht man von statischer Bindung (auch Lexical Scoping) Statisch ist

23 22 Thomas Letschert FH Giessen Friedberg die Bindung, weil sie von der Programmstatik, dem Quelltext, bestimmt wird. Die Ausführung, die Programmdynamik spielt keine Rolle. Man beachte aber, dass die Bindung sich auf Namen und Definitionen bezieht, nicht auf Namen und Werte/Objekte (Speicherbereiche). Bei einer rekursiven Funktionen können zu einer Definition beliebig viele Objekte existieren. Eines davon ist das aktuelle, auf das sich der Name bezieht. Die Beziehung von Namen zu Werten/Objekten versteht man dann oft als eine zweistufige Abbildung: Der Variablenname bezieht sich auf eine Variable (einen Speicherbereich) (statische Abbildung) und dieser hat einen bestimmten aktuellen Wert. Statische Bindung: Name T ext Definition Ausführung Wert. Dynamische (Namens ) Bindung Ein in den letzten Jahrzehnten etwas aus der Mode gekommenes Konzept der Bindung ist die dynamische Bindung (auch Dynamic Scoping) als Grundstruktur der Namensbindung einer Programmiersprache. In Sprachen mit dynamischer Bindung wird ein Name nicht an Hand der textuellen Struktur des Programms einer Definition zugeordnet. Ein Name bezieht sich statt dessen auf die zuletzt ausgeführte Definition, für diesen Namen. Smalltalk und frühe Versionen von Lisp sind bekannte Beispiele für Sprachen mit dynamischer Bindung. var x = 0; define f() { print x; define g() { var x = 1; f(); g(); // Ausgabe 0/1 bei statischer/dynamischer Bindung Dynamische Bindung macht Programme schwerer verstehbar. Man kann nicht allein durch Analyse des Textes entscheiden, auf was genau sich ein Name bezieht. Dagegen steht die größere Flexibilität. Der Programmlauf bestimmt, was mit einem Namen gemeint ist. Diese Flexibilität ist der Grund, dass mit dem Polymorphismus eine zivilisierte Form der dynamischen Bindung wieder populär wurde. Bei dynamischer Bindung bestimmt der aktuelle Ausführungszustand die Bedeutung eines Namens und seinen aktiellen Wert: Dynamische Bindung: Name Ausführung Definition Ausführung In Sprachen mit statischer Bindung ist die Unterscheidung zwischen Definition (z.b. Variable x) und Wert (z.b. 5) eines Namens sehr wichtig. Bei dynamischer Bindung kann sie vernachlässigt werden. Ein Name hat zu einem bestimmten Zeitpunkt der Ausführung einen bestimmten Wert: Dynamische Bindung: Name Ausführung Wert. Bei statischer Bindung hat die Beziehung Name Definition eine textuelle Ausdehnung (Gültigkeitsbereich) und eine Zeitdauer (Gültigkeitsdauer): Die Zeit in der sich die Kontrolle im Gültigkeitsbereich befindet. In Sprachen mit dynamischer Bindung gibt es nur Gültigkeitsdauer: Die Zeit in der die Bindung eines Namens nicht durch eine neue überschrieben wird. Wert. Polymorphismus als Dynamische (Namens ) Bindung Polymorphismus ist die moderne (und beschränkte) Form der dynamischen Bindung. Der Aufruf o.m(x); führt zum Aufruf einer Methode, die nicht allein statisch identifiziert werden kann: Es hängt vom aktuellen Wert von o ab, welches m tatsächlich aktiv wird. Die Dynamik ist dabei natürlich etwas gebremst. Der Wertebereich von o ist durch den Typ der Variablen o beschränkt und damit sind es auch die als m in Frage kommenden Methoden. Moderne objektorientierte Programmiersprachen bieten also eine gezähmte Version der dynamischen Bindung an. Eine, die die Flexibilität der dynamischen Bindung mit der Übersichtlichkeit und Effizienz der statischen Bindung mit beiderseitigen Kompromissen sinnvoll kombiniert.

Einführung in die Programmierung

Einführung in die Programmierung Technische Universität München WS 2003/2004 Institut für Informatik Prof. Dr. Christoph Zenger Testklausur Einführung in die Programmierung Probeklausur Java (Lösungsvorschlag) 1 Die Klasse ArrayList In

Mehr

Formale Sprachen und Grammatiken

Formale Sprachen und Grammatiken Formale Sprachen und Grammatiken Jede Sprache besitzt die Aspekte Semantik (Bedeutung) und Syntax (formaler Aufbau). Die zulässige und korrekte Form der Wörter und Sätze einer Sprache wird durch die Syntax

Mehr

Objektorientierte Programmierung

Objektorientierte Programmierung Objektorientierte Programmierung 1 Geschichte Dahl, Nygaard: Simula 67 (Algol 60 + Objektorientierung) Kay et al.: Smalltalk (erste rein-objektorientierte Sprache) Object Pascal, Objective C, C++ (wiederum

Mehr

Lineargleichungssysteme: Additions-/ Subtraktionsverfahren

Lineargleichungssysteme: Additions-/ Subtraktionsverfahren Lineargleichungssysteme: Additions-/ Subtraktionsverfahren W. Kippels 22. Februar 2014 Inhaltsverzeichnis 1 Einleitung 2 2 Lineargleichungssysteme zweiten Grades 2 3 Lineargleichungssysteme höheren als

Mehr

Diana Lange. Generative Gestaltung Operatoren

Diana Lange. Generative Gestaltung Operatoren Diana Lange Generative Gestaltung Operatoren Begriffserklärung Verknüpfungsvorschrift im Rahmen logischer Kalküle. Quelle: google Operatoren sind Zeichen, die mit einer bestimmten Bedeutung versehen sind.

Mehr

Definition von domänenspezifischen Sprachen mit Xtext: Einführung. 19. November 2014

Definition von domänenspezifischen Sprachen mit Xtext: Einführung. 19. November 2014 Definition von domänenspezifischen Sprachen mit Xtext: Einführung 19. November 2014 Überblick Was ist zu tun, wenn wir selbst einen Ansatz für modellgetriebenen Entwicklung definieren wollen? Anforderungserfassung

Mehr

Grundbegriffe der Informatik

Grundbegriffe der Informatik Grundbegriffe der Informatik Einheit 15: Reguläre Ausdrücke und rechtslineare Grammatiken Thomas Worsch Universität Karlsruhe, Fakultät für Informatik Wintersemester 2008/2009 1/25 Was kann man mit endlichen

Mehr

Java Kurs für Anfänger Einheit 4 Klassen und Objekte

Java Kurs für Anfänger Einheit 4 Klassen und Objekte Java Kurs für Anfänger Einheit 4 Klassen und Ludwig-Maximilians-Universität München (Institut für Informatik: Programmierung und Softwaretechnik von Prof.Wirsing) 13. Juni 2009 Inhaltsverzeichnis klasse

Mehr

Erwin Grüner 09.02.2006

Erwin Grüner 09.02.2006 FB Psychologie Uni Marburg 09.02.2006 Themenübersicht Folgende Befehle stehen in R zur Verfügung: {}: Anweisungsblock if: Bedingte Anweisung switch: Fallunterscheidung repeat-schleife while-schleife for-schleife

Mehr

Äquivalente Grammatiken / attributierte Grammatik

Äquivalente Grammatiken / attributierte Grammatik Äquivalente Grammatiken / attributierte Grammatik Linksfaktorisierung Elimination von Linksrekursion Umwandlung von EBNF in BNF Attributierte Grammatik Semantikfunktionen und Übersetzungsschema Synthetisierte,

Mehr

Einführung in die Java- Programmierung

Einführung in die Java- Programmierung Einführung in die Java- Programmierung Dr. Volker Riediger Tassilo Horn riediger horn@uni-koblenz.de WiSe 2012/13 1 Wichtig... Mittags keine Pommes... Praktikum A 230 C 207 (Madeleine + Esma) F 112 F 113

Mehr

1 Syntax von Programmiersprachen

1 Syntax von Programmiersprachen 1 Syntax von Programmiersprachen Syntax ( Lehre vom Satzbau ): formale Beschreibung des Aufbaus der Worte und Sätze, die zu einer Sprache gehören; im Falle einer Programmier-Sprache Festlegung, wie Programme

Mehr

Motivation. Formale Grundlagen der Informatik 1 Kapitel 5 Kontextfreie Sprachen. Informales Beispiel. Informales Beispiel.

Motivation. Formale Grundlagen der Informatik 1 Kapitel 5 Kontextfreie Sprachen. Informales Beispiel. Informales Beispiel. Kontextfreie Kontextfreie Motivation Formale rundlagen der Informatik 1 Kapitel 5 Kontextfreie Sprachen Bisher hatten wir Automaten, die Wörter akzeptieren Frank Heitmann heitmann@informatik.uni-hamburg.de

Mehr

Objektorientierte Programmierung. Kapitel 12: Interfaces

Objektorientierte Programmierung. Kapitel 12: Interfaces 12. Interfaces 1/14 Objektorientierte Programmierung Kapitel 12: Interfaces Stefan Brass Martin-Luther-Universität Halle-Wittenberg Wintersemester 2012/13 http://www.informatik.uni-halle.de/ brass/oop12/

Mehr

Programmiersprachen und Übersetzer

Programmiersprachen und Übersetzer Programmiersprachen und Übersetzer Sommersemester 2010 19. April 2010 Theoretische Grundlagen Problem Wie kann man eine unendliche Menge von (syntaktisch) korrekten Programmen definieren? Lösung Wie auch

Mehr

Objektorientierte Programmierung

Objektorientierte Programmierung Universität der Bundeswehr Fakultät für Informatik Institut 2 Priv.-Doz. Dr. Lothar Schmitz FT 2006 Zusatzaufgaben Lösungsvorschlag Objektorientierte Programmierung Lösung 22 (Java und UML-Klassendiagramm)

Mehr

Java: Vererbung. Teil 3: super() www.informatikzentrale.de

Java: Vererbung. Teil 3: super() www.informatikzentrale.de Java: Vererbung Teil 3: super() Konstruktor und Vererbung Kindklasse ruft SELBSTSTÄNDIG und IMMER zuerst den Konstruktor der Elternklasse auf! Konstruktor und Vererbung Kindklasse ruft SELBSTSTÄNDIG und

Mehr

Programmieren in Java

Programmieren in Java Programmieren in Java objektorientierte Programmierung 2 2 Zusammenhang Klasse-Datei In jeder *.java Datei kann es genau eine public-klasse geben wobei Klassen- und Dateiname übereinstimmen. Es können

Mehr

Institut für Programmierung und Reaktive Systeme 25. August 2014. Programmier-Labor. 04. + 05. Übungsblatt. int binarysearch(int[] a, int x),

Institut für Programmierung und Reaktive Systeme 25. August 2014. Programmier-Labor. 04. + 05. Übungsblatt. int binarysearch(int[] a, int x), Technische Universität Braunschweig Dr. Werner Struckmann Institut für Programmierung und Reaktive Systeme 25. August 2014 Programmier-Labor 04. + 05. Übungsblatt Aufgabe 21: a) Schreiben Sie eine Methode

Mehr

Programmierkurs Java

Programmierkurs Java Programmierkurs Java Dr. Dietrich Boles Aufgaben zu UE16-Rekursion (Stand 09.12.2011) Aufgabe 1: Implementieren Sie in Java ein Programm, das solange einzelne Zeichen vom Terminal einliest, bis ein #-Zeichen

Mehr

Das Typsystem von Scala. L. Piepmeyer: Funktionale Programmierung - Das Typsystem von Scala

Das Typsystem von Scala. L. Piepmeyer: Funktionale Programmierung - Das Typsystem von Scala Das Typsystem von Scala 1 Eigenschaften Das Typsystem von Scala ist statisch, implizit und sicher 2 Nichts Primitives Alles ist ein Objekt, es gibt keine primitiven Datentypen scala> 42.hashCode() res0:

Mehr

2.11 Kontextfreie Grammatiken und Parsebäume

2.11 Kontextfreie Grammatiken und Parsebäume 2.11 Kontextfreie Grammatiken und Parsebäume Beispiel: Beispiel (Teil 3): Beweis für L(G) L: Alle Strings aus L der Länge 0 und 2 sind auch in L(G). Als Induktionsannahme gehen wir davon aus, dass alle

Mehr

1 Mathematische Grundlagen

1 Mathematische Grundlagen Mathematische Grundlagen - 1-1 Mathematische Grundlagen Der Begriff der Menge ist einer der grundlegenden Begriffe in der Mathematik. Mengen dienen dazu, Dinge oder Objekte zu einer Einheit zusammenzufassen.

Mehr

1. Formale Sprachen 1.2 Grammatiken formaler Sprachen

1. Formale Sprachen 1.2 Grammatiken formaler Sprachen 1. Formale Sprachen 1.2 Grammatiken formaler Sprachen Die Regeln zur Bildung korrekter Wörter einer Sprache kann man in einer natürlichen Sprache formulieren. Da dies jedoch wieder Mehrdeutigkeiten mit

Mehr

Der Aufruf von DM_in_Euro 1.40 sollte die Ausgabe 1.40 DM = 0.51129 Euro ergeben.

Der Aufruf von DM_in_Euro 1.40 sollte die Ausgabe 1.40 DM = 0.51129 Euro ergeben. Aufgabe 1.30 : Schreibe ein Programm DM_in_Euro.java zur Umrechnung eines DM-Betrags in Euro unter Verwendung einer Konstanten für den Umrechnungsfaktor. Das Programm soll den DM-Betrag als Parameter verarbeiten.

Mehr

Objektorientierte Programmierung. Kapitel 3: Syntaxdiagramme und Grammatikregeln

Objektorientierte Programmierung. Kapitel 3: Syntaxdiagramme und Grammatikregeln Stefan Brass: OOP (Java), 3. Syntaxdiagramme und Grammatikregeln 1/32 Objektorientierte Programmierung Kapitel 3: Syntaxdiagramme und Grammatikregeln Stefan Brass Martin-Luther-Universität Halle-Wittenberg

Mehr

Computeranwendung und Programmierung (CuP)

Computeranwendung und Programmierung (CuP) Computeranwendung und Programmierung (CuP) VO: Peter Auer (Informationstechnologie) UE: Norbert Seifter (Angewandet Mathematik) Organisatorisches (Vorlesung) Vorlesungszeiten Montag 11:15 12:45 Freitag

Mehr

SWE1 / Übung 2 (19.10.2011)

SWE1 / Übung 2 (19.10.2011) SWE1 / Übung 2 (19.1.211) Simulation von Algorithmen Testen, Testplan Beispiel arithmetische Ausdrücke Handsimulation von Algorithmen Man versteht einen Algorithmus (insbesonders einen "Fremden"), wenn

Mehr

Objektorientierte Programmierung für Anfänger am Beispiel PHP

Objektorientierte Programmierung für Anfänger am Beispiel PHP Objektorientierte Programmierung für Anfänger am Beispiel PHP Johannes Mittendorfer http://jmittendorfer.hostingsociety.com 19. August 2012 Abstract Dieses Dokument soll die Vorteile der objektorientierten

Mehr

Einführung in. Logische Schaltungen

Einführung in. Logische Schaltungen Einführung in Logische Schaltungen 1/7 Inhaltsverzeichnis 1. Einführung 1. Was sind logische Schaltungen 2. Grundlegende Elemente 3. Weitere Elemente 4. Beispiel einer logischen Schaltung 2. Notation von

Mehr

5 DATEN. 5.1. Variablen. Variablen können beliebige Werte zugewiesen und im Gegensatz zu

5 DATEN. 5.1. Variablen. Variablen können beliebige Werte zugewiesen und im Gegensatz zu Daten Makro + VBA effektiv 5 DATEN 5.1. Variablen Variablen können beliebige Werte zugewiesen und im Gegensatz zu Konstanten jederzeit im Programm verändert werden. Als Variablen können beliebige Zeichenketten

Mehr

Innere Klassen in Java

Innere Klassen in Java Innere Klassen in Java SS 2012 Prof. Dr. Margarita Esponda Innere Klassen Klassen- oder Interfacedefinitionen können zur besseren Strukturierung von Programmen verschachtelt werden Eine "Inner Class" wird

Mehr

Folge 19 - Bäume. 19.1 Binärbäume - Allgemeines. Grundlagen: Ulrich Helmich: Informatik 2 mit BlueJ - Ein Kurs für die Stufe 12

Folge 19 - Bäume. 19.1 Binärbäume - Allgemeines. Grundlagen: Ulrich Helmich: Informatik 2 mit BlueJ - Ein Kurs für die Stufe 12 Grundlagen: Folge 19 - Bäume 19.1 Binärbäume - Allgemeines Unter Bäumen versteht man in der Informatik Datenstrukturen, bei denen jedes Element mindestens zwei Nachfolger hat. Bereits in der Folge 17 haben

Mehr

Kapitel 2. Methoden zur Beschreibung von Syntax

Kapitel 2. Methoden zur Beschreibung von Syntax 1 Kapitel 2 Methoden zur Beschreibung von Syntax Grammatik, die sogar Könige zu kontrollieren weiß... aus Molière, Les Femmes Savantes (1672), 2. Akt 2 Ziele Zwei Standards zur Definition der Syntax von

Mehr

Binäre Bäume. 1. Allgemeines. 2. Funktionsweise. 2.1 Eintragen

Binäre Bäume. 1. Allgemeines. 2. Funktionsweise. 2.1 Eintragen Binäre Bäume 1. Allgemeines Binäre Bäume werden grundsätzlich verwendet, um Zahlen der Größe nach, oder Wörter dem Alphabet nach zu sortieren. Dem einfacheren Verständnis zu Liebe werde ich mich hier besonders

Mehr

Persönliche Zukunftsplanung mit Menschen, denen nicht zugetraut wird, dass sie für sich selbst sprechen können Von Susanne Göbel und Josef Ströbl

Persönliche Zukunftsplanung mit Menschen, denen nicht zugetraut wird, dass sie für sich selbst sprechen können Von Susanne Göbel und Josef Ströbl Persönliche Zukunftsplanung mit Menschen, denen nicht zugetraut Von Susanne Göbel und Josef Ströbl Die Ideen der Persönlichen Zukunftsplanung stammen aus Nordamerika. Dort werden Zukunftsplanungen schon

Mehr

4. AuD Tafelübung T-C3

4. AuD Tafelübung T-C3 4. AuD Tafelübung T-C3 Simon Ruderich 17. November 2010 Arrays Unregelmäßige Arrays i n t [ ] [ ] x = new i n t [ 3 ] [ 4 ] ; x [ 2 ] = new i n t [ 2 ] ; for ( i n t i = 0; i < x. l e n g t h ; i ++) {

Mehr

Fachgebiet Informationssysteme Prof. Dr.-Ing. N. Fuhr. Programmierung Prof. Dr.-Ing. Nobert Fuhr. Übungsblatt Nr. 6

Fachgebiet Informationssysteme Prof. Dr.-Ing. N. Fuhr. Programmierung Prof. Dr.-Ing. Nobert Fuhr. Übungsblatt Nr. 6 Gudrun Fischer Sascha Kriewel programmierung@is.informatik.uni-duisburg.de Anmeldung zur Klausur! Übungsblatt Nr. 6 Um an der Klausur teilzunehmen, müssen sich Studierende der angewandten Informatik in

Mehr

Einführung in die objektorientierte Programmierung mit Java. Klausur am 19. Oktober 2005

Einführung in die objektorientierte Programmierung mit Java. Klausur am 19. Oktober 2005 Einführung in die objektorientierte Programmierung mit Java Klausur am 19. Oktober 2005 Matrikelnummer: Nachname: Vorname: Semesteranzahl: Die Klausur besteht aus drei Frageblöcken zu den Inhalten der

Mehr

Grundlagen der Theoretischen Informatik, SoSe 2008

Grundlagen der Theoretischen Informatik, SoSe 2008 1. Aufgabenblatt zur Vorlesung Grundlagen der Theoretischen Informatik, SoSe 2008 (Dr. Frank Hoffmann) Lösung von Manuel Jain und Benjamin Bortfeldt Aufgabe 2 Zustandsdiagramme (6 Punkte, wird korrigiert)

Mehr

4. AUSSAGENLOGIK: SYNTAX. Der Unterschied zwischen Objektsprache und Metasprache lässt sich folgendermaßen charakterisieren:

4. AUSSAGENLOGIK: SYNTAX. Der Unterschied zwischen Objektsprache und Metasprache lässt sich folgendermaßen charakterisieren: 4. AUSSAGENLOGIK: SYNTAX 4.1 Objektsprache und Metasprache 4.2 Gebrauch und Erwähnung 4.3 Metavariablen: Verallgemeinerndes Sprechen über Ausdrücke von AL 4.4 Die Sprache der Aussagenlogik 4.5 Terminologie

Mehr

Was ist ein Compiler?

Was ist ein Compiler? Was ist ein Compiler? Was ist ein Compiler und worum geht es? Wie ist ein Compiler aufgebaut? Warum beschäftigen wir uns mit Compilerbau? Wie ist die Veranstaltung organisiert? Was interessiert Sie besonders?

Mehr

Einführung in die Programmierung

Einführung in die Programmierung : Inhalt Einführung in die Programmierung Wintersemester 2008/09 Prof. Dr. Günter Rudolph Lehrstuhl für Algorithm Engineering Fakultät für Informatik TU Dortmund - mit / ohne Parameter - mit / ohne Rückgabewerte

Mehr

50. Mathematik-Olympiade 2. Stufe (Regionalrunde) Klasse 11 13. 501322 Lösung 10 Punkte

50. Mathematik-Olympiade 2. Stufe (Regionalrunde) Klasse 11 13. 501322 Lösung 10 Punkte 50. Mathematik-Olympiade. Stufe (Regionalrunde) Klasse 3 Lösungen c 00 Aufgabenausschuss des Mathematik-Olympiaden e.v. www.mathematik-olympiaden.de. Alle Rechte vorbehalten. 503 Lösung 0 Punkte Es seien

Mehr

Vererbung & Schnittstellen in C#

Vererbung & Schnittstellen in C# Vererbung & Schnittstellen in C# Inhaltsübersicht - Vorüberlegung - Vererbung - Schnittstellenklassen - Zusammenfassung 1 Vorüberlegung Wozu benötigt man Vererbung überhaubt? 1.Um Zeit zu sparen! Verwendung

Mehr

Kapitel 4 Die Datenbank Kuchenbestellung Seite 1

Kapitel 4 Die Datenbank Kuchenbestellung Seite 1 Kapitel 4 Die Datenbank Kuchenbestellung Seite 1 4 Die Datenbank Kuchenbestellung In diesem Kapitel werde ich die Theorie aus Kapitel 2 Die Datenbank Buchausleihe an Hand einer weiteren Datenbank Kuchenbestellung

Mehr

4. Jeder Knoten hat höchstens zwei Kinder, ein linkes und ein rechtes.

4. Jeder Knoten hat höchstens zwei Kinder, ein linkes und ein rechtes. Binäre Bäume Definition: Ein binärer Baum T besteht aus einer Menge von Knoten, die durch eine Vater-Kind-Beziehung wie folgt strukturiert ist: 1. Es gibt genau einen hervorgehobenen Knoten r T, die Wurzel

Mehr

10 Erweiterung und Portierung

10 Erweiterung und Portierung 10.1 Überblick In vielen Fällen werden Compiler nicht vollständig neu geschrieben, sondern von einem Rechnersystem auf ein anderes portiert. Das spart viel Arbeit, ist aber immer noch eine sehr anspruchsvolle

Mehr

Die Gleichung A x = a hat für A 0 die eindeutig bestimmte Lösung. Für A=0 und a 0 existiert keine Lösung.

Die Gleichung A x = a hat für A 0 die eindeutig bestimmte Lösung. Für A=0 und a 0 existiert keine Lösung. Lineare Gleichungen mit einer Unbekannten Die Grundform der linearen Gleichung mit einer Unbekannten x lautet A x = a Dabei sind A, a reelle Zahlen. Die Gleichung lösen heißt, alle reellen Zahlen anzugeben,

Mehr

Folge 18 - Vererbung

Folge 18 - Vererbung Workshop Folge 18 - Vererbung 18.1 Ein einfacher Fall der Vererbung Schritt 1 - Vorbereitungen Besorgen Sie sich - vielleicht aus einer der Übungen der Folge 17 - ein fertiges und lauffähiges Listenprojekt,

Mehr

Studentische Lösung zum Übungsblatt Nr. 7

Studentische Lösung zum Übungsblatt Nr. 7 Studentische Lösung zum Übungsblatt Nr. 7 Aufgabe 1) Dynamische Warteschlange public class UltimateOrderQueue private Order[] inhalt; private int hinten; // zeigt auf erstes freies Element private int

Mehr

Erweiterung der Aufgabe. Die Notenberechnung soll nicht nur für einen Schüler, sondern für bis zu 35 Schüler gehen:

Erweiterung der Aufgabe. Die Notenberechnung soll nicht nur für einen Schüler, sondern für bis zu 35 Schüler gehen: VBA Programmierung mit Excel Schleifen 1/6 Erweiterung der Aufgabe Die Notenberechnung soll nicht nur für einen Schüler, sondern für bis zu 35 Schüler gehen: Es müssen also 11 (B L) x 35 = 385 Zellen berücksichtigt

Mehr

Grundlagen der Programmierung Prof. H. Mössenböck. 14. Schrittweise Verfeinerung

Grundlagen der Programmierung Prof. H. Mössenböck. 14. Schrittweise Verfeinerung Grundlagen der Programmierung Prof. H. Mössenböck 14. Schrittweise Verfeinerung Entwurfsmethode für Algorithmen Wie kommt man von der Aufgabenstellung zum Programm? Beispiel geg.: Text aus Wörtern ges.:

Mehr

Stellen Sie bitte den Cursor in die Spalte B2 und rufen die Funktion Sverweis auf. Es öffnet sich folgendes Dialogfenster

Stellen Sie bitte den Cursor in die Spalte B2 und rufen die Funktion Sverweis auf. Es öffnet sich folgendes Dialogfenster Es gibt in Excel unter anderem die so genannten Suchfunktionen / Matrixfunktionen Damit können Sie Werte innerhalb eines bestimmten Bereichs suchen. Als Beispiel möchte ich die Funktion Sverweis zeigen.

Mehr

Java 7. Elmar Fuchs Grundlagen Programmierung. 1. Ausgabe, Dezember 2011 JAV7

Java 7. Elmar Fuchs Grundlagen Programmierung. 1. Ausgabe, Dezember 2011 JAV7 Java 7 Elmar Fuchs Grundlagen Programmierung 1. Ausgabe, Dezember 2011 JAV7 5 Java 7 - Grundlagen Programmierung 5 Kontrollstrukturen In diesem Kapitel erfahren Sie wie Sie die Ausführung von von Bedingungen

Mehr

368 4 Algorithmen und Datenstrukturen

368 4 Algorithmen und Datenstrukturen Kap04.fm Seite 368 Dienstag, 7. September 2010 1:51 13 368 4 Algorithmen und Datenstrukturen Java-Klassen Die ist die Klasse Object, ein Pfeil von Klasse A nach Klasse B bedeutet Bextends A, d.h. B ist

Mehr

Verhindert, dass eine Methode überschrieben wird. public final int holekontostand() {...} public final class Girokonto extends Konto {...

Verhindert, dass eine Methode überschrieben wird. public final int holekontostand() {...} public final class Girokonto extends Konto {... PIWIN I Kap. 8 Objektorientierte Programmierung - Vererbung 31 Schlüsselwort: final Verhindert, dass eine Methode überschrieben wird public final int holekontostand() {... Erben von einer Klasse verbieten:

Mehr

Ein Blick voraus. des Autors von C++: Bjarne Stroustrup. 04.06.2005 Conrad Kobsch

Ein Blick voraus. des Autors von C++: Bjarne Stroustrup. 04.06.2005 Conrad Kobsch Ein Blick voraus des Autors von C++: Bjarne Stroustrup 04.06.2005 Conrad Kobsch Inhalt Einleitung Rückblick Nur eine Übergangslösung? Was würde C++ effektiver machen? Quelle 2 Einleitung Wo steht C++,

Mehr

Datentypen. Agenda für heute, 4. März, 2010. Pascal ist eine streng typisierte Programmiersprache

Datentypen. Agenda für heute, 4. März, 2010. Pascal ist eine streng typisierte Programmiersprache Agenda für heute, 4. März, 2010 Zusammengesetzte if-then-else-anweisungen Datentypen Pascal ist eine streng typisierte Programmiersprache Für jeden Speicherplatz muss ein Datentyp t (Datenformat) t) definiert

Mehr

IT-Basics 2. DI Gerhard Fließ

IT-Basics 2. DI Gerhard Fließ IT-Basics 2 DI Gerhard Fließ Wer bin ich? DI Gerhard Fließ Telematik Studium an der TU Graz Softwareentwickler XiTrust www.xitrust.com www.tugraz.at Worum geht es? Objektorientierte Programmierung Konzepte

Mehr

Professionelle Seminare im Bereich MS-Office

Professionelle Seminare im Bereich MS-Office Der Name BEREICH.VERSCHIEBEN() ist etwas unglücklich gewählt. Man kann mit der Funktion Bereiche zwar verschieben, man kann Bereiche aber auch verkleinern oder vergrößern. Besser wäre es, die Funktion

Mehr

Software Engineering Klassendiagramme Assoziationen

Software Engineering Klassendiagramme Assoziationen Software Engineering Klassendiagramme Assoziationen Prof. Adrian A. Müller, PMP, PSM 1, CSM Fachbereich Informatik und Mikrosystemtechnik 1 Lesen von Multiplizitäten (1) Multiplizitäten werden folgendermaßen

Mehr

Zwischenablage (Bilder, Texte,...)

Zwischenablage (Bilder, Texte,...) Zwischenablage was ist das? Informationen über. die Bedeutung der Windows-Zwischenablage Kopieren und Einfügen mit der Zwischenablage Vermeiden von Fehlern beim Arbeiten mit der Zwischenablage Bei diesen

Mehr

Kapitel 6. Vererbung

Kapitel 6. Vererbung Kapitel 6 Vererbung Vererbung 1 Ziele Das Vererbungsprinzip der objektorientierten Programmierung verstehen Und in Java umsetzen können Insbesondere folgende Begriffe verstehen und anwenden können: Ober/Unterklassen

Mehr

Klassenbeziehungen & Vererbung

Klassenbeziehungen & Vererbung Klassenbeziehungen & Vererbung VL Objektorientierte Programmierung Raimund Kirner teilweise nach Folien von Franz Puntigam, TU Wien Überblick Arten von Klassenbeziehungen Untertypen versus Vererbung in

Mehr

Binäre Bäume Darstellung und Traversierung

Binäre Bäume Darstellung und Traversierung Binäre Bäume Darstellung und Traversierung Name Frank Bollwig Matrikel-Nr. 2770085 E-Mail fb641378@inf.tu-dresden.de Datum 15. November 2001 0. Vorbemerkungen... 3 1. Terminologie binärer Bäume... 4 2.

Mehr

Ausarbeitung des Interpreter Referats

Ausarbeitung des Interpreter Referats Ausarbeitung des Interpreter Referats Gliederung 1. Programmiersprache 1.2. Syntax 1.2.1. Konkrete Syntax 1.2.2. Abstrakter Syntax Baum (Abstrakte Syntax) 2. Parser 2.1. Syntaktische Struktur einer Sprache

Mehr

Übungsblatt 3: Algorithmen in Java & Grammatiken

Übungsblatt 3: Algorithmen in Java & Grammatiken Humboldt-Universität zu Berlin Grundlagen der Programmierung (Vorlesung von Prof. Bothe) Institut für Informatik WS 15/16 Übungsblatt 3: Algorithmen in Java & Grammatiken Abgabe: bis 9:00 Uhr am 30.11.2015

Mehr

Typumwandlungen bei Referenztypen

Typumwandlungen bei Referenztypen Typumwandlungen bei Referenztypen Genau wie es bei einfachen Typen Typumwandlungen gibt, gibt es auch bei Referenztypen Umwandlungen von einem Referenztypen in einen anderen Referenztypen, die wie bei

Mehr

Formeln. Signatur. aussagenlogische Formeln: Aussagenlogische Signatur

Formeln. Signatur. aussagenlogische Formeln: Aussagenlogische Signatur Signatur Formeln Am Beispiel der Aussagenlogik erklären wir schrittweise wichtige Elemente eines logischen Systems. Zunächst benötigt ein logisches System ein Vokabular, d.h. eine Menge von Namen, die

Mehr

In diesem Thema lernen wir die Grundlagen der Datenbanken kennen und werden diese lernen einzusetzen. Access. Die Grundlagen der Datenbanken.

In diesem Thema lernen wir die Grundlagen der Datenbanken kennen und werden diese lernen einzusetzen. Access. Die Grundlagen der Datenbanken. In diesem Thema lernen wir die Grundlagen der Datenbanken kennen und werden diese lernen einzusetzen. Access Die Grundlagen der Datenbanken kurspc15 Inhaltsverzeichnis Access... Fehler! Textmarke nicht

Mehr

Programmieren in C. Macros, Funktionen und modulare Programmstruktur. Prof. Dr. Nikolaus Wulff

Programmieren in C. Macros, Funktionen und modulare Programmstruktur. Prof. Dr. Nikolaus Wulff Programmieren in C Macros, Funktionen und modulare Programmstruktur Prof. Dr. Nikolaus Wulff Der C Präprozessor Vor einem Compile Lauf werden alle Präprozessor Kommandos/Makros ausgewertet. Diese sind

Mehr

Informationsblatt Induktionsbeweis

Informationsblatt Induktionsbeweis Sommer 015 Informationsblatt Induktionsbeweis 31. März 015 Motivation Die vollständige Induktion ist ein wichtiges Beweisverfahren in der Informatik. Sie wird häufig dazu gebraucht, um mathematische Formeln

Mehr

5.5.8 Öffentliche und private Eigenschaften

5.5.8 Öffentliche und private Eigenschaften 5.5.8 Öffentliche und private Eigenschaften Schnittstellen vs. Implementierungen: Schnittstelle einer Klasse beschreibt, was eine Klasse leistet und wie sie benutzt werden kann, ohne dass ihre Implementierung

Mehr

1 topologisches Sortieren

1 topologisches Sortieren Wolfgang Hönig / Andreas Ecke WS 09/0 topologisches Sortieren. Überblick. Solange noch Knoten vorhanden: a) Suche Knoten v, zu dem keine Kante führt (Falls nicht vorhanden keine topologische Sortierung

Mehr

5. Tutorium zu Programmieren

5. Tutorium zu Programmieren 5. Tutorium zu Programmieren Dennis Ewert Gruppe 6 Universität Karlsruhe Institut für Programmstrukturen und Datenorganisation (IPD) Lehrstuhl Programmierparadigmen WS 2008/2009 c 2008 by IPD Snelting

Mehr

Kapitel 4. Einführung in den Scannergenerator Flex. Einführung in den Scannergenerator Flex Wintersemester 2008/09 1 / 9

Kapitel 4. Einführung in den Scannergenerator Flex. Einführung in den Scannergenerator Flex Wintersemester 2008/09 1 / 9 Kapitel 4 Einführung in den Scannergenerator Flex Einführung in den Scannergenerator Flex Wintersemester 2008/09 1 / 9 Generatoren für die lexikalische Analyse Scannergeneratoren werden eingesetzt um die

Mehr

Das Leitbild vom Verein WIR

Das Leitbild vom Verein WIR Das Leitbild vom Verein WIR Dieses Zeichen ist ein Gütesiegel. Texte mit diesem Gütesiegel sind leicht verständlich. Leicht Lesen gibt es in drei Stufen. B1: leicht verständlich A2: noch leichter verständlich

Mehr

Der lokale und verteilte Fall

Der lokale und verteilte Fall Lokale Beans Der lokale und verteilte Fall RemoteClient Lokaler Client (JSP) RemoteSession/Entity-Bean Lokale Session/Entity-Bean 2 Lokale Beans Die bisher vorgestellten EJBswaren immer in der Lage auf

Mehr

Praktikum zu Einführung in die Informatik für LogWiIngs und WiMas Wintersemester 2015/16. Vorbereitende Aufgaben

Praktikum zu Einführung in die Informatik für LogWiIngs und WiMas Wintersemester 2015/16. Vorbereitende Aufgaben Praktikum zu Einführung in die Informatik für LogWiIngs und WiMas Wintersemester 2015/16 Fakultät für Informatik Lehrstuhl 14 Lars Hildebrand, Marcel Preuß, Iman Kamehkhosh, Marc Bury, Diana Howey Übungsblatt

Mehr

13 OOP MIT DELPHI. Records und Klassen Ein Vergleich

13 OOP MIT DELPHI. Records und Klassen Ein Vergleich 13 OOP MIT DELPHI Delphi war früher "Object Pascal". Dieser Name impliziert eine Funktionalität, welche in der Welt der Programmierung nicht mehr wegzudenken ist: die objektorientierte Programmierung,

Mehr

Programmierung 2. Übersetzer: Code-Erzeugung. Sebastian Hack. Klaas Boesche. Sommersemester 2012. hack@cs.uni-saarland.de. boesche@cs.uni-saarland.

Programmierung 2. Übersetzer: Code-Erzeugung. Sebastian Hack. Klaas Boesche. Sommersemester 2012. hack@cs.uni-saarland.de. boesche@cs.uni-saarland. 1 Programmierung 2 Übersetzer: Code-Erzeugung Sebastian Hack hack@cs.uni-saarland.de Klaas Boesche boesche@cs.uni-saarland.de Sommersemester 2012 Bytecodes Der Java Übersetzer erzeugt keine Maschinensprache

Mehr

Prüfung Computation, Programming

Prüfung Computation, Programming Prüfung Computation, Programming 1. Computation: Reguläre Ausdrücke [5 Punkte] Zusammenfassung reguläre Ausdrücke a Das Zeichen a. Ein beliebiges Zeichen [abc] Ein beliebiges Zeichen aus der Menge {a,

Mehr

Kapitel 2: Formale Sprachen Kontextfreie Sprachen. reguläre Grammatiken/Sprachen. kontextfreie Grammatiken/Sprachen

Kapitel 2: Formale Sprachen Kontextfreie Sprachen. reguläre Grammatiken/Sprachen. kontextfreie Grammatiken/Sprachen reguläre Grammatiken/prachen Beschreibung für Bezeichner in Programmiersprachen Beschreibung für wild cards in kriptsprachen (/* reguläre Ausdrücke */)?; [a-z]; * kontextfreie Grammatiken/prachen Beschreibung

Mehr

II. Grundlagen der Programmierung. 9. Datenstrukturen. Daten zusammenfassen. In Java (Forts.): In Java:

II. Grundlagen der Programmierung. 9. Datenstrukturen. Daten zusammenfassen. In Java (Forts.): In Java: Technische Informatik für Ingenieure (TIfI) WS 2005/2006, Vorlesung 9 II. Grundlagen der Programmierung Ekkart Kindler Funktionen und Prozeduren Datenstrukturen 9. Datenstrukturen Daten zusammenfassen

Mehr

4. BEZIEHUNGEN ZWISCHEN TABELLEN

4. BEZIEHUNGEN ZWISCHEN TABELLEN 4. BEZIEHUNGEN ZWISCHEN TABELLEN Zwischen Tabellen können in MS Access Beziehungen bestehen. Durch das Verwenden von Tabellen, die zueinander in Beziehung stehen, können Sie Folgendes erreichen: Die Größe

Mehr

Outlook. sysplus.ch outlook - mail-grundlagen Seite 1/8. Mail-Grundlagen. Posteingang

Outlook. sysplus.ch outlook - mail-grundlagen Seite 1/8. Mail-Grundlagen. Posteingang sysplus.ch outlook - mail-grundlagen Seite 1/8 Outlook Mail-Grundlagen Posteingang Es gibt verschiedene Möglichkeiten, um zum Posteingang zu gelangen. Man kann links im Outlook-Fenster auf die Schaltfläche

Mehr

Grammatiken in Prolog

Grammatiken in Prolog 12. Grammatiken in Prolog 12-1 Grammatiken in Prolog Allgemeines: Gedacht zur Verarbeitung natürlicher Sprache. Dort braucht man kompliziertere Grammatiken als etwa im Compilerbau, andererseits sind die

Mehr

L10N-Manager 3. Netzwerktreffen der Hochschulübersetzer/i nnen Mannheim 10. Mai 2016

L10N-Manager 3. Netzwerktreffen der Hochschulübersetzer/i nnen Mannheim 10. Mai 2016 L10N-Manager 3. Netzwerktreffen der Hochschulübersetzer/i nnen Mannheim 10. Mai 2016 Referentin: Dr. Kelly Neudorfer Universität Hohenheim Was wir jetzt besprechen werden ist eine Frage, mit denen viele

Mehr

Große Übung Praktische Informatik 1

Große Übung Praktische Informatik 1 Große Übung Praktische Informatik 1 2005-12-08 fuessler@informatik.uni-mannheim.de http://www.informatik.uni-mannheim.de/pi4/people/fuessler 1: Announcements / Orga Weihnachtsklausur zählt als Übungsblatt,

Mehr

Rundung und Casting von Zahlen

Rundung und Casting von Zahlen W E R K S T A T T Rundung und Casting von Zahlen Intrexx 7.0 1. Einleitung In diesem Werkstattbeitrag erfahren Sie, wie Zahlenwerte speziell in Velocity, aber auch in Groovy, gerundet werden können. Für

Mehr

Vorlesung Diskrete Strukturen Graphen: Wieviele Bäume?

Vorlesung Diskrete Strukturen Graphen: Wieviele Bäume? Vorlesung Diskrete Strukturen Graphen: Wieviele Bäume? Bernhard Ganter Institut für Algebra TU Dresden D-01062 Dresden bernhard.ganter@tu-dresden.de WS 2013/14 Isomorphie Zwei Graphen (V 1, E 1 ) und (V

Mehr

Wir machen neue Politik für Baden-Württemberg

Wir machen neue Politik für Baden-Württemberg Wir machen neue Politik für Baden-Württemberg Am 27. März 2011 haben die Menschen in Baden-Württemberg gewählt. Sie wollten eine andere Politik als vorher. Die Menschen haben die GRÜNEN und die SPD in

Mehr

Grammatiken. Einführung

Grammatiken. Einführung Einführung Beispiel: Die arithmetischen Ausdrücke über der Variablen a und den Operationen + und können wie folgt definiert werden: a, a + a und a a sind arithmetische Ausdrücke Wenn A und B arithmetische

Mehr

Einführung in die Algebra

Einführung in die Algebra Prof. Dr. H. Brenner Osnabrück SS 2009 Einführung in die Algebra Vorlesung 13 Einheiten Definition 13.1. Ein Element u in einem Ring R heißt Einheit, wenn es ein Element v R gibt mit uv = vu = 1. DasElementv

Mehr

Grundlagen der Programmierung Prof. H. Mössenböck. 3. Verzweigungen

Grundlagen der Programmierung Prof. H. Mössenböck. 3. Verzweigungen Grundlagen der Programmierung Prof. H. Mössenböck 3. Verzweigungen If-Anweisung n > 0? j n if (n > 0) x = x / n; ohne else-zweig x x / n j max x x > y? n max y if (x > y) max = x; else max = y; mit else-zweig

Mehr

affilinet_ Flash-Spezifikationen

affilinet_ Flash-Spezifikationen affilinet_ Flash-Spezifikationen Inhaltsverzeichnis Allgemeines...2 Klickzählung...2 Lead/Sale Programme... 2 PPC und Kombi Programme...3 Übergabe von Formulardaten...4 clicktag Variante Sale/Lead Programm...4

Mehr

Graphic Coding. Klausur. 9. Februar 2007. Kurs A

Graphic Coding. Klausur. 9. Februar 2007. Kurs A Graphic Coding Klausur 9. Februar 2007 Kurs A Name: Matrikelnummer: Hinweise - Es sind keine Hilfsmaterialien erlaubt. (Keine Bücher, Taschenrechner, Handys) - Sie haben zwei Stunden Zeit. - Insgesamt

Mehr

BEISPIELKLAUSUR Softwareentwicklung:

BEISPIELKLAUSUR Softwareentwicklung: Prof. Dr. Andreas Fink Institut für Informatik Fakultät für Wirtschafts- und Sozialwissenschaften Helmut-Schmidt-Universität / Universität der Bundeswehr Hamburg BEISPIELKLAUSUR Softwareentwicklung: Objektorientierte

Mehr

25 kann ohne Rest durch 5 geteilt werden! ist wahr

25 kann ohne Rest durch 5 geteilt werden! ist wahr Lehrbrief 2: Lektion 8 - C -Praxis 4-1 - 5.2 Einfache Entscheidungen mit if und die Vergleichsoperatoren Nun tauchen wir immer tiefer in die Geheimnisse von C ein und beschäftigen uns mit einem sehr wichtigen

Mehr