Fraktale malen mit Lindemeyer- Grammatik

Werbung
Virtuelle Lehrerfortbildung Informatik Niedersachsen
Hans Peter Schneider
Fraktale malen mit LindemeyerGrammatik
Ein Parser für einen Stack-Automaten zur
Steuerung von Turtle-Grafiken
Eine virtuelle Unterrichtsreihe im Rahmen des
jahrgangsübergreifenden Informatikunterrichts der
Oberstufe an Gymnasien
Seite 2
Virtuelle Lehrerfortbildung Informatik Niedersachsen
Hans Peter Schneider
Ampelschaltung – Logik löten
Inhalt
Einordnung..................................................................................................... 3
Die Unterrichtsinhalte ...................................................................................... 3
Grammatiken formaler Sprachen ................................................................................... 4
Lindemeyer-Grammatiken ............................................................................................. 4
Turtle-Grafik ................................................................................................................. 5
Fraktale........................................................................................................................ 5
Das Projekt ..................................................................................................... 6
Turtle-Befehlsinterpreter ................................................................................................. 7
Parser für Lindemeyer-Grammatik .................................................................................. 8
Erweiterung um Stack-Befehle ...................................................................................... 11
Das Java-Programm LindeTurtle................................................................................... 12
Beispielprogramme ..................................................................................................... 13
Quellen ........................................................................................................ 13
Virtuelle Lehrerfortbildung Informatik Niedersachsen
Hans Peter Schneider
Ampelschaltung – Logik löten
Seite 3
Einordnung
Das Informatikprofil gehört am Gymnasium Lüneburger Heide zu den Aushängeschildern. Nach
dem für alle Schüler der 6. Klassen verpflichtenden PC-Führerschein, in dem den Schülern der Umgang
mit dem Rechner, dem Schulnetzwerk, dem Internet und den üblichen Office-Anwendungen
beigebracht wird, haben die Schüler die Möglichkeit, Informatik ab der 7. Klasse durchgehend bis zum
Abitur als Unterrichts- und Prüfungsfach zu belegen. In der Mittelstufe wird Informatik im Rahmen des
Profilunterrichts Klasse 7 bis 9 (bisher Wahlpflichtunterricht der Klassen 9 und 10) angeboten, in der
Oberstufe als jahrgangsübergreifender Kurs, zur Zeit als Grundkurs P3/P4 für die Klassen 11 bis 13,
zukünftig als Prüfungskurs mit normalen Anforderungen als Ersatz für eine zweite Naturwissenschaft.
Die Abiturthemen werden in einem dreijährigen Zyklus wechselnd unterrichtet. Für die Teilnahme am
Oberstufenkurs wird die Teilnahme an zwei Mittelstufenkursen vorausgesetzt. Schüler, die Informatik in
Klasse 11 neu beginnen, nehmen am Unterricht der Klasse 9 teil. Der Grund für diese Einschränkung
ist der, dass in der Mittelstufe drei Halbjahre JAVA-Programmierung unterrichtet wird und diese JAVAKenntnisse für den Oberstufenkurs notwendig für eine in ihren Leistungen einigermaßen homogene
Schülerstruktur notwendig sind.
Der Aufbau und die Inhalte der Kurse im Einzelnen:
Neue Richtlinien: Zentralabitur und Profilunterricht
Alte Richtlinien: Schulabitur und Wahlpflichtunterricht
Klasse 7 Profilunterricht: 3 Wochenstunden
? Tabellenkalkulation, Excel-Programmierung
? HTML und CSS, Webseitengestaltung
? Einführung in JavaScript
Klasse 8 Profilunterricht: 4 Wochenstunden
Klasse 9 Wahlpflichtunterricht: 4 Wochenstunden
? Sequentielle Programmierung mit JavaKara
? HTML und CSS, Webseitengestaltung
? Kontrollstrukturen von JAVA
? JAVA-Kontrollstrukturen mit JavaKara
? Vertiefung in JavaScript und HTML-DOM
? Einführung in JavaScript, HTML-DOM
Klasse 9 Profilunterricht: 4 Wochenstunden
Klasse 10 Profilunterricht: 4 Wochenstunden
? Objektorientierte Programmierung mit JAVA
? Objektorientierte Programmierung mit JAVA
Klasse 10 bis 12 NA-Kurs: 2+4+4 Wochenstunden Klasse 11 bis 13 Grundkurs: 3+3+3 Wochenstunden
? Datenbanken, ER-Modelle und SQL
? Informatik und Gesellschaft: Datenschutz, Urheberrecht, soziale Auswirkungen, Geschichte
? Algorithmen und Datenstrukturen I: Suchen und Sortieren auf Feldern
? Algorithmen und Datenstrukturen II: dynamische Listen und Bäume, Stack und Pipe
? Endliche Automaten und Parserbau: Zustandsgraphen, Touring-Maschinen, Parser und Interpreter
? Technische Informatik: Logik, Rechnerstrukturen
Die Unterrichtsinhalte
Für die Durchführung des Projektes gehen die Themen „Datenstrukturen“ und „endliche Automaten“
sowie die hier beschriebenen Unterrichtsinhalte voraus. Zusätzlich wird von den Schülern erwartet, dass
sie Übung in der JAVA-Programmierung aus der Mittelstufe mitbringen und die formal entwickelten
Lösungen wie hier als Beispiel beschrieben, weitgehend selbstständig in JAVA implementieren können.
Die Unterrichtsinhalte werden aus Platzgründen nur kurz angerissen.
Seite 4
Virtuelle Lehrerfortbildung Informatik Niedersachsen
Hans Peter Schneider
Ampelschaltung – Logik löten
Grammatiken formaler Sprachen
Eine formale Sprache L (etwa eine Programmiersprache) besteht aus einem Tupel L(T, N, P, S) der
Menge T der Terminalsymbole (Alphabet), der Menge N der Nichtterminalsymbole, der Menge P der
Produktionen (Substitutionsregeln) und dem Startsymbol S. Die Menge P unter Verwendung der
anderen drei nennt man gern auch die Grammatik der Sprache L. Zur Darstellung der der Regeln der
Grammatik gibt es verschiedene Formen, von denen 1. die einfache, mathematisch-logische
Darstellung, 2. das Syntaxdiagramm und 3. die Backus-Naur-Form (BNF) und ihre Erweiterung (EBNF).
Hier ein Beispiel in drei Ausführungen. Definiert wird ein Satz als Folge von Wörtern, beendet mit
einem Punkt. Die Wörter werden durch Leerzeichen _ getrennt. Das Startsymbol S ist Satz, die Menge
der Nichtterminalsymbole ist {Satz, Wort, Zeichen} und die Menge der Terminalsymbole ist
{a, b, … z, A, B, … Z}.
einfache Form, der mathematischen Logik entlehnt
Satz = Wort . | Wort _ Satz
Syntaxdiagramm
Satz
Wort = Zeichen | Zeichen Wort
_
Zeichen = a | b | … | z | A | B | … | Z
Wort
erweiterte Backus-Naur-Form (EBNF)
<Satz> ::= <Wort> {'_' <Wort> } '.'
<Wort> ::= <Zeichen> { <Zeichen> }
.
Wort
Zeichen
Zeichen
a
b
…
<Zeichen> ::= 'a' | 'b' | … | 'z' | 'A' | 'B' | … | 'Z'
Während bei der einfachen Darstellung Wiederholungen rekursiv definiert werden müssen, sind in
den Backus-Naur-Formen Klammern für Option [] und Repetition {} und Vorrang () definiert.
Lindemeyer-Grammatiken
Lindemeyer-Grammatiken sind spezielle generative Grammatiken, die rekursiv die Ersetzung
einzelner Zeichen durch Zeichenketten beschreiben. Im Unterschied zu den oben beschriebenen
Grammatiken sind die Mengen T der Terminale und N der Nichtterminale identisch: Auf der linken
Seite der Regeln stehen Terminale, die durch eine Folge von Terminalen ersetzt werden. Diese
Eigenschaft
ermöglicht
Grammatik Ergebnisse nach Anwendung der Regeln
rekursive Regeln. Mit Hilfe
S ::= abuba
0.: abuba
dieser Regeln wird eine a ::= dudu
1.: duduikkibaikkidudu
gegebene
Zeichenkette b ::= ikki
2.: dbadbaikkiikkiduduikkidbadba
u ::= ba
3.: dikkidududikkiduduikkiikkidbadbaikkidikkidududikkidudu
beliebig verlängert.
Durch die Struktur von Lindemeyer-Grammatiken fällt die Nähe zu fraktalen Strukturen, besonders
zu Koch’schen Kurven auf, die durch sehr ähnliche Ersetzungsregeln erzeugt werden (siehe unten).
Tatsächlich sind Lindemeyer-Grammatiken geradezu prädestiniert, um mit ihnen Koch’sche Kurven zu
erzeugen, in dem man die Zeichenketten als Anweisungssequenzen für ein geeignetes Grafiksystem wie
einen Turtle-Grafikkontext interpretiert. Dies ist der zentrale Punkt unseres Projektes.
Virtuelle Lehrerfortbildung Informatik Niedersachsen
Hans Peter Schneider
Ampelschaltung – Logik löten
Seite 5
Turtle-Grafik
Turtle-Grafik nennt man eine grafische Umgebung, in der die Position des „Zeichenstiftes“ nur
relativ verändert werden kann durch ein paar einfache Befehle. Dabei stellt
man sich vor, der Zeichenpunkt sei eine Schildkröte, die im wesentlichen
durch die beiden Befehle „gehe“ und „drehe“ an einen anderen Punkt
gesteuert wird, dabei hinterlässt sie eine Spur auf der Zeichenfläche. Die
Distanz des Befehls „gehe“ sowie der Winkel des Befehls „drehe“ sind
festgelegte Werte. Ursprünglich wurde diese Grafikumgebung implementiert,
um einfache Plotter zu steuern; die Schrittdistanz war abhängig von der
Mechanik des Plotters und entsprach der minimalen Distanz, die die
verwendeten Schrittmotoren bewegt werden konnten, der festgelegte Winkel war stets 90°. Danach
verwendete man das Konzept in Robotern mit Zeichenstift, die sich frei auf einer Zeichenfläche
bewegen konnte. Auch dabei waren Distanz und Winkel durch die Schrittmotoren des Antriebs
festgelegt: Die Distanz entsprach der Länge, die die beiden Schrittmotoren der beiden Räder minimal
in gleicher Richtung zurücklegten, der Winkel der, um den sich der Roboter drehte, wenn beide
Schrittmotoren die Räder minimal in entgegen gesetzte Richtungen drehten. Alle alten
Programmiersprachen (wie BASIC und PASCAL) und aus Tradition auch viele der modernen enthalten
ein Paket mit einer Turtle-Grafikumgebung, in JAVA muss man sie jedoch selber schreiben, dies
machen wir unter Anderem zum Gegenstand unseres Projektes.
Fraktale
Fraktale tauschen zum ersten Mal 1875 auf, als Weierstrass eine stetige, in keinem Punkt
differenzierbare Funktion konstruiert, was zu einer fünfzigjährigen Krise der Mathematik führte. Dies
machten ihm Mathematiker wie Cantor, Peano, Lebesgue, Hausdorff, Bolzano, Sierpinski und Koch
nach. Schließlich prägte 1977 der Informatiker Mandelbrot1 beruhend auf den Arbeiten von Julia2 den
Begriff des Fraktals und definierte eine neue, nicht auf die Analysis aufbauende Mathematik zu
Beschreibung von Fraktalen, wobei er vor allem den Begriff der „fraktalen Dimension“ Df definierte, der
alle positiven reellen Werte annehmen kann und nur bei analytischen Formen mit der normalen,
ganzzahligen, sogenannten „topologischen“ DimenKoch-Mäander
sion Dt übereinstimmt.
Eine der gängigsten Verfahren, ein Fraktal zu
erzeugen, ist die unendlich häufige Anwendung eines
Generators, in der ausgehend von einer
geometrischen
Grundfigur
durch
wiederholte
Befolgung einer Bearbeitungsvorschrift diese immer
komplexer wird und letztlich gegen das Fraktal
konvergiert. Das von Koch3 implizierte Verfahren
eines geometrischen Generators zur Erzeugung
fraktaler Kurven aus Streckenzügen. Dabei wird eine
Strecke ersetzt durch einen endlichen Polygonzug,
1
N=8
r=¼
Df = 1,5
Harter-Heightway-Drachen
N=4
r=½
Df = 2
Benoît B. Mandelbrot (* 20. November 1924 in Warschau, Polen)
Gaston Maurice Julia (* 3. Februar 1893 in Sidi bel Abbès, Algerien; † 19. März 1978 in Paris)
3
Niels Fabian Helge von Koch (* 25. Januar 1870 in Stockholm, † 11. März 1924 ebenda)
2
Virtuelle Lehrerfortbildung Informatik Niedersachsen
Hans Peter Schneider
Ampelschaltung – Logik löten
Seite 6
bestehend aus lauter gleichlangen Teilstrecken. Im nächsten Schritt wird jede Teilstrecke des
Polygonzugs wiederum ersetzt durch eine maßstäbliche Verkleinerung des Polygonzugs usw. Aus sehr
einfachen Generatoren können so sehr komplexe fraktale Kurven entstehen. Die oben erwähnte
fraktale Dimension Df dieser Koch’schen Kurven ist definiert als das Verhältnis der Polygonzuglänge zur
Länge der ersetzten Strecke in folgender Weise:
Sei n die Zahl der Teilstücke des Polygonzugs und r die relative Länge der Teilstücke zu der zu
log(n)
ersetzenden Strecke. So ist Df definiert als: D f ?
log( 1r )
Der Wert der Dimension Df ist ein Maß dafür, in wie weit die Kurve eine Fläche füllt, Eine Kurve mit
Df = 2 hat also einen positiven Flächeninhalt. In den Kästen sieht man ein paar Beispiele.
Koch-Schneeflocke
N=4
r = 1/3
Df = 1,26
Das Projekt
Im Folgenden wird beispielhaft die
#: 0F++4F++7F;
Entwicklung eines kleinen JAVA-Programms
F: +F--FF++F-;
beschrieben, das einen Automaten enthält,
der einen Parser darstellt, der LindemeyerGrammatiken analysiert und als Programme
für Turtle-Grafiken interpretiert. Durch die
Definition einer einfachen LindemeyerGrammatik kann ein komplexes Fraktal (etwa
eine Koch’sche Kurve) von einer Turtle gezeichnet werden (siehe Kasten).
Das Projekt erinnert ein wenig an das Projekt „LOGO für Arme“4, nur dass Programme aus
Lindemeyer-Grammatiken rekursiv aufgebaut sind und weder Entscheidungen noch Schleifen kennen.
Jede Wiederholung muss also rekursiv formuliert werden. Ein weiterer Unterschied ist der, dass ein
solches Programm nicht aus einer Folge von Anweisungen (mit einer Reihenfolge) besteht, sondern aus
einer Menge von Regeln (ohne Reihenfolge, ähnlich einem PROLOG-Programm). Ein Parser kann
daher nicht einfach im Code vorne Anfangen und sich nach hinten durcharbeiten und dabei schon den
Interpreter anschmeißen, sondern muss sich an bestimmten Schlüsselzeichen orientieren und sich von
diesen ausgehend die Regeln erfassen. Ein solcher Parser kann als eine Touring-Maschine mit einem
großen Zeichensatz interpretiert werden, da der Parser sich im Code je nach Zustand vor und zurück
bewegt.
4
siehe [1] E. Modrow „Einführung in die theoretische Informatik“, Kapitel 3.6 und 10.
Virtuelle Lehrerfortbildung Informatik Niedersachsen
Hans Peter Schneider
Ampelschaltung – Logik löten
Seite 7
Turtle-Befehlsinterpreter
Um Ein Turtle-Programm zu schreiben, muss jeder Turtle-Befehl
als Zeichen (-kette) dargestellt werden. Übergibt man dieses
Zeichen dem Interpreter der Turtle, so führt die Turtle den Befehl
aus. Die typischen Turtle-Befehle und ihre Zeichendarstellung sind
in der nebenstehenden Tabelle aufgelistet. Prinzipiell darf die
übergebene Zeichenkette jedes beliebige Zeichen enthalten, aber
nur die nebenstehenden Zeichen werden interpretiert, es gibt in
dem Sinne also kein fehlerhaftes Programm.
eine Standardlänge gehen
und zeichnen
eine Standardlänge gehen
ohne zu zeichnen
um Standardwinkel nach
rechts drehen
um Standardwinkel nach
links drehen
umdrehen
in Ausgangsstellung gehen
Farbe auswählen
F
M
+
–
!
#
0..9
Ein Turtle-Objekt muss den Standardwinkel und die
Standardlänge seiner Bewegungen kennen, ebenso den Grafikkontext in dem es zeichnen soll und die
Koordinaten und die Richtung seiner Ausgangsstellung. Für diesen Zweck braucht eine Turtle-Klasse
entsprechende Set-Methoden. Ein Turtle-Objekt kennt jederzeit seine derzeitige Position und seine
Ausrichtung. Zum Zeichnen verwendet das Turtle-Objekt die Methoden des zugewiesenen
Grafikkontextes.
public class Turtle {
static double c = Math.PI/180.;
//Winkel-Radiant-Umrechnungskonstante
static Color[] penCol = { Color.black, Color.darkGray, Color.gray, Color.red, Color.orange,
Color.yellow, Color.green, Color.cyan, Color.blue, Color.magenta };
Graphics graph;
// Grafik-Kontext der Turtle-Grafik
double stpL = 10.;
double stpA = c*60.;
double posX = 0.;
double posY = 0.;
double posDir = c*90.;
int posCol = 0;
// Schrittlänge
// Schrittwinkel
// aktuelle Position: Ort(x,y), Winkel, Farbe
// ------------ Konstruktoren --------------------------------------------------------------------------------------public Turtle(Graphics g) {
// Turtle mit Grafikkontext erzeugen
graph = g;
};
public Turtle() {
};
// Turtle erzeugen
// ----------- Einstellungen ----------------------------------------------------------------------------------------public void setGraphics(Graphis g) {
// Grafikkontext setzen
graph = g;
};
public void setPrms(double length, double angle) {
stpL = length;
stpA = correct(c*angle);
}; // setPrms
// Initialisierung: Grundwerte
public void setStart(double x, double y, double dir) {
posX = x;
posY = y;
posDir = correct(c*dir);
}; // setStart
// Initialisierung: Startwerte
// ----------- Interpreter -----------------------------------------------------------------------------------------public void draw(String code) {
//------------------ Regel befolgen
double actX;
double actY;
char c;
for (int i = 0; i < code.length(); i++) {
// code abarbeiten
Seite 8
Virtuelle Lehrerfortbildung Informatik Niedersachsen
Hans Peter Schneider
Ampelschaltung – Logik löten
c = code.charAt(i);
// nächsten Befehl nehmen
actX = posX;
// alte Position merken
actY = posY;
switch (c) {
case 'F': // male Schritt (forward)
posX = actX+Math.cos(posDir)*stpL;
posY = actY+Math.sin(posDir)*stpL;
graph.setColor(penCol[posCol]);
graph.drawLine((int)Math.round(actX), (int)Math.round(actY),
(int)Math.round(posX), (int)Math.round(posY));
break;
case 'M': // gehe Schritt ohne malen (muted forward)
posX = actX+Math.cos(posDir)*stpL;
posY = actY+Math.sin(posDir)*stpL;
break;
case '+': // drehe rechts
posDir = correct(posDir+stpA);
break;
case '-': // drehe links
posDir = correct(posDir-stpA);
break;
case '!': // drehe um
posDir = correct(posDir+Math.PI);
break;
default: // Farbe auswählen
if ((c >= '0')&&(c <= '9')) {
posCol = (int)c - (int)'0';
};
};
};
}; // draw
// ------- interne Methoden -----------------------------------------------------------------------------public double correct(double a) {
// Winkelkorrektor: -180°... +180°
double z = 2*Math.PI;
while (a >= Math.PI) {
a = a - z;
};
while (a < -Math.PI) {
a = a + z;
};
return a;
}; // correct
}; // class Turtle
Parser für Lindemeyer-Grammatik
Eine Lindemeyer-Grammatik beinhaltet die rekursive Ersetzung von Zeichen durch Zeichenketten
und ermöglicht so erst die grafische Rekursion, die nötig ist, um Fraktale zu erzeugen. Diese
Eigenschaften bürden wir nicht der Turtle auf (die kann ja nichts dafür), sondern überlassen sie dem
vorgeschalteten Parser zur Auswertung: Der Parser ordnet zunächst die Ersetzungsregeln in eine Liste
ein und führt dann die rekursiven Ersetzungen aus, wobei er Zeichen, für die keine Ersetzungsregel
existiert, an den Turtle-Interpreter weiterreicht. Um keinen endlosen Automaten zu erzeugen, wird die
endlose rekursive Tiefe und Selbstähnlichkeit der Fraktale dem Pragmatismus geopfert (wir müssen bei
der Darstellung eh auf ganze Pixel runden) und lassen den Benutze bei jeder Anwendung eine
Rekursionstiefe festlegen, über die hinaus keine Ersetzungen mehr vorgenommen werden. Quasi als
Trost räumen wir dem Benutzer aber die Möglichkeit ein, bei Erreichen der Rekursionstiefe eine letzte,
alternative Ersetzung vornehmen zu lassen. Der Parser analysiert also die Lindemeyer-Grammatik und
nimmt nicht wahr, dass die Zeichen, die er verarbeitet, irgendwelche Bedeutungen haben. Erst wenn
der Parser sein Ergebnis an den Interpreter der Turtle weiter gibt, werden aus den Zeichen Befehle. Hier
die Syntax der Grammatik für den Parser in EBNF und als Syntaxdiagramm:
Virtuelle Lehrerfortbildung Informatik Niedersachsen
Hans Peter Schneider
Ampelschaltung – Logik löten
<Programm>
<Startanweisung>
<Anweisung>
<Zeichenfolge>
<Zeichen>
Seite 9
::= { <Anweisung> } <Startanweisung> { <Anweisung> }
::= '#' ':' <Zeichenfolge> [ ':' <Zeichenfolge> ] ';'
::= <Zeichen> ':' <Zeichenfolge> [ ':' <Zeichenfolge> ] ';'
::= <Zeichen> { <Zeichen> }
::= ? alle Unicode-Zeichen außer '#', ':', ';' und den Blanks ' ', \t und \n ?
Programm
Startanweisung
Anweisung
Startanweisung
#
:
Anweisung
Zeichenfolge
;
:
Anweisung
Zeichen
:
Zeichenfolge
Zeichenfolge
;
:
Zeichenfolge
Zeichen
Zeichenfolge
Zeichen
alle Unicode-Zeichen außer '#' ':' ';' ' ' \t \n
Jede Anweisung wird als Regel formuliert, vergleichbar einem PROLOG-Programm. Die Reihenfolge
der Regeln ist dabei uninteressant, irgendwo sollte es aber eine Startregel geben, die mit #: beginnt.
Der Automat, der diese Regeln analysiert, muss also nicht von vorn nach hinten die Zeichen abarbeiten
wie in einem sequentiellen Programm, sondern sucht nach Schlüsselzeichen und wandert von diesen je
nach Zustand vorwärts oder rückwärts durch den Code wie eine Touring-Maschine. Die
Schlüsselzeichen sind die in der Grammatik verwendeten Begrenzer ':' und ';' . Vor dem Parsen werden
alle Blanks, die der Benutzer zur Formatierung verwenden darf, aus dem Code entfernt. Da es leichter
ist, wenn sich der Parser bei seiner Arbeit die Positionen
S1
der Begrenzer merken kann, wird er besser als Stack':',
L,
(1)
*, RR, (2)
Automat implementiert. Es gibt höchstens drei Werte, die
*, R, –
*, R, –
sich der Parser gleichzeitig merken muss, so kann der
Stack einfach in Form von drei definierte Variablen
S2
S0
realisiert werden.
';', R, (3)
Eine Anweisung besteht also mindestens aus einem
Zeichen, gefolgt vom Doppelpunkt und einer
Zeichenfolge, abgeschlossen mit einem Semikolon, zum
Beispiel: A: ABGFAFR; Dies bedeutet, dass das Zeichen A
in jeder Zeichenfolge des Programms ersetzt wird durch
die angegebene Zeichenfolge ABGFAFR. Nach der ersten
zwei Ersetzungsvorgängen sieht die Zeichenfolge
ABGFAFR also so aus:
ABGFAFRBGFABGFAFRFRBGFABGFAFRBGFABGFAFRFRFR
Ein Programm kann aus beliebig vielen solchen
Ersetzungsanweisungen bestehen.
Das Rautenzeichen '#' bezeichnet den Programmstart,
also die Zeichenfolge mit der die Ersetzung anfangen soll.
Findet der Parser keine Raute, so führt dies zu einem
Start
':', R, (3)
';', R, (4)
S3
*, R, –
* : alle anderen Eingaben
R: Postion ein Zeichen nach rechts
L: Position ein Zeichen nach links
(1) Position a merken;
(2) Regelslot auswählen, Zeichen in Regelslot
speichern;
(3) Position b merken, Zeichenfolge zwischen
a und b in Regelslot (normale Ersetzung)
speichern;
(4) Position c merken, Zeichenfolge zwischen
b und c in Regelslot (finale Ersetzung)
speichern;
– keine Aktion
Seite 10
Virtuelle Lehrerfortbildung Informatik Niedersachsen
Hans Peter Schneider
Ampelschaltung – Logik löten
Syntaxfehler. Es ist jedoch egal, in welcher Reihenfolge die Ersetzungsanweisungen stehen. Steht in
einer Anweisung eine zweite Zeichenfolge hinter einem zweiten Doppelpunkt, wird diese zweite
Zeichenfolge in der letzten Ersetzung bei erreichen der vorgegebenen Rekursionstiefe an Stelle der
ersten Zeichenfolge benutzt; diese Angabe ist jedoch optional.
Der vom Parser beachtete Bereich reicht vom Zeichen vor dem ersten Doppelpunkt bis zum nächsten
Semikolon jeder Regel. Jedes Zeichen außerhalb dieser Bereiche wird ignoriert, eine Fehlermeldung ist
auf Grund des Zusammenhangs nicht nötig. Auf diese weise kann man fast überall, also vor dem
Programm, nach jeder Regel und am Ende des Programms ohne weitere Kennzeichnung Kommentare
einfügen, ohne Einfluss auf die Syntax auszuüben.
Der Parser ist Bestandteil des Fensters, das die Zeichnung zeigt. Die Liste der Ersetzungsregeln wird
vom Parser nur einmal erzeugt, wenn ein neues Programm eingegeben wurde. Die Ersetzungsrekursion
wird dann von der paint-Methode des Fensters angestoßen. Während der Rekursion zeichnet die Turtle
dann die codierte Grafik. Hier folgt der wesentliche Code des Parsers parse() und der
Rekursionsmaschine run(). Der vollständige Code findet sich in den Anlagen.
class LindeScreen extends Frame implements MouseListener, MouseMotionListener {
Turtle turtle;
String item = "";
String[] subst;
String[] finst;
// Schildkröte
// Liste der Symbole mit Ersetzungsregeln (0-tes ist Startanweisung)
// Liste der zu den Symbolen gehörigen Ersetzungen
// Liste der zu den Symbolen gehörigen finalen Ersetzungen
?
public void parse() {
// Parser unterteilt den Text des Editors
String code = editTA.getText().trim();
// Regeltext holen
item = "#";
// Zuordnungstabelle auf null
subst = new String[40];
finst = new String[40];
int i = 0;
// Aktuelle Position im Code
int j;
// Position des ersten ':' der gefundenen Regel
int g;
// Position des zweiten ':' der gefundenen Regel
int k;
// Position des ';' der gefundenen Regel
int f;
// Position des Endes der Standardersetzung ( g, wenn g existiert, sont k)
int h;
// Index der Ersetzungsregel im Regelfeld (item, subst, finst)
while (i < code.length()) {
// Scannen des Textes
j = code.indexOf(':', i);
// ersten ':' nach Position i erkennen
if (j > -1) {
g = code.indexOf(':', j+1);
// zweiten ':' erkennen, bevor..
k = code.indexOf(';', j);
// ';' erkennen
if (k <= j) {
//
... sonst Textende
k = code.length();
};
if ((g > -1)&&(g < k)) {
if ((j == 0)||("# ".indexOf(code.charAt(j-1)) > -1)) {
// Startzeile mit # erkennen
finst[0] = code.substring(g+1, k).trim();
} else if ((h = item.indexOf(code.charAt(j-1))) > -1) {
// vorhandene Regel überschreiben
finst[h] = code.substring(g+1, k).trim();
} else if((h = item.length()) < 40) {
// neue Regel einfügen
item = item+code.charAt(j-1);
finst[h] = code.substring(g+1, k).trim();
};
f = g;
} else {
f = k;
};
if ((j == 0)||("# ".indexOf(code.charAt(j-1)) > -1)) {
// Startzeile erkennen
subst[0] = code.substring(j+1, f).trim();
} else if ((h = item.indexOf(code.charAt(j-1))) > -1) {
// vorhandene Regel überschreiben
subst[h] = code.substring(j+1, f).trim();
} else if((h = item.length()) < 40) {
// neue Regel einfügen
item = item+code.charAt(j-1);
subst[h] = code.substring(j+1, f).trim();
};
Virtuelle Lehrerfortbildung Informatik Niedersachsen
Hans Peter Schneider
Ampelschaltung – Logik löten
i = k;
} else {
i = code.length();
};
newTurtle = true;
};
}; // parse
Seite 11
// kein ':' mehr, also ignoriere Rest.
public void run(String s, int d) {
//---------- Rekursionsebene der Turtle-Aktivität
int nr;
char c;
for (int i = 0; i < s.length(); i++) {
// Regelzeile abarbeiten
c = s.charAt(i);
nr = item.indexOf(c);
if (nr == -1) {
// wenn Zeichen nicht in Ersetzungsliste
turtle.draw(""+c);
// ... dann gib's der Schildkröte
} else if (d < tiefe) {
// ist es in der Liste
run(subst[nr], d+1);
// ... dann mach das ganze mit der Ersetzungsregel (Rekursion)
} else if (finst[nr] == null) {
// ist aber die maximale Rekursionstiefe erreicht
turtle.draw(subst[nr]);
// .... dann füttere die Schildkröte mit der ganzen Regel
} else {
turtle.draw(finst[nr]);
// .... bzw. mit der fialen Regel, wenn vorhanden
};
};
}; // run
public void run() {
// ----------- Turtle-Aktivität starten
turtle.setPrms(laenge, winkel);
// Initialisierung
turtle.setStart(startX, startY, startA);
turtle.draw("0");
// Farbe auf schwarz setzen (mit dem Interpreter!)
if (item.length() > 0) {
// gibt es wenigstens eine Startregel?
if (tiefe > 0) {
// wenn Rekursionstiefe > 0 dann starte Rekursion
run(subst[0], 1);
} else if (finst[0] == null) {
// sonst füttere die Schildkröte mit der Startregel
turtle.draw(subst[0]);
} else {
turtle.draw(finst[0]);
// .... bzw. mit der finalen Startregel, wenn vorhanden
};
};
}; // run
?
};
Erweiterung um Stack-Befehle
Turtle-Zustand auf den Stack
[
Die Programmierungsmöglichkeiten der Turtle erweitern sich legen (push)
beträchtlich, wenn man der Turtle einen Stack verpasst, der Turtle-Zustand vom Stack
]
Interpreter lässt sich mit relativ wenig Aufwand diesbezüglich holen (pop)
aufrüsten. Der Turtle erleichtert es, in ihren Grafiken Abzweigungen zu zeichnen, in dem die Position
der Abzweigung gespeichert wird, erst der eine Zweig gezeichnet wird, dann die Position der
Abzweigung restauriert wird, um dann den anderen Zweig zu zeichnen. Die Klasse Turtle wird wie folgt
ergänzt:
public class Turtle {
?
// ------------ Stack-Element -----------------------------------------------------------------------------class StackEl {
double pX;
// Werte: Ort(x,y), Richtung, Farbe
double pY;
double pD;
Color pC;
StackEl nxt;
// Zeiger auf nächstes Element
StackEl() {
pX = posX;
pY = posY;
// Konstruktor
// Werte aus Turtle übernehmen
Virtuelle Lehrerfortbildung Informatik Niedersachsen
Hans Peter Schneider
Ampelschaltung – Logik löten
Seite 12
pD = posDir;
pC = posCol;
};
}; // StackEl
// --------------- Turtle-Variablen -----------------------------------------------------------------------------StackEl stack;
// leeren Stack erzeugen
?
// ---------------- Stack-Methoden ----------------------------------------------------------------------------public void push() {
// Stack push
StackEl ptr = new StackEl();
ptr.nxt = stack;
// Element wird am Anfang der Liste eingefügt
stack = ptr;
}; // push
public void pop() {
if (stack != null) {
posX = stack.pX;
posY = stack.pY;
posDir = stack.pD;
posCol = stack.pC;
stack = stack.nxt;
};
}; // pop
// Stack pop
// Turtle-Werte restaurieren
// Element wird aus der Liste herausgenommen
?
// ------------ Interpreter ------------------------------------------------------------------------public void draw(String code) {
//------------------ Regel befolgen
?
switch (c) {
?
case '[': push();
break;
case ']': pop();
break;
// Turtle-Zustand auf den Stack legen
// Turtle-Zustand vom Stack holen
??
};
?
}; // draw
};
Das Java-Programm LindeTurtle
Das Hauptfenster enthält auf dem ersten Reiter den
Editor, in dem die Lindemeyer-Regeln definiert werden. Auf
dem zweiten Reiter werden die Einstellungen für die Startund Standardwerte vorgenommen. Der dritte Reiter enthält
eine Übersicht über die Turtle-Befehle. Ein zweites Fenster,
das über den Menüeintrag „Fenster – Leinwand“ geöffnet
wird, enthält die grafische Fläche für die Turtle. Sobald es
geöffnet wird, interpretiert es den Inhalt des Editors. Soll
der Editor-Inhalt nach einer Änderung neu interpretiert
werden, kann man dies durch den Menüeintrag
„Schildkröte – starten“ auslösen.
Das Datei-Menü erlaubt es, die Lindemeyer-Regeln
zusammen mit den Einstellungen in einer .ltf-Datei zu
speichern oder wahlweise die Lindemeyer-Regeln allein als reine .txt-Datei.
Virtuelle Lehrerfortbildung Informatik Niedersachsen
Hans Peter Schneider
Ampelschaltung – Logik löten
Beispielprogramme
‚Delta Dragon’ (Df = 2)
Hans Peter Schneider
#: 0F++4F++7F;
F: +F--FF++F-;
'Penta Plexity' (Df = 1.91)
Roger Penrose
#: 4F++7F++5F++9F++6F;
F: F++F++F!F-F++F;
Winkel: 60°
Länge: 1.25
Startpunkt: (150|200)
Startrichtung: 0°
Rekursionstiefe: 8
Winkel: 36°
Länge: 2.7
Startpunkt: (300|30)
Startrichtung: 36°
Rekursionstiefe: 5
‚Sierpinski-Dreieck’ (Df=1.58)
#: b;
a: +b-a-b+ : F;
b: -a+b+a- : F;
'Snowflake' (Df = 1.2618)
Helge von Koch
#: F--F--F;
F: F+F--F+F;
Winkel: 60°
Länge: 2
Startpunkt: (40|500)
Startrichtung: 0°
Rekursionstiefe: 8
Winkel: 60°
Länge: 2
Startpunkt: (50|150)
Startrichtung: 60°
Rekursionstiefe: 5
‚Koch-Pyramide’ (Df = 1.46)
#: F;
F: F-F+F+F-F;
‚Rispen’
#: 4[+++a][---a]5Fba;
a: Fb[+Fba][-Fba]Fba;
b: Fb;
Winkel: 90°
Länge: 2.4
Startpunkt: (10|500)
Startrichtung: 0°
Rekursionstiefe: 5
Winkel: 20°
Länge: 5
Startpunkt: (300|400)
Startrichtung: –90°
Rekursionstiefe: 7
Quellen
[1] Eckard Modrow „Einführung in die theoretische Informatik“, VLIN
[2] Benoit B. Mandelbrot „Die fraktale Geometrie der Natur“, Birkhäuser, Basel 1987
[3] Nikolaus Wirth „Compilerbau“, Teubner Studienbücher Informatik, Stuttgart 1984
Seite 13
Herunterladen