1 - Backus-Naur-Form. (a) Ableitung des Wortes /a*(a b)b/ aus der gegebenen BNF:

Ähnliche Dokumente
1 - Semantik von Ausdrücken

C++ Teil 2. Sven Groß. 16. Apr IGPM, RWTH Aachen. Sven Groß (IGPM, RWTH Aachen) C++ Teil Apr / 22

einlesen n > 0? Ausgabe Negative Zahl

Java - Schleifen. Bedingung. wiederhole. Anweisung Anweisung Anweisung. Leibniz Universität IT Services Anja Aue

float: Fließkommazahl nach IEEE 754 Standard mit 32 bit

Programmierkurs Java

Java: Eine kurze Einführung an Beispielen

Klausur "ADP" SS 2015

JavaScript. Dies ist normales HTML. Hallo Welt! Dies ist JavaScript. Wieder normales HTML.

Probeklausur: Programmierung WS04/05

Programmieren I. Kapitel 5. Kontrollfluss

Informatik I. 19. Schleifen und Iteration für verlinkte Listen. Jan-Georg Smaus. Albert-Ludwigs-Universität Freiburg. 27.

= 7 (In Binärdarstellung: = 0111; Unterlauf) = -8 (In Binärdarstellung: = 1000; Überlauf)

Algorithmen und ihre Programmierung -Teil 2-

Programmierung in Python

Institut für Programmierung und Reaktive Systeme. Java 3. Markus Reschke

Die for -Schleife HEUTE. Schleifen. Arrays. Schleifen in JAVA. while, do reichen aus, um alle iterativen Algorithmen zu beschreiben

Wie entwerfe ich ein Programm?

Einführung in die Computerlinguistik

Einführung in die Programmierung für NF. Rückgabewerte, EBNF, Fallunterscheidung, Schleifen

1 Aufgaben 1.1 Objektorientiert: ("extended-hamster") Sammel-Hamster

Nachklausur Bitte in Druckschrift leserlich ausfüllen!

Operatoren für elementare Datentypen Bedingte Anweisungen Schleifen. Operatoren für elementare Datentypen Bedingte Anweisungen Schleifen

f 1 (n) = log(n) + n 2 n 5 f 2 (n) = n 3 + n 2 f 3 (n) = log(n 2 ) f 4 (n) = n n f 5 (n) = (log(n)) 2

Modul Entscheidungsunterstützung in der Logistik. Einführung in die Programmierung mit C++ Übung 2

Semantik von Programmiersprachen SS 2017

Aufgabe 8. 1 Arbeitsweise illustrieren. 2 Korrektheitsbeweis führen. 3 Laufzeitanalyse durchführen.

Vorkurs Informatik WiSe 16/17

Vorlesung Programmieren

pue13 January 28, 2017

3. Grundanweisungen in Java

Java programmieren mit JavaKara. Eine Zusammenfassung in Beispielen

Aufgabenblatt 5. Kompetenzstufe 1. Allgemeine Informationen zum Aufgabenblatt:

7. Einführung in C++ Programmieren / Algorithmen und Datenstrukturen 1 Prof. Dr. Bernhard Humm FB Informatik, Hochschule Darmstadt

Anweisungen zur Ablaufsteuerung

Das diesem Dokument zugrundeliegende Vorhaben wurde mit Mitteln des Bundesministeriums für Bildung und Forschung unter dem Förderkennzeichen

2. Grundlagen. Beschreibung von Algorithmen durch Pseudocode. Korrektheit von Algorithmen durch Invarianten.

II. Grundlagen der Programmierung. Beispiel: Merge Sort. Beispiel: Merge Sort (Forts. ) Beispiel: Merge Sort (Forts. )

Algorithmen implementieren. Implementieren von Algorithmen

Kapitel 12: Induktive

Einführung in das Programmieren Probeklausur Lösungen

Verschlüsseln eines Bildes. Visuelle Kryptographie. Verschlüsseln eines Bildes. Verschlüsseln eines Bildes

PostScript -Sprache. Frank Richter

Schleifen in C/C++/Java

Lösungsvorschlag Serie 2 Rekursion

Variablen. int Flugzeug. float. I write code Hund. String. long. Borchers: Programmierung für Alle (Java), WS 06/07 Kapitel

1 Aufgaben 1.1 Umgebungsvariable setzen: CLASSPATH

Repetitive Strukturen

2 Eine einfache Programmiersprache

Informatik B von Adrian Neumann

C# - Einführung in die Programmiersprache Arrays, Enumeration und Collections. Leibniz Universität IT Services Anja Aue

Einführung in die Informatik 1

! 1. Rekursive Algorithmen.! 2. Rekursive (dynamische) Datenstrukturen. II.3.2 Rekursive Datenstrukturen - 1 -

Elementare Konzepte von

Einführung in die Programmierung mit VBA

Grundlagen der Programmierung Prof. H. Mössenböck. 6. Methoden

Der Dreyfus-Wagner Algorithmus für das Steiner Baum Problem

Vorlesung Programmieren

3. Übungsblatt zu Algorithmen I im SoSe 2017

2 Teil 2: Nassi-Schneiderman

Organisatorisches. Neue Übungsblätter: Nur mehr elektronisch? Abgabe Di, , 14 Uhr bis Do, , 8Uhr

Organisatorisches. drei Gruppen Gruppe 1: 10:10-11:40, Gruppe 2: 11:45-13:15 Gruppe 3: 13:20-14:50

Algorithmen und Datenstrukturen

Grammatiken. Grammatiken sind regelbasierte Kalküle zur Konstruktion von Systemen und Sprachen Überprüfung von Systemen und Sprachen

Die Notwendigkeit für wiederholte Programmausführungen. Agenda für heute, 11. März, 2010

Programmierkurs Java

3.3. Rekursive Datentypen

Tutoraufgabe 1 (Sortieren): Lösung: Datenstrukturen und Algorithmen SS14 Lösung - Übung 4

Beispiele elementarer Datentypen Ganze Zahlen (integer) Unterbereiche Gleitkommazahlen Festkommazahlen

Arrays. Theorieteil. Inhaltsverzeichnis. Begriffe. Programmieren mit Java Modul 3. 1 Modulübersicht 3

Programmiersprache 1 (C++) Prof. Dr. Stefan Enderle NTA Isny

Beispiel 19. December 4, 2009

EINFÜHRUNG IN DIE PROGRAMMIERUNG

System.out.println("TEXT");

Vorlesung Objektorientierte Programmierung Klausur

Technische Universität Braunschweig Institut für Programmierung und Reaktive Systeme

Definition 4 (Operationen auf Sprachen) Beispiel 5. Seien A, B Σ zwei (formale) Sprachen. Konkatenation: AB = {uv ; u A, v B} A + = n 1 An

7. Syntax: Grammatiken, EBNF

Inhalt Kapitel 2: Rekursion

Algorithmische Bioinformatik 1

Grundlagen von C# - 2

FH D. Objektorientierte Programmierung in Java FH D FH D. Prof. Dr. Ing. André Stuhlsatz. Referenzen. Referenzen

Großübung zu Einführung in die Programmierung

Effiziente Java Programmierung

Matrikelnummer:

Abschnitt 11: Beispiel: Die Klasse String (Teil 1)

Funktionale Programmierung ALP I. µ-rekursive Funktionen WS 2012/2013. Prof. Dr. Margarita Esponda. Prof. Dr. Margarita Esponda

Kurs 1613 Einführung in die imperative Programmierung Musterlösung zur Klausur am

Tag 5. Repetitorium Informatik (Java) Dozent: Marius Kamp Lehrstuhl für Informatik 2 (Programmiersysteme)

Wuerfel - augenzahl: int + Wuerfel() + wuerfeln() + gibaugenzahl(): int

1 Stückweise konstante Funktionen (ca =10 Punkte)

Parsing regulärer Ausdrücke. Karin Haenelt

FHZ. K13 Rekursion. Lernziele. Hochschule Technik+Architektur Luzern Abteilung Informatik, Fach Programmieren. Inhalt

Java I Vorlesung 6 Referenz-Datentypen

Es ist für die Lösung der Programmieraufgabe nicht nötig, den mathematischen Hintergrund zu verstehen, es kann aber beim Verständnis helfen.

Einführung in die Informatik: Programmierung und Software-Entwicklung, WS 16/17. Kapitel 14. Bäume. Bäume 1

EINFÜHRUNG IN DIE PROGRAMMIERUNG

Einführung Datentypen Verzweigung Schleifen. Java Crashkurs. Kim-Manuel Klein May 4, 2015

Überblick und Wiederholung

Transkript:

1 - Backus-Naur-Form (a) Ableitung des Wortes /a*(a b)b/ aus der gegebenen BNF: R Regel R -> '/' S '/' => '/' S '/' Regel S -> E S => '/' E S '/' Regel E -> E '*' => '/' E '*' S '/' Regel E -> D => '/' D '*' S '/' Regel D -> 'a' => '/' 'a' '*' S '/' Regel S -> E S => '/' 'a' '*' E S '/' Regel E -> '(' E ' ' E ')' => '/' 'a' '*' '(' E ' ' E ')' S '/' Regel E -> D (2x) => '/' 'a' '*' '(' D ' ' D ')' S '/' Regel D -> 'a' => '/' 'a' '*' '(' 'a' ' ' D ')' S '/' Regel D -> 'b' => '/' 'a' '*' '(' 'a' ' ' 'b' ')' S '/' Regel S -> E => '/' 'a' '*' '(' 'a' ' ' 'b' ')' E '/' Regel E -> D => '/' 'a' '*' '(' 'a' ' ' 'b' ')' D '/' Regel D -> 'b' => '/' 'a' '*' '(' 'a' ' ' 'b' ')' 'b' '/' Ableitungsbaum für das Wort /a*(a b)b/: R +-------------------+---------------+ '/' S '/' +-------------+-----+ E S +-+-+ +-----+-----+ E '*' E S +--+---+---+---+ D '(' E ' ' E ')' E 'a' D D D 'a' 'b' 'b' (b) Definition einer BNF für Zeichenfolgen aus n Buchstaben gefolgt von n Ziffern (n > 0): Z ::= A B A Z B A ::= '0'... '9' B ::= 'a'... 'z' 1

2 - (6/8 ECTS) Termauswertung (a) Umwandlung von 5 ** 2 < 4 + val / 3 in Term-Baum-Darstellung: < / \ ** + / \ / \ 5 2 4 / / \ val 3 (b) Umwandlung des Ausdrucks 5 ** 2 < 4 + val / 3 in Postfix-Darstellung: 5 2 ** 4 val 3 / + < Auswertung des Ausdrucks in Postfix-Darstellung mittels Stack-Maschine, wobei val durch 7 ersetzt wurde: 5 2 ** 4 7 3 / + < => 5 2 ** 4 7 3 / + < => 5 2 ** 4 7 3 / + < => 25 4 7 3 / + < => 25 4 7 3 / + < => 25 4 7 3 / + < => 25 4 7 3 / + < => 25 4 2 + < => 25 6 < => false Das Ergebnis des Ausdrucks ist also der boolesche Wert false. 3 - (5 ECTS) Programmpunkttabelle PP n m r i j x Ausgabe # 1 2 # 2 3 # 3 1 # 4 1 # 5 0 # 6 1 # 7 1 # 6 2 # 7 2 # 8 # 9 2 # 4 2 # 5 0 # 6 1 # 7 2 # 6 2 # 7 4 # 8 2

PP n m r i j x Ausgabe # 9 4 #10 #11 4 (b) Das Programm berechnet n m in der Variablen r. In jedem Schritt der äußeren for-schleife berechnet die innere for-schleife x := n r und setzt dann r := x. Vor der Schleife (bei #3) gilt r = 1. Nach dem ersten Durchlauf (bei #9) gilt r = n 1 = n, nach dem zweiten Durchlauf r = n n = n 2 und nach dem m-ten entsprechend r = n n m 1 = n m. 4 - Methodenschreibweise 1. Indeed, z. B. für a = "Hallo", b ="Welt" 2. Ganz genau, z. B. für a = 38, b = 4 3. Nein, die Operation + ist auf booleschen Werten nicht definiert. 4. Nein, man kann auch einen String vervielfachen; * ist also auch eine Methode für Strings und Ganzzahlen, z. B. für a = "Ho", b = 3 5. Nein, c[2] ist ein Ausdruck; genau genommen ist es der Wert, der an Indexposition 2 im Array c oder im String c steht. 6. Richtig, hier wird das Element an Indexposition 2 von c (wobei c ein Array oder String ist) durch den Wert 42 ersetzt; genau genommen wird die mutierende Methode []=(x, y) auf einem Array/String aufrufen, die das Element an der Indexposition x durch den Wert von y ersetzt, in Methodenschreibweise: c.[]=(2, 42). 7. Ja, man kann die Zuweisung auch schreiben als a.[](1).[]=(3, 73): Der erste Methodenaufruf selektiert das erste Element des Arrays a. In diesem Fall muss dieser Wert wieder ein Array sein, damit die zweistellige Methode []=(x, y) aufgerufen werden kann, die dem Array an der Indexposition x den Wert y durch einen Seiteneffekt zuweist. Damit ist der zweite Methodenaufruf in der Tat mutierend, der erste jedoch nicht. 8. Ja, z. B. für b = [true, [0, 1, 2, 42]] 9. Nein, es ist natürlich genau anders herum; mutierende Funktionen werden durch ein Ausrufezeichen am Ende des Namens signalisiert. 10. Genau, der **-Operator ist rechtsassoziativ. 5 - Tabellensumme # Aufgabe 5 (a) # Iterative Variante def array_sum_odd(a) sum = 0; for index in 0.. a.size-1 do if a[index] % 2!= 0 then sum = sum + a[index]; return sum; 3

# Rekursive Variante def array_sum_odd(a) sum = 0; if a.size > 0 then if a[0] % 2!= 0 then sum = a[0]; sum = sum + array_sum_odd(a[1, a.size-1]); return sum; # Aufgabe 5 (b) # Iterative Variante def row_sum_odd(a) result = []; for index in 0.. a.size-1 do result[index] = array_sum_odd(a[index]); # Rekursive Variante def row_sum_odd(a) result = []; if a.size > 0 then result = [array_sum_odd(a[0])] + row_sum_odd(a[1, a.size-1]); # Aufgabe 5 (c) # Mutierende Variante def row_sum_odd!(a) for index in 0.. a.size-1 do a[index] = array_sum_odd(a[index]); 6 - Objektidentität Würde die *-Methode für i=1 keine Kopie erzeugen, sondern einfach dasselbe Objekt zurückliefern, so würde beim destruktiven Update y[0,1] = "b" dasjenige Objekt, auf das auch x zeigt, verändert. Das Programm würde sich also völlig anders verhalten, als für i=0 oder i>1, wo ja neue Objekte angelegt werden müssen, da diese nicht identisch aussehen. Beispiel: Für die Variablenbelegung i = 1 erhielte man, wenn * keine Kopie erzeugen würde: Zeile x y ------+--------------- 1 Obj1 2 Obj1 3 4

# Obj1 # # --------- # # String # # # # "a" PP 1 # # "b" PP 3 # Für die Variablenbelegung i = 2 erhält man dagegen (* erzeugt Kopie): Zeile x y ------+---------- 1 Obj1 2 Obj2 3 # Obj1 # # --------- # # String # # # # "a" (PP 1)# # Obj2 # # --------- # # String # # # # "aa" PP 2 # # "ba" PP 3 # 7 - (6/8 ECTS) Klassen und Objekte (a) Um von außen den Wert des Attributs @time zu lesen, wird eine Methode get_time hinzugefügt, die den Wert von @time zurückgibt: def get_time() return @time; (b) Die Methode to_s wird um eine Fallunterscheidung für die Stunden-Anzeige ergänzt. def to_s() time = @time; hours = 0; minutes = 0; seconds = 0; if time >= 3600 then hours = time / 3600; time = time % 3600; if time >= 60 then minutes = time / 60; 5

time = time % 60; if time > 0 then seconds = time; return hours.to_s + ":" + minutes.to_s + ":" + seconds.to_s; (c) Um eine echte Kopie eines Timer-Objekts zu erzeugen, wird zunächst ein neues Objekt erzeugt. Jetzt muss der Wert des @time-attribut des neuen Objekts copy auf denselben Wert gesetzt werden wie das @time-attribut des erzeugenden Objekts. Schreibzugriff von außen ist aber nicht erlaubt! Daher muss die Methode tick! verwenden werden, um den Wert des @count-attributs zu setzen. Die Methode wird solange für das Objekt copy aufgerufen, bis der gewünschte @time-wert erreicht ist (also @time mal): def clone() copy = Timer.new(); for i in 1.. @time copy.tick!(); return copy; 8 - (5 ECTS) Aufzählen und Überprüfen Eine Möglichkeit ist es, mit drei ineinander verschachtelten for-schleifen alle möglichen Werte für i, j und k durchzulaufen und jeweils zu prüfen, ob n = 2 i 3 j 5 k gilt. Die Werte laufen dabei jeweils von 0 bis m, wobei m = n vorgegeben ist: def is_hamming(n) result = false; m = Math.sqrt(n); for i in 0.. m do for j in 0.. m do for k in 0.. m do if 2**i * 3**j * 5**k == n then result = true; Eine weitere Möglichkeit besteht darin, mit drei ineinander verschachtelten while-schleifen die Variablen i, j und k aufzuzählen und die Aufzählung abzubrechen, sobald n = 2 i 3 j 5 k (n ist eine Hamming-Zahl) gilt. Diese Variante hat den Vorteil, dass nicht alle möglichen Werte aufgezählt werden müssen. def is_hamming(n) result = false; i = 0; while result == false && 2**i <= n do j = 0; while result == false && 2**i * 3*j <= n do k = 0; while 2**i * 3*j * 5**k < n do k = k + 1; 6

if 2**i * 3**j * 5**k == n then result = true; j = j + 1; i = i + 1; Die Variante mit der while-schleife lässt sich auch effizienter implementieren, indem nicht die Exponenten hochgezählt werden, sondern die potentielle Hamming-Zahl selbst (bzw. deren Faktoren x = 2 i, y = 2 i 3 j = x 3 j, z = 2 i 3 j 5 k = y 5 k ), also: def is_hamming(n) result = false; x = 1; while result == false && x <= n do y = x; while result == false && y <= n do z = y; while z < n do z = z * 5; if z == n then result = true; y = y * 3; x = x * 2; Anmerkung: Die Aufzählungsgrenze m = n in der Version mit for liefert für n = 8 das falsche Ergebnis. Besser wäre z.b. m = n (in Ruby: m = Math.sqrt(n).ceil) oder m = log 2 (n) (in Ruby: m = Math.log(n, 2).floor) zu verwenden, obwohl in beiden Fällen meist viel zu viele Zahlen getestet werden. Die optimimale Lösung wäre, für i von 0 bis log 2 (n) zu zählen, für j von 0 bis log 3 ( n 2 ) und für k von 0 i bis log 5 ( n 2 i 3 ) (in Ruby: Math.log(n, 2).floor, Math.log(n/(2**i), 3).floor und Math.log(n/(2**i j * 3**j), 5).floor). 7