From 29d411250681398ebd7621bc9731b22c317b9670 Mon Sep 17 00:00:00 2001 From: Dirk Zechnall Date: Sun, 5 Jan 2025 23:45:15 +0100 Subject: [PATCH] =?UTF-8?q?Subtrees=20hinzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AuS_A_DFA_Implementieren/.gitignore | 7 + .../lehrer/.classpath | 11 + .../AuS_A_DFA_Implementieren/lehrer/.project | 17 ++ .../lehrer/BinaerzahlenPruefer.java | 127 +++++++++++ .../lehrer/DEA_BeispieleTest.java | 104 +++++++++ .../lehrer/EndlicherAutomat.java | 212 ++++++++++++++++++ .../lehrer/KGB_007Sucher.java | 106 +++++++++ .../lehrer/MailadressenChecker.java | 114 ++++++++++ .../AuS_A_DFA_Implementieren/lehrer/README.md | 3 + .../lehrer/lehrkraft-liesmich.txt | 36 +++ .../lehrer/package.bluej | 75 +++++++ .../AuS_A_DFA_Implementieren/lsg/.classpath | 11 + .../AuS_A_DFA_Implementieren/lsg/.project | 17 ++ .../lsg/BinaerzahlenPruefer.java | 113 ++++++++++ .../lsg/DEA_BeispieleTest.java | 104 +++++++++ .../lsg/EndlicherAutomat.java | 196 ++++++++++++++++ .../lsg/KGB_007Sucher.java | 92 ++++++++ .../lsg/MailadressenChecker.java | 93 ++++++++ .../AuS_A_DFA_Implementieren/lsg/README.md | 3 + .../lsg/lehrkraft-liesmich.txt | 36 +++ .../lsg/package.bluej | 75 +++++++ .../AuS_A_DFA_Implementieren/readme.adoc | 11 + .../AuS_A_DFA_Implementieren/roh/.classpath | 11 + .../AuS_A_DFA_Implementieren/roh/.project | 17 ++ .../roh/BinaerzahlenPruefer.java | 93 ++++++++ .../roh/DEA_BeispieleTest.java | 104 +++++++++ .../roh/EndlicherAutomat.java | 146 ++++++++++++ .../roh/KGB_007Sucher.java | 80 +++++++ .../roh/MailadressenChecker.java | 78 +++++++ .../AuS_A_DFA_Implementieren/roh/README.md | 3 + .../roh/lehrkraft-liesmich.txt | 36 +++ .../roh/package.bluej | 75 +++++++ .../AuS_A_DFA_Implementieren/tipp/.classpath | 11 + .../AuS_A_DFA_Implementieren/tipp/.project | 17 ++ .../tipp/BinaerzahlenPruefer.java | 102 +++++++++ .../tipp/DEA_BeispieleTest.java | 104 +++++++++ .../tipp/EndlicherAutomat.java | 167 ++++++++++++++ .../tipp/KGB_007Sucher.java | 85 +++++++ .../tipp/MailadressenChecker.java | 83 +++++++ .../AuS_A_DFA_Implementieren/tipp/README.md | 3 + .../tipp/lehrkraft-liesmich.txt | 36 +++ .../tipp/package.bluej | 75 +++++++ 42 files changed, 2889 insertions(+) create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/.gitignore create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/lehrer/.classpath create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/lehrer/.project create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/lehrer/BinaerzahlenPruefer.java create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/lehrer/DEA_BeispieleTest.java create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/lehrer/EndlicherAutomat.java create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/lehrer/KGB_007Sucher.java create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/lehrer/MailadressenChecker.java create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/lehrer/README.md create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/lehrer/lehrkraft-liesmich.txt create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/lehrer/package.bluej create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/lsg/.classpath create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/lsg/.project create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/lsg/BinaerzahlenPruefer.java create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/lsg/DEA_BeispieleTest.java create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/lsg/EndlicherAutomat.java create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/lsg/KGB_007Sucher.java create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/lsg/MailadressenChecker.java create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/lsg/README.md create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/lsg/lehrkraft-liesmich.txt create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/lsg/package.bluej create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/readme.adoc create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/roh/.classpath create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/roh/.project create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/roh/BinaerzahlenPruefer.java create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/roh/DEA_BeispieleTest.java create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/roh/EndlicherAutomat.java create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/roh/KGB_007Sucher.java create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/roh/MailadressenChecker.java create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/roh/README.md create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/roh/lehrkraft-liesmich.txt create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/roh/package.bluej create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/tipp/.classpath create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/tipp/.project create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/tipp/BinaerzahlenPruefer.java create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/tipp/DEA_BeispieleTest.java create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/tipp/EndlicherAutomat.java create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/tipp/KGB_007Sucher.java create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/tipp/MailadressenChecker.java create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/tipp/README.md create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/tipp/lehrkraft-liesmich.txt create mode 100644 Quellcodes/AuS_A_DFA_Implementieren/tipp/package.bluej diff --git a/Quellcodes/AuS_A_DFA_Implementieren/.gitignore b/Quellcodes/AuS_A_DFA_Implementieren/.gitignore new file mode 100644 index 0000000..2cc0cb6 --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/.gitignore @@ -0,0 +1,7 @@ +**/*.sh +**/*.class +**/*.ctxt +repo.adoc +repo_subtree.adoc +/alt +/hide diff --git a/Quellcodes/AuS_A_DFA_Implementieren/lehrer/.classpath b/Quellcodes/AuS_A_DFA_Implementieren/lehrer/.classpath new file mode 100644 index 0000000..a177209 --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/lehrer/.classpath @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Quellcodes/AuS_A_DFA_Implementieren/lehrer/.project b/Quellcodes/AuS_A_DFA_Implementieren/lehrer/.project new file mode 100644 index 0000000..5557916 --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/lehrer/.project @@ -0,0 +1,17 @@ + + + tabellen-dfa + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/Quellcodes/AuS_A_DFA_Implementieren/lehrer/BinaerzahlenPruefer.java b/Quellcodes/AuS_A_DFA_Implementieren/lehrer/BinaerzahlenPruefer.java new file mode 100644 index 0000000..17f7550 --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/lehrer/BinaerzahlenPruefer.java @@ -0,0 +1,127 @@ +/** + * Implementiert einen DEA, der 0/1-Folgen darauf prüft, ob + * sie "gültige Binärzahlen" darstellen. + * Gültige Binärzahlen im Sinne dieser Aufgabe beginnen mit + * einer 1, auf die eine beliebige Folge von 0/1 folgt. Die + * einzelne Ziffer 0 ist ebenfalls erlaubt, jedoch keine + * Binärzahlen mit "überflüssigen" führenden Nullen. Die + * Binärzahl darf außerdem ein Vorzeichen führen (muss aber + * nicht). + * + * Gültig sind beispielsweise: + * 1 + * 0 + * 101 + * 11 + * 10 + * 1110010010 + * 111 + * -1 + * 1000000000 + * +1 + * 1001001100 + * + * Ungültig sind beispielsweise: + * 123 + * 00 + * hallo + * 0000000011111 + * 1+1 + * Binärzahl + * abc + * 321 + * 01010101010101 + * 0101 + * 01 + * 0101010101 + * + * @author Urs Lautebach + * @version 2021-Januar + */ + +/* Lehrertipps: + * 1. + * Diese Beispielaufgabe kann bei Bedarf noch vereinfacht + * werden, indem zunächst die Vorzeichen weggelassen werden. + * Allerdings passen dann die mitgelieferten Unit-Tests nicht! + * + * 2. + * Viele Schüler muss man daran erinnern, beim Testen gezielt + * auch ungültige Wörter zu verwenden. + * + * 3. + * Die Lösung setzt etwas Erfahrung mit mehrdimensionalen + * Arrays voraus. + */ + +public class BinaerzahlenPruefer extends EndlicherAutomat { + /* Bei der Implementierung dieser Klasse muss man nur + * die Attribute alphabet, uebergaenge und endzustaende + * geeignet festlegen -- alles andere macht die + * Vaterklasse "EndlicherAutomat.java". + * + * Dort ist auch erklärt, wie die Nummerierung der + * Zustände, des Startzustand und der Übergangstabelle + * zu verstehen sind. + */ + + // Transitionstabelle anlegen mit den Zuständen auf der + // senkrechten und den Alphabetzeichen auf der Rechtsachse: + private static final int[][] transiTabelle = new int [][] { + /* die erste Zeile enthaelt die Sprünge heraus aus + * dem Fehlerzustand 0 (den der Automat ja aber gar + * nicht wieder verlassen darf), die also alle 0 sind: + */ + { 0,0,0,0 }, + { 2,2,3,4 }, + { 0,0,3,4 }, + { 0,0,0,0 }, + { 0,0,4,4 }, + }; + + // Alphabet für diesen Automaten als char[] + private static final char[] binaerzAlph = new char[] { + '-', '+', '0', '1' + }; + + // Menge der Endzustände: + private static final int[] endzustaende = new int[] { + 3, 4 + }; + + /** + * Konstruktor für einen BinaerzahlenPruefer: + */ + public BinaerzahlenPruefer() { + // Aufruf des super-Konstruktors: + super(binaerzAlph, transiTabelle, endzustaende); + } + + /** + * Die main-Methode erlaubt den Aufruf von + * Binaerzahlenpruefer von der Kommandozeile aus mit + * mehreren zu testenden Strings: + * + * java BinaerzahlenPruefer 1110101 0001 +1010 abc + * + * Die Ausgabe lautet dann + * Akzeptiert: "1110101" ist ein gueltiges Binärwort. + * FEHLER! "0001" ist kein gültiges Binärwort! + * Akzeptiert: "+1010" ist ein gueltiges Binärwort. + * FEHLER! "abc" ist kein gültiges Binärwort! + */ + public static void main(String[] args) { + // Endlichen Automaten instanziieren: + EndlicherAutomat binaerzahlenDFA = new BinaerzahlenPruefer(); + + for(String wort : args) { + if(binaerzahlenDFA.gehoertZuSprache(wort)) { + System.out.println("Akzeptiert: \"" + wort + + "\" ist ein gültiges Binärwort."); + } else { + System.out.println("FEHLER! \"" + wort + + "\" ist kein gültiges Binärwort!"); + } + } + } +} diff --git a/Quellcodes/AuS_A_DFA_Implementieren/lehrer/DEA_BeispieleTest.java b/Quellcodes/AuS_A_DFA_Implementieren/lehrer/DEA_BeispieleTest.java new file mode 100644 index 0000000..d544eb0 --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/lehrer/DEA_BeispieleTest.java @@ -0,0 +1,104 @@ +import static org.junit.Assert.*; +import org.junit.Test; +public class DEA_BeispieleTest { + + public DEA_BeispieleTest() { + } + + @Test + public void testBinaerzahlenPruefer() { + BinaerzahlenPruefer binZPruefer = new BinaerzahlenPruefer(); + assertTrue(binZPruefer.gehoertZuSprache("1")); + assertTrue(binZPruefer.gehoertZuSprache("0")); + assertTrue(binZPruefer.gehoertZuSprache("+1")); + assertTrue(binZPruefer.gehoertZuSprache("+0")); + assertTrue(binZPruefer.gehoertZuSprache("-1")); + assertTrue(binZPruefer.gehoertZuSprache("-0")); + assertTrue(binZPruefer.gehoertZuSprache("11")); + assertTrue(binZPruefer.gehoertZuSprache("10")); + assertTrue(binZPruefer.gehoertZuSprache("+11")); + assertTrue(binZPruefer.gehoertZuSprache("-11")); + assertTrue(binZPruefer.gehoertZuSprache("+10")); + assertTrue(binZPruefer.gehoertZuSprache("1111")); + assertTrue(binZPruefer.gehoertZuSprache("1000")); + assertTrue(binZPruefer.gehoertZuSprache("10100101010111000")); + assertTrue(binZPruefer.gehoertZuSprache("1111111")); + + assertFalse(binZPruefer.gehoertZuSprache("")); + assertFalse(binZPruefer.gehoertZuSprache("+")); + assertFalse(binZPruefer.gehoertZuSprache("-")); + assertFalse(binZPruefer.gehoertZuSprache("x")); + assertFalse(binZPruefer.gehoertZuSprache("binärzahl")); + assertFalse(binZPruefer.gehoertZuSprache("00")); + assertFalse(binZPruefer.gehoertZuSprache("+00")); + assertFalse(binZPruefer.gehoertZuSprache("0+0")); + assertFalse(binZPruefer.gehoertZuSprache("123")); + assertFalse(binZPruefer.gehoertZuSprache("0011")); + assertFalse(binZPruefer.gehoertZuSprache("011")); + assertFalse(binZPruefer.gehoertZuSprache("111111+")); + assertFalse(binZPruefer.gehoertZuSprache("000000+")); + assertFalse(binZPruefer.gehoertZuSprache("1+1")); + assertFalse(binZPruefer.gehoertZuSprache("0+1")); + assertFalse(binZPruefer.gehoertZuSprache("1+0")); + } + + @Test + public void testMailadressenChecker() { + MailadressenChecker mailAdrPruefer = new MailadressenChecker(); + assertTrue(mailAdrPruefer.gehoertZuSprache("b@b.bb")); + assertTrue(mailAdrPruefer.gehoertZuSprache("b@b.bbbb")); + assertTrue(mailAdrPruefer.gehoertZuSprache("b.b.b@bbb.bbb")); + assertTrue(mailAdrPruefer.gehoertZuSprache("bbbbbb@b.bb")); + assertTrue(mailAdrPruefer.gehoertZuSprache("b@b.b.b.b.bb")); + assertTrue(mailAdrPruefer.gehoertZuSprache("...@b.bb")); + assertTrue(mailAdrPruefer.gehoertZuSprache("...bbb@bb.bb")); + assertTrue(mailAdrPruefer.gehoertZuSprache("bbb...@bb.bb")); + + assertFalse(mailAdrPruefer.gehoertZuSprache("")); + assertFalse(mailAdrPruefer.gehoertZuSprache(".")); + assertFalse(mailAdrPruefer.gehoertZuSprache("b")); + assertFalse(mailAdrPruefer.gehoertZuSprache("@")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bb@")); + assertFalse(mailAdrPruefer.gehoertZuSprache("b@b.b@bbb")); + assertFalse(mailAdrPruefer.gehoertZuSprache("@bb")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bb@.")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bb@..")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bb@bb.b")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bb@bb.bb.b")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bbb@bbb")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bb.bb.bb.bb")); + assertFalse(mailAdrPruefer.gehoertZuSprache( + "bbb@bb.bb.bb@bb.bb")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bbb@bbb..bbb")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bbb@bbb.bbb..")); + } + + @Test + public void test007Sucher() { + KGB_007Sucher detektiv = new KGB_007Sucher(); + assertTrue(detektiv.gehoertZuSprache("007")); + assertTrue(detektiv.gehoertZuSprache("1007")); + assertTrue(detektiv.gehoertZuSprache("0071")); + assertTrue(detektiv.gehoertZuSprache("007007")); + assertTrue(detektiv.gehoertZuSprache("00007")); + assertTrue(detektiv.gehoertZuSprache("0000712")); + assertTrue(detektiv.gehoertZuSprache("2121200777")); + assertTrue(detektiv.gehoertZuSprache("0077")); + assertTrue(detektiv.gehoertZuSprache("212121007000000")); + assertTrue(detektiv.gehoertZuSprache("00712772712717")); + + assertFalse(detektiv.gehoertZuSprache("")); + assertFalse(detektiv.gehoertZuSprache("0")); + assertFalse(detektiv.gehoertZuSprache("1")); + assertFalse(detektiv.gehoertZuSprache("2")); + assertFalse(detektiv.gehoertZuSprache("7")); + assertFalse(detektiv.gehoertZuSprache("07")); + assertFalse(detektiv.gehoertZuSprache("001")); + assertFalse(detektiv.gehoertZuSprache("002")); + assertFalse(detektiv.gehoertZuSprache("000")); + assertFalse(detektiv.gehoertZuSprache("001002")); + assertFalse(detektiv.gehoertZuSprache("7001")); + assertFalse(detektiv.gehoertZuSprache("0707")); + } +} + diff --git a/Quellcodes/AuS_A_DFA_Implementieren/lehrer/EndlicherAutomat.java b/Quellcodes/AuS_A_DFA_Implementieren/lehrer/EndlicherAutomat.java new file mode 100644 index 0000000..10f9b40 --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/lehrer/EndlicherAutomat.java @@ -0,0 +1,212 @@ +/** + * Implementiert einen tabellengetriebenen endlichen Automaten. + * Instanzen dieser Klasse sind DEAs, die Wörter über einem + * vorgegebenen Alphabet akzeptieren oder ablehnen können. + * + * Die Aufgabe wurde vom schriftlichen Abitur 2020 in + * Baden-Württemberg inspiriert. + * + * @author Urs Lautebach + * @version 2020-Oktober + */ +/* Lehrertipps: + * + * 1. + * Die Implementierung setzt etwas Erfahrung mit mehrdimensionalen + * Arrays sowie die Handhabung von String und char voraus. + * + * 2. + * Die Schüler sollten vor der Implementierung dieser Klasse + * schon Transitionstabellen von Hand erstellt (in der + * Regel aus einem Übergangsgraphen) und idealerweise auch + * schon "Läufe" der so beschriebenen DEAs von Hand simuliert + * haben. Wenn sie das sogar ohne den Übergangsgraphen tun, + * entsteht leichter die richtige Grundvorstellung davon, warum + * dieser "allgemeine" DEA beliebige konkrete DEAs simulieren + * kann. + */ +public class EndlicherAutomat { + private int [][] uebergaenge = null; + private int[] endzustaende = null; + private char[] alphabet = null; + + /** + * Konstruiert einen tabellengetriebenen DEA. Nach dem + * erfolgreichen Aufruf dieses Konstruktors kann man den DEA + * anschließend durch einen Aufruf der Methode + * gehoertZuSprache(String):boolean + * beliebig viele Wörter testen lassen. + * + * @param tabelle Transitionstabelle des DEA. Die erste + * Dimension bezeichnet die Zustände; die zweite Dimension die + * Eingabezeichen. Die Einträge der Tabelle bezeichnen jeweils + * den Zustand, in den der Automat (abhängig vom vorherigen + * Zustand und der Eingabe) wechseln soll. + * + * Der Zustand 0 gilt als Fehlerzustand. Zustand 1 ist immer + * Startzustand (so dass der Startzustand, anders als bei der + * formalen Beschreibung eines DEA, nicht explizit angegeben + * werden muss). + * + * @param finalStates Ein int[], das die Endzustände enthält. + * + * @param alphabet ein Array von char, das das Alphabet des + * Automaten enthält. Die Reihenfolge der Zeichen im Alphabet + * muss der Reihenfolge der Zeichen an der entsprechenden + * Achse der Transitionstabelle entsprechen. + */ + EndlicherAutomat(final char[] alphabet, + final int[][] uebergaenge, final int[] endzustaende ) { + this.alphabet = alphabet; + this.uebergaenge = uebergaenge; + this.endzustaende = endzustaende; + // Die Methode plausibilitaetPruefen ist fertig vorgegeben; + // sie fängt einige offensichtliche Fehler ab: + plausibilitaetPruefen(); + } + + /** + * Die Methode prüft, ob die Attribute transitionstabelle, + * alphabet und endzustaende zueinander passen. Sie ist fertig + * implementiert und dafür gedacht, am Ende des Konstruktors + * aufgerufen zu werden. + */ + private void plausibilitaetPruefen() { + if (uebergaenge == null) { + throw new IllegalArgumentException( + "Die Übergangstabelle ist null oder leer!"); + } + if (alphabet == null) { + throw new IllegalArgumentException( + "Das Alphabet ist null oder leer!"); + } + if (endzustaende == null) { + throw new IllegalArgumentException( + "endzustaende ist null oder leer!"); + } + int tabelleHoehe = this.uebergaenge.length; + int tabelleBreite = uebergaenge[0].length; + // Hat die Übergangstabelle eine Zeile pro Alphabetzeichen? + if (tabelleBreite != alphabet.length) { + throw new IllegalArgumentException("Das Alphabet hat " + + alphabet.length + " Zeichen, die " + + " Übergangstabelle aber " + tabelleBreite + + " Spalten!"); + } + // Sind alle Zeilen der Transitionstabelle gleich lang? + for (int[] zeile : uebergaenge) { + if (zeile.length != tabelleBreite) { + throw new IllegalArgumentException( + "Übergangstabelle ist nicht rechteckig!"); + } + } + // Hat einer der Endzustände eine unplausible Nummer? + for (int zustand : endzustaende) { + if (zustand < 1 || zustand > tabelleHoehe - 1) { + throw new IllegalArgumentException( + "Der Endzustand " + zustand + + " kommt in der Übergangstabelle nicht vor!"); + } + } + } + + /** + * Ermittelt die Nummer (den Index) des angegebenen Zeichens + * innerhalb des Alphabetes dieses Automaten. + * + * @param input Zeichen, dessen Position ermittelt werden soll. + * @returns Position des Zeichens input innerhalb des Alphabets + * dieses Automaten; oder -1, falls es im Alphabet nicht + * enthalten ist. + */ + public int indexImAlphabet(char input) { + /* Man geht das Alphabet durch und prüft, welches seiner + * Zeichen dem "input" entspricht. Falls man es findet, + * gibt man dessen Index zurück. + */ + for (int index = 0; index < alphabet.length; index++) { + if (input == alphabet[index]) + return index; + } + return -1; + } + + /** + * Überprüft, ob der Zustand mit der übergebenen + * Zustandsnummer einer der Endzustände dieses Automaten ist. + * + * @param nr Nummer des fraglichen Zustandes. + * @return ob der Zustand ein Endzustand ist. + */ + public boolean istEndzustand(int nr) { + for (int endzustand : endzustaende) { + // Gehe durch alle Endzustände. Wenn nr einer ist... + if (nr == endzustand) { + return true; + } + } + return false; + } + + /** + * Die Methode kann mit einem String als Parameter aufgerufen + * werden, was oft bequemer ist als ein char[]. + * + * @param s String mit dem zu prüfenden Wort. + * @return ob s von diesem Automaten erkannt wird. + */ + public boolean gehoertZuSprache(String s) { + // Hier ist nichts zu implementieren. + return gehoertZuSprache(s.toCharArray()); + } + + /** + * Übergibt dem DEA ein ganzes Wort und ermittelt, ob er es + * akzeptiert oder nicht. + * + * @param eingabe Die komplette zu analysierende Eingabe als + * char[]. + * @returns true, wenn das Wort vom DEA akzeptiert wird; + * false sonst. + */ + public boolean gehoertZuSprache(final char[] wort) { + /* + * Um zu prüfen, ob wort akzeptiert wird, muss der DEA... + * + * - in Zustand 1 beginnen; + * + * - das Eingabewort Zeichen für Zeichen durchgehen; + * + * - prüfen, ob das Zeichen im Alphabet vorkommt; + * + * - nach dessen Position im Alphabet und altem Zustand + * einen neuen Zustand aus der Tabelle uebergaenge wählen; + * + * - false antworten, wenn er im Fehlerzustand landet, + * + * - am Schluss der Eingabe entscheiden, ob der erreichte + * Zustand ein Endzustand ist. + */ + // Startzustand ist immer Nr. 1: + int zustand = 1; + // Gehe durch alle Zeichen durch: + for (char zeichen : wort) { + int alphIndex = indexImAlphabet(zeichen); + // Falls das Zeichen gar nicht im Alphabet ist...: + if (alphIndex == -1) { + return false; + } else { + // wechsle in neuen Zustand: + zustand = uebergaenge[zustand][alphIndex]; + } + // Bin ich im Fehlerzustand? + if (zustand == 0) { + return false; + } + } + // Die Eingabe ist verbraucht. Falls der aktuelle Zustand + // ein Endzustand ist, akzeptieren wir die Eingabe: + return istEndzustand(zustand); + } +} + diff --git a/Quellcodes/AuS_A_DFA_Implementieren/lehrer/KGB_007Sucher.java b/Quellcodes/AuS_A_DFA_Implementieren/lehrer/KGB_007Sucher.java new file mode 100644 index 0000000..25cd63b --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/lehrer/KGB_007Sucher.java @@ -0,0 +1,106 @@ +/** + * Dieser DFA erkennt Ziffernfolgen, in denen sich irgendwo + * James Bond versteckt hat -- in denen also die Folge 007 + * vorkommt. + * + * Die folgenden Ziffernfolgen sollen beispielsweise akzeptiert + * werden: + * + * 12107117700710012021, + * 007, + * 0071121727, + * 0000000071121727, + * 007007007007, + * 1121271201212007. + * + * Die folgenden Ziffernfolgen sollen nicht akzeptiert werden: + * + * 12107110710012021, + * 07, + * 00, + * 07112172, + * 1121271201212000. + + * Um den Automaten und vor allem die Übergangstabelle + * übersichtlicher zu halten, verwendet er nicht alle Ziffern, + * sondern lediglich das eingeschränkte Alphabet {0, 1, 2, 7}. + * + * Es empfiehlt sich, den Automaten zunächst zu zeichnen, + * die Übergangstabelle auf Papier zu erstellen und dann zu + * programmieren. + * + * @author Urs Lautebach + * @version 2020-Okt + */ +/* Lehrertipps: + * 1. + * Viele Schüler muss man daran erinnern, beim Testen gezielt + * auch ungültige Wörter zu verwenden. + * + * 2. + * Die Lösung setzt etwas Erfahrung mit mehrdimensionalen + * Arrays voraus. + * + * 3. + * Die 007-Aufgabe ist ein Beispiel für das sogenannte + * "Substring-Problem", das im 2016er Informatik-Bildungsplan + * für Baden-Württemberg ausdrücklich erwähnt wird. + */ + +public class KGB_007Sucher extends EndlicherAutomat { + /* Bei der Implementierung dieser Klasse muss man nur + * die Attribute alphabet, uebergaenge und endzustaende + * geeignet festlegen -- alles andere macht die + * Vaterklasse. + */ + private static final char[] alphabet = new char[] { + '0', '1', '2', '7' + }; + + private static final int[][] uebergaenge = new int[][] { + { 0, 0, 0, 0 }, + { 2, 1, 1, 1 }, + { 3, 1, 1, 1 }, + { 3, 1, 1, 4 }, + { 4, 4, 4, 4 }, + }; + + private static final int[] endzustaende = new int[] { + 4 + }; + + /** + * Instanziiert einen KGB_007Sucher-DFA. + */ + KGB_007Sucher() { + // Aufruf des super-Konstruktors: + super(alphabet, uebergaenge, endzustaende); + } + + /** + * Die main-Methode erlaubt den Aufruf des + * Binaerzahlenpruefers von der Kommandozeile aus mit: + * + * java KGB_007Sucher 127007117 1717771021 + * + * Die Ausgabe lautet dann + * + * Akzeptiert: "127007117" enthält 007. + * FEHLER! "1717771021" enthält kein 007! + */ + public static void main(String[] args) { + // Endlichen Automaten instanziieren: + EndlicherAutomat fahnder = new KGB_007Sucher(); + + for(String wort : args) { + if(fahnder.gehoertZuSprache(wort)) { + System.out.println("Akzeptiert: \"" + wort + + "\" enthält 007."); + } else { + System.out.println("FEHLER! \"" + wort + + "\" enthält kein 007!"); + } + } + } +} + diff --git a/Quellcodes/AuS_A_DFA_Implementieren/lehrer/MailadressenChecker.java b/Quellcodes/AuS_A_DFA_Implementieren/lehrer/MailadressenChecker.java new file mode 100644 index 0000000..b3864c4 --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/lehrer/MailadressenChecker.java @@ -0,0 +1,114 @@ +/** + * Die Klasse implementiert einen DEA für die Überprüfung äußerst + * einfacher Mailadressen, die nur aus dem Buchstaben b, Punkten + * und dem at-Zeichen bestehen dürfen. Der Nutzername darf fast + * beliebig aufgebaut sein; die Domain muss mindestens zweistufig + * sein und die Toplevel-Domain muss mindestens zwei Zeichen haben. + * + * Gültig sind damit beispielsweise: + * b@b.bb + * b@b.bbbb + * b.b.b@bbb.bbb + * bbbbbb@b.bb + * b@b.b.b.b.bb + * ...@b.bb + * ...bbb@bb.bb + * bbb...@bb.bb + * + * Ungültig sind beispielsweise: + * . + * b + * @ + * bb@ + * b@b.b@bbb + * b@b.b@bb.b + * @bb + * bb@. + * bb@.. + * + * Tatsächlich ist die Überprüfung realer Mailadressen wesentlich + * komlizierter als in diesem Beispiel. + * + * @author Urs Lautebach + * @version 2021-Janar + * + */ +/* Lehrertipps: + * 1. + * Mit einem vollständigen Buchstabenalphabet wird die + * Transitionstabelle schnell unhandlich und besteht dann doch + * fast nur aus identischen Transitionen. Die Einschränkung + * auf einen einzigen Buchstaben erlaubt die Konzentration auf + * interessantere Fragen. + * + * 2. + * Diese Aufgabe kann bei Bedarf noch vereinfacht werden, indem + * einzelne Anforderungen weggelassen werden. + * Allerdings passen dann die mitgelieferten Unit-Tests nicht! + * + * 3. + * Viele Schüler muss man daran erinnern, beim Testen gezielt + * auch ungültige Wörter zu verwenden. + * + * 4. + * Die Lösung setzt etwas Erfahrung mit mehrdimensionalen + * Arrays voraus. + */ +public class MailadressenChecker extends EndlicherAutomat { + /* Bei der Implementierung dieser Klasse muss man nur + * die Attribute alphabet, uebergaenge und endzustaende + * geeignet festlegen -- alles andere macht die + * Vaterklasse. + */ + + private static final char[] alphabet = new char[] { + 'b', '.', '@' + }; + private static final int[][] transitionen = new int[][] { + { 0, 0, 0 }, + { 2, 2, 0 }, + { 2, 2, 3 }, + { 4, 0, 0 }, + { 4, 5, 0 }, + { 6, 0, 0 }, + { 7, 5, 0 }, + { 7, 5, 0 }, + }; + + private static final int[] endzustaende = new int[] { + 7 + }; + + MailadressenChecker() { + // Aufruf des super-Konstruktors: + super(alphabet, transitionen, endzustaende); + } + + + /** + * Die main-Methode erlaubt den Aufruf des + * Binaerzahlenpruefers von der Kommandozeile mit beliebig + * vielen zu prüfenden Strings: + * java MailadressenChecker bbb.b@bb.bb a@b@c + * + * Die Ausgabe lautet dann + * + * Akzeptiert: "bbb.b@bb.bb" ist eine gültige Mailadresse. + * FEHLER! "a@b@c" ist als Mailadresse ungültig! + */ + public static void main(String[] args) { + // Endlichen Automaten instanziieren: + EndlicherAutomat checker = new MailadressenChecker(); + + for(String wort : args) { + if(checker.gehoertZuSprache(wort)) { + System.out.println("Akzeptiert: \"" + wort + + "\" ist eine gültige Mailadresse."); + } else { + System.out.println("FEHLER! \"" + wort + + "\" ist als Mailadresse ungültig!"); + } // end if + } // end for + } // end main +} // end class + diff --git a/Quellcodes/AuS_A_DFA_Implementieren/lehrer/README.md b/Quellcodes/AuS_A_DFA_Implementieren/lehrer/README.md new file mode 100644 index 0000000..c2be6f0 --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/lehrer/README.md @@ -0,0 +1,3 @@ +# Tabellen Dfa + +Implementierung eines allgemeinen tabellengetriebenen DFA als Programmieraufgabe (samt einiger Beispiel-DFA). \ No newline at end of file diff --git a/Quellcodes/AuS_A_DFA_Implementieren/lehrer/lehrkraft-liesmich.txt b/Quellcodes/AuS_A_DFA_Implementieren/lehrer/lehrkraft-liesmich.txt new file mode 100644 index 0000000..63abfcd --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/lehrer/lehrkraft-liesmich.txt @@ -0,0 +1,36 @@ +Dieser Ordner enthält ein Java-Projekt, in dem die Implementierung +eines tabellengetriebenen DEA geübt wird. Es kann mit BlueJ oder +einem anderen Java-Werkzeug bearbeitet werden. + +Das Projekt liegt in drei Ausstattungen vor; Sie als Lehrkraft +entscheiden, welche Variante(n) Sie den Schülern vorlegen. + +Der Ordner... + +.skelett +-------- +enthält das Rohprojekt ohne Lösungshinweise oder Tipps, aber mit +Spezifikation, Arbeitsanweisungen und JUnit-Test. Das Projekt +sollte fehlerfrei compilieren und lauffähig sein, aber natürlich +schlagen die Tests fehl. + +.tipp +----- +enthält das gleiche Projekt, aber zusätzlich mit Tipps für die +Bearbeitung durch Schüler. + +.lsg +---- +enthält die Musterlösung des fertig ausprogrammierten Projekts. +Hier sollten alle Tests erfolgreich laufen. + +Zu bearbeiten sind die Datei EndlicherAutomat.java, in der der +eigentliche Tabellenmechanismus implementiert wird, und eine Auswahl +der Beispiel-DFA für + +Mailadressen +007-Suche +oder Binärzahlen + +bzw. eigene DFA für andere Sprachen nach Wahl. + diff --git a/Quellcodes/AuS_A_DFA_Implementieren/lehrer/package.bluej b/Quellcodes/AuS_A_DFA_Implementieren/lehrer/package.bluej new file mode 100644 index 0000000..a54f5f2 --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/lehrer/package.bluej @@ -0,0 +1,75 @@ +#BlueJ package file +dependency1.from=DFA_BeispieleTest +dependency1.to=BinaerzahlenPruefer +dependency1.type=UsesDependency +dependency2.from=DFA_BeispieleTest +dependency2.to=MailadressenChecker +dependency2.type=UsesDependency +dependency3.from=DFA_BeispieleTest +dependency3.to=KGB_007Sucher +dependency3.type=UsesDependency +editor.fx.0.height=0 +editor.fx.0.width=0 +editor.fx.0.x=0 +editor.fx.0.y=0 +objectbench.height=119 +objectbench.width=770 +package.divider.horizontal=0.5785785785785785 +package.divider.vertical=0.7519685039370079 +package.editor.height=375 +package.editor.width=643 +package.editor.x=100 +package.editor.y=100 +package.frame.height=600 +package.frame.width=800 +package.numDependencies=3 +package.numTargets=6 +package.showExtends=true +package.showUses=true +project.charset=UTF-8 +readme.height=58 +readme.name=@README +readme.width=47 +readme.x=10 +readme.y=10 +target1.height=62 +target1.name=bin +target1.type=PackageTarget +target1.width=80 +target1.x=70 +target1.y=10 +target2.height=50 +target2.name=KGB_007Sucher +target2.showInterface=false +target2.type=ClassTarget +target2.width=140 +target2.x=10 +target2.y=130 +target3.height=50 +target3.name=DFA_BeispieleTest +target3.showInterface=false +target3.type=UnitTestTargetJunit4 +target3.width=160 +target3.x=180 +target3.y=240 +target4.height=50 +target4.name=EndlicherAutomat +target4.showInterface=false +target4.type=ClassTarget +target4.width=160 +target4.x=180 +target4.y=30 +target5.height=50 +target5.name=MailadressenChecker +target5.showInterface=false +target5.type=ClassTarget +target5.width=180 +target5.x=170 +target5.y=130 +target6.height=50 +target6.name=BinaerzahlenPruefer +target6.showInterface=false +target6.type=ClassTarget +target6.width=180 +target6.x=380 +target6.y=130 diff --git a/Quellcodes/AuS_A_DFA_Implementieren/lsg/.classpath b/Quellcodes/AuS_A_DFA_Implementieren/lsg/.classpath new file mode 100644 index 0000000..a177209 --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/lsg/.classpath @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Quellcodes/AuS_A_DFA_Implementieren/lsg/.project b/Quellcodes/AuS_A_DFA_Implementieren/lsg/.project new file mode 100644 index 0000000..5557916 --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/lsg/.project @@ -0,0 +1,17 @@ + + + tabellen-dfa + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/Quellcodes/AuS_A_DFA_Implementieren/lsg/BinaerzahlenPruefer.java b/Quellcodes/AuS_A_DFA_Implementieren/lsg/BinaerzahlenPruefer.java new file mode 100644 index 0000000..7e3a0ef --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/lsg/BinaerzahlenPruefer.java @@ -0,0 +1,113 @@ +/** + * Implementiert einen DEA, der 0/1-Folgen darauf prüft, ob + * sie "gültige Binärzahlen" darstellen. + * Gültige Binärzahlen im Sinne dieser Aufgabe beginnen mit + * einer 1, auf die eine beliebige Folge von 0/1 folgt. Die + * einzelne Ziffer 0 ist ebenfalls erlaubt, jedoch keine + * Binärzahlen mit "überflüssigen" führenden Nullen. Die + * Binärzahl darf außerdem ein Vorzeichen führen (muss aber + * nicht). + * + * Gültig sind beispielsweise: + * 1 + * 0 + * 101 + * 11 + * 10 + * 1110010010 + * 111 + * -1 + * 1000000000 + * +1 + * 1001001100 + * + * Ungültig sind beispielsweise: + * 123 + * 00 + * hallo + * 0000000011111 + * 1+1 + * Binärzahl + * abc + * 321 + * 01010101010101 + * 0101 + * 01 + * 0101010101 + * + * @author Urs Lautebach + * @version 2021-Januar + */ + + +public class BinaerzahlenPruefer extends EndlicherAutomat { + /* Bei der Implementierung dieser Klasse muss man nur + * die Attribute alphabet, uebergaenge und endzustaende + * geeignet festlegen -- alles andere macht die + * Vaterklasse "EndlicherAutomat.java". + * + * Dort ist auch erklärt, wie die Nummerierung der + * Zustände, des Startzustand und der Übergangstabelle + * zu verstehen sind. + */ + + // Transitionstabelle anlegen mit den Zuständen auf der + // senkrechten und den Alphabetzeichen auf der Rechtsachse: + private static final int[][] transiTabelle = new int [][] { + /* die erste Zeile enthaelt die Sprünge heraus aus + * dem Fehlerzustand 0 (den der Automat ja aber gar + * nicht wieder verlassen darf), die also alle 0 sind: + */ + { 0,0,0,0 }, + { 2,2,3,4 }, + { 0,0,3,4 }, + { 0,0,0,0 }, + { 0,0,4,4 }, + }; + + // Alphabet für diesen Automaten als char[] + private static final char[] binaerzAlph = new char[] { + '-', '+', '0', '1' + }; + + // Menge der Endzustände: + private static final int[] endzustaende = new int[] { + 3, 4 + }; + + /** + * Konstruktor für einen BinaerzahlenPruefer: + */ + public BinaerzahlenPruefer() { + // Aufruf des super-Konstruktors: + super(binaerzAlph, transiTabelle, endzustaende); + } + + /** + * Die main-Methode erlaubt den Aufruf von + * Binaerzahlenpruefer von der Kommandozeile aus mit + * mehreren zu testenden Strings: + * + * java BinaerzahlenPruefer 1110101 0001 +1010 abc + * + * Die Ausgabe lautet dann + * Akzeptiert: "1110101" ist ein gueltiges Binärwort. + * FEHLER! "0001" ist kein gültiges Binärwort! + * Akzeptiert: "+1010" ist ein gueltiges Binärwort. + * FEHLER! "abc" ist kein gültiges Binärwort! + */ + public static void main(String[] args) { + // Endlichen Automaten instanziieren: + EndlicherAutomat binaerzahlenDFA = new BinaerzahlenPruefer(); + + for(String wort : args) { + if(binaerzahlenDFA.gehoertZuSprache(wort)) { + System.out.println("Akzeptiert: \"" + wort + + "\" ist ein gültiges Binärwort."); + } else { + System.out.println("FEHLER! \"" + wort + + "\" ist kein gültiges Binärwort!"); + } + } + } +} diff --git a/Quellcodes/AuS_A_DFA_Implementieren/lsg/DEA_BeispieleTest.java b/Quellcodes/AuS_A_DFA_Implementieren/lsg/DEA_BeispieleTest.java new file mode 100644 index 0000000..d544eb0 --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/lsg/DEA_BeispieleTest.java @@ -0,0 +1,104 @@ +import static org.junit.Assert.*; +import org.junit.Test; +public class DEA_BeispieleTest { + + public DEA_BeispieleTest() { + } + + @Test + public void testBinaerzahlenPruefer() { + BinaerzahlenPruefer binZPruefer = new BinaerzahlenPruefer(); + assertTrue(binZPruefer.gehoertZuSprache("1")); + assertTrue(binZPruefer.gehoertZuSprache("0")); + assertTrue(binZPruefer.gehoertZuSprache("+1")); + assertTrue(binZPruefer.gehoertZuSprache("+0")); + assertTrue(binZPruefer.gehoertZuSprache("-1")); + assertTrue(binZPruefer.gehoertZuSprache("-0")); + assertTrue(binZPruefer.gehoertZuSprache("11")); + assertTrue(binZPruefer.gehoertZuSprache("10")); + assertTrue(binZPruefer.gehoertZuSprache("+11")); + assertTrue(binZPruefer.gehoertZuSprache("-11")); + assertTrue(binZPruefer.gehoertZuSprache("+10")); + assertTrue(binZPruefer.gehoertZuSprache("1111")); + assertTrue(binZPruefer.gehoertZuSprache("1000")); + assertTrue(binZPruefer.gehoertZuSprache("10100101010111000")); + assertTrue(binZPruefer.gehoertZuSprache("1111111")); + + assertFalse(binZPruefer.gehoertZuSprache("")); + assertFalse(binZPruefer.gehoertZuSprache("+")); + assertFalse(binZPruefer.gehoertZuSprache("-")); + assertFalse(binZPruefer.gehoertZuSprache("x")); + assertFalse(binZPruefer.gehoertZuSprache("binärzahl")); + assertFalse(binZPruefer.gehoertZuSprache("00")); + assertFalse(binZPruefer.gehoertZuSprache("+00")); + assertFalse(binZPruefer.gehoertZuSprache("0+0")); + assertFalse(binZPruefer.gehoertZuSprache("123")); + assertFalse(binZPruefer.gehoertZuSprache("0011")); + assertFalse(binZPruefer.gehoertZuSprache("011")); + assertFalse(binZPruefer.gehoertZuSprache("111111+")); + assertFalse(binZPruefer.gehoertZuSprache("000000+")); + assertFalse(binZPruefer.gehoertZuSprache("1+1")); + assertFalse(binZPruefer.gehoertZuSprache("0+1")); + assertFalse(binZPruefer.gehoertZuSprache("1+0")); + } + + @Test + public void testMailadressenChecker() { + MailadressenChecker mailAdrPruefer = new MailadressenChecker(); + assertTrue(mailAdrPruefer.gehoertZuSprache("b@b.bb")); + assertTrue(mailAdrPruefer.gehoertZuSprache("b@b.bbbb")); + assertTrue(mailAdrPruefer.gehoertZuSprache("b.b.b@bbb.bbb")); + assertTrue(mailAdrPruefer.gehoertZuSprache("bbbbbb@b.bb")); + assertTrue(mailAdrPruefer.gehoertZuSprache("b@b.b.b.b.bb")); + assertTrue(mailAdrPruefer.gehoertZuSprache("...@b.bb")); + assertTrue(mailAdrPruefer.gehoertZuSprache("...bbb@bb.bb")); + assertTrue(mailAdrPruefer.gehoertZuSprache("bbb...@bb.bb")); + + assertFalse(mailAdrPruefer.gehoertZuSprache("")); + assertFalse(mailAdrPruefer.gehoertZuSprache(".")); + assertFalse(mailAdrPruefer.gehoertZuSprache("b")); + assertFalse(mailAdrPruefer.gehoertZuSprache("@")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bb@")); + assertFalse(mailAdrPruefer.gehoertZuSprache("b@b.b@bbb")); + assertFalse(mailAdrPruefer.gehoertZuSprache("@bb")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bb@.")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bb@..")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bb@bb.b")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bb@bb.bb.b")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bbb@bbb")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bb.bb.bb.bb")); + assertFalse(mailAdrPruefer.gehoertZuSprache( + "bbb@bb.bb.bb@bb.bb")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bbb@bbb..bbb")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bbb@bbb.bbb..")); + } + + @Test + public void test007Sucher() { + KGB_007Sucher detektiv = new KGB_007Sucher(); + assertTrue(detektiv.gehoertZuSprache("007")); + assertTrue(detektiv.gehoertZuSprache("1007")); + assertTrue(detektiv.gehoertZuSprache("0071")); + assertTrue(detektiv.gehoertZuSprache("007007")); + assertTrue(detektiv.gehoertZuSprache("00007")); + assertTrue(detektiv.gehoertZuSprache("0000712")); + assertTrue(detektiv.gehoertZuSprache("2121200777")); + assertTrue(detektiv.gehoertZuSprache("0077")); + assertTrue(detektiv.gehoertZuSprache("212121007000000")); + assertTrue(detektiv.gehoertZuSprache("00712772712717")); + + assertFalse(detektiv.gehoertZuSprache("")); + assertFalse(detektiv.gehoertZuSprache("0")); + assertFalse(detektiv.gehoertZuSprache("1")); + assertFalse(detektiv.gehoertZuSprache("2")); + assertFalse(detektiv.gehoertZuSprache("7")); + assertFalse(detektiv.gehoertZuSprache("07")); + assertFalse(detektiv.gehoertZuSprache("001")); + assertFalse(detektiv.gehoertZuSprache("002")); + assertFalse(detektiv.gehoertZuSprache("000")); + assertFalse(detektiv.gehoertZuSprache("001002")); + assertFalse(detektiv.gehoertZuSprache("7001")); + assertFalse(detektiv.gehoertZuSprache("0707")); + } +} + diff --git a/Quellcodes/AuS_A_DFA_Implementieren/lsg/EndlicherAutomat.java b/Quellcodes/AuS_A_DFA_Implementieren/lsg/EndlicherAutomat.java new file mode 100644 index 0000000..1680b57 --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/lsg/EndlicherAutomat.java @@ -0,0 +1,196 @@ +/** + * Implementiert einen tabellengetriebenen endlichen Automaten. + * Instanzen dieser Klasse sind DEAs, die Wörter über einem + * vorgegebenen Alphabet akzeptieren oder ablehnen können. + * + * Die Aufgabe wurde vom schriftlichen Abitur 2020 in + * Baden-Württemberg inspiriert. + * + * @author Urs Lautebach + * @version 2020-Oktober + */ +public class EndlicherAutomat { + private int [][] uebergaenge = null; + private int[] endzustaende = null; + private char[] alphabet = null; + + /** + * Konstruiert einen tabellengetriebenen DEA. Nach dem + * erfolgreichen Aufruf dieses Konstruktors kann man den DEA + * anschließend durch einen Aufruf der Methode + * gehoertZuSprache(String):boolean + * beliebig viele Wörter testen lassen. + * + * @param tabelle Transitionstabelle des DEA. Die erste + * Dimension bezeichnet die Zustände; die zweite Dimension die + * Eingabezeichen. Die Einträge der Tabelle bezeichnen jeweils + * den Zustand, in den der Automat (abhängig vom vorherigen + * Zustand und der Eingabe) wechseln soll. + * + * Der Zustand 0 gilt als Fehlerzustand. Zustand 1 ist immer + * Startzustand (so dass der Startzustand, anders als bei der + * formalen Beschreibung eines DEA, nicht explizit angegeben + * werden muss). + * + * @param finalStates Ein int[], das die Endzustände enthält. + * + * @param alphabet ein Array von char, das das Alphabet des + * Automaten enthält. Die Reihenfolge der Zeichen im Alphabet + * muss der Reihenfolge der Zeichen an der entsprechenden + * Achse der Transitionstabelle entsprechen. + */ + EndlicherAutomat(final char[] alphabet, + final int[][] uebergaenge, final int[] endzustaende ) { + this.alphabet = alphabet; + this.uebergaenge = uebergaenge; + this.endzustaende = endzustaende; + // Die Methode plausibilitaetPruefen ist fertig vorgegeben; + // sie fängt einige offensichtliche Fehler ab: + plausibilitaetPruefen(); + } + + /** + * Die Methode prüft, ob die Attribute transitionstabelle, + * alphabet und endzustaende zueinander passen. Sie ist fertig + * implementiert und dafür gedacht, am Ende des Konstruktors + * aufgerufen zu werden. + */ + private void plausibilitaetPruefen() { + if (uebergaenge == null) { + throw new IllegalArgumentException( + "Die Übergangstabelle ist null oder leer!"); + } + if (alphabet == null) { + throw new IllegalArgumentException( + "Das Alphabet ist null oder leer!"); + } + if (endzustaende == null) { + throw new IllegalArgumentException( + "endzustaende ist null oder leer!"); + } + int tabelleHoehe = this.uebergaenge.length; + int tabelleBreite = uebergaenge[0].length; + // Hat die Übergangstabelle eine Zeile pro Alphabetzeichen? + if (tabelleBreite != alphabet.length) { + throw new IllegalArgumentException("Das Alphabet hat " + + alphabet.length + " Zeichen, die " + + " Übergangstabelle aber " + tabelleBreite + + " Spalten!"); + } + // Sind alle Zeilen der Transitionstabelle gleich lang? + for (int[] zeile : uebergaenge) { + if (zeile.length != tabelleBreite) { + throw new IllegalArgumentException( + "Übergangstabelle ist nicht rechteckig!"); + } + } + // Hat einer der Endzustände eine unplausible Nummer? + for (int zustand : endzustaende) { + if (zustand < 1 || zustand > tabelleHoehe - 1) { + throw new IllegalArgumentException( + "Der Endzustand " + zustand + + " kommt in der Übergangstabelle nicht vor!"); + } + } + } + + /** + * Ermittelt die Nummer (den Index) des angegebenen Zeichens + * innerhalb des Alphabetes dieses Automaten. + * + * @param input Zeichen, dessen Position ermittelt werden soll. + * @returns Position des Zeichens input innerhalb des Alphabets + * dieses Automaten; oder -1, falls es im Alphabet nicht + * enthalten ist. + */ + public int indexImAlphabet(char input) { + /* Man geht das Alphabet durch und prüft, welches seiner + * Zeichen dem "input" entspricht. Falls man es findet, + * gibt man dessen Index zurück. + */ + for (int index = 0; index < alphabet.length; index++) { + if (input == alphabet[index]) + return index; + } + return -1; + } + + /** + * Überprüft, ob der Zustand mit der übergebenen + * Zustandsnummer einer der Endzustände dieses Automaten ist. + * + * @param nr Nummer des fraglichen Zustandes. + * @return ob der Zustand ein Endzustand ist. + */ + public boolean istEndzustand(int nr) { + for (int endzustand : endzustaende) { + // Gehe durch alle Endzustände. Wenn nr einer ist... + if (nr == endzustand) { + return true; + } + } + return false; + } + + /** + * Die Methode kann mit einem String als Parameter aufgerufen + * werden, was oft bequemer ist als ein char[]. + * + * @param s String mit dem zu prüfenden Wort. + * @return ob s von diesem Automaten erkannt wird. + */ + public boolean gehoertZuSprache(String s) { + // Hier ist nichts zu implementieren. + return gehoertZuSprache(s.toCharArray()); + } + + /** + * Übergibt dem DEA ein ganzes Wort und ermittelt, ob er es + * akzeptiert oder nicht. + * + * @param eingabe Die komplette zu analysierende Eingabe als + * char[]. + * @returns true, wenn das Wort vom DEA akzeptiert wird; + * false sonst. + */ + public boolean gehoertZuSprache(final char[] wort) { + /* + * Um zu prüfen, ob wort akzeptiert wird, muss der DEA... + * + * - in Zustand 1 beginnen; + * + * - das Eingabewort Zeichen für Zeichen durchgehen; + * + * - prüfen, ob das Zeichen im Alphabet vorkommt; + * + * - nach dessen Position im Alphabet und altem Zustand + * einen neuen Zustand aus der Tabelle uebergaenge wählen; + * + * - false antworten, wenn er im Fehlerzustand landet, + * + * - am Schluss der Eingabe entscheiden, ob der erreichte + * Zustand ein Endzustand ist. + */ + // Startzustand ist immer Nr. 1: + int zustand = 1; + // Gehe durch alle Zeichen durch: + for (char zeichen : wort) { + int alphIndex = indexImAlphabet(zeichen); + // Falls das Zeichen gar nicht im Alphabet ist...: + if (alphIndex == -1) { + return false; + } else { + // wechsle in neuen Zustand: + zustand = uebergaenge[zustand][alphIndex]; + } + // Bin ich im Fehlerzustand? + if (zustand == 0) { + return false; + } + } + // Die Eingabe ist verbraucht. Falls der aktuelle Zustand + // ein Endzustand ist, akzeptieren wir die Eingabe: + return istEndzustand(zustand); + } +} + diff --git a/Quellcodes/AuS_A_DFA_Implementieren/lsg/KGB_007Sucher.java b/Quellcodes/AuS_A_DFA_Implementieren/lsg/KGB_007Sucher.java new file mode 100644 index 0000000..97ecf52 --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/lsg/KGB_007Sucher.java @@ -0,0 +1,92 @@ +/** + * Dieser DFA erkennt Ziffernfolgen, in denen sich irgendwo + * James Bond versteckt hat -- in denen also die Folge 007 + * vorkommt. + * + * Die folgenden Ziffernfolgen sollen beispielsweise akzeptiert + * werden: + * + * 12107117700710012021, + * 007, + * 0071121727, + * 0000000071121727, + * 007007007007, + * 1121271201212007. + * + * Die folgenden Ziffernfolgen sollen nicht akzeptiert werden: + * + * 12107110710012021, + * 07, + * 00, + * 07112172, + * 1121271201212000. + + * Um den Automaten und vor allem die Übergangstabelle + * übersichtlicher zu halten, verwendet er nicht alle Ziffern, + * sondern lediglich das eingeschränkte Alphabet {0, 1, 2, 7}. + * + * Es empfiehlt sich, den Automaten zunächst zu zeichnen, + * die Übergangstabelle auf Papier zu erstellen und dann zu + * programmieren. + * + * @author Urs Lautebach + * @version 2020-Okt + */ + +public class KGB_007Sucher extends EndlicherAutomat { + /* Bei der Implementierung dieser Klasse muss man nur + * die Attribute alphabet, uebergaenge und endzustaende + * geeignet festlegen -- alles andere macht die + * Vaterklasse. + */ + private static final char[] alphabet = new char[] { + '0', '1', '2', '7' + }; + + private static final int[][] uebergaenge = new int[][] { + { 0, 0, 0, 0 }, + { 2, 1, 1, 1 }, + { 3, 1, 1, 1 }, + { 3, 1, 1, 4 }, + { 4, 4, 4, 4 }, + }; + + private static final int[] endzustaende = new int[] { + 4 + }; + + /** + * Instanziiert einen KGB_007Sucher-DFA. + */ + KGB_007Sucher() { + // Aufruf des super-Konstruktors: + super(alphabet, uebergaenge, endzustaende); + } + + /** + * Die main-Methode erlaubt den Aufruf des + * Binaerzahlenpruefers von der Kommandozeile aus mit: + * + * java KGB_007Sucher 127007117 1717771021 + * + * Die Ausgabe lautet dann + * + * Akzeptiert: "127007117" enthält 007. + * FEHLER! "1717771021" enthält kein 007! + */ + public static void main(String[] args) { + // Endlichen Automaten instanziieren: + EndlicherAutomat fahnder = new KGB_007Sucher(); + + for(String wort : args) { + if(fahnder.gehoertZuSprache(wort)) { + System.out.println("Akzeptiert: \"" + wort + + "\" enthält 007."); + } else { + System.out.println("FEHLER! \"" + wort + + "\" enthält kein 007!"); + } + } + } +} + diff --git a/Quellcodes/AuS_A_DFA_Implementieren/lsg/MailadressenChecker.java b/Quellcodes/AuS_A_DFA_Implementieren/lsg/MailadressenChecker.java new file mode 100644 index 0000000..2000865 --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/lsg/MailadressenChecker.java @@ -0,0 +1,93 @@ +/** + * Die Klasse implementiert einen DEA für die Überprüfung äußerst + * einfacher Mailadressen, die nur aus dem Buchstaben b, Punkten + * und dem at-Zeichen bestehen dürfen. Der Nutzername darf fast + * beliebig aufgebaut sein; die Domain muss mindestens zweistufig + * sein und die Toplevel-Domain muss mindestens zwei Zeichen haben. + * + * Gültig sind damit beispielsweise: + * b@b.bb + * b@b.bbbb + * b.b.b@bbb.bbb + * bbbbbb@b.bb + * b@b.b.b.b.bb + * ...@b.bb + * ...bbb@bb.bb + * bbb...@bb.bb + * + * Ungültig sind beispielsweise: + * . + * b + * @ + * bb@ + * b@b.b@bbb + * b@b.b@bb.b + * @bb + * bb@. + * bb@.. + * + * Tatsächlich ist die Überprüfung realer Mailadressen wesentlich + * komlizierter als in diesem Beispiel. + * + * @author Urs Lautebach + * @version 2021-Janar + * + */ +public class MailadressenChecker extends EndlicherAutomat { + /* Bei der Implementierung dieser Klasse muss man nur + * die Attribute alphabet, uebergaenge und endzustaende + * geeignet festlegen -- alles andere macht die + * Vaterklasse. + */ + + private static final char[] alphabet = new char[] { + 'b', '.', '@' + }; + private static final int[][] transitionen = new int[][] { + { 0, 0, 0 }, + { 2, 2, 0 }, + { 2, 2, 3 }, + { 4, 0, 0 }, + { 4, 5, 0 }, + { 6, 0, 0 }, + { 7, 5, 0 }, + { 7, 5, 0 }, + }; + + private static final int[] endzustaende = new int[] { + 7 + }; + + MailadressenChecker() { + // Aufruf des super-Konstruktors: + super(alphabet, transitionen, endzustaende); + } + + + /** + * Die main-Methode erlaubt den Aufruf des + * Binaerzahlenpruefers von der Kommandozeile mit beliebig + * vielen zu prüfenden Strings: + * java MailadressenChecker bbb.b@bb.bb a@b@c + * + * Die Ausgabe lautet dann + * + * Akzeptiert: "bbb.b@bb.bb" ist eine gültige Mailadresse. + * FEHLER! "a@b@c" ist als Mailadresse ungültig! + */ + public static void main(String[] args) { + // Endlichen Automaten instanziieren: + EndlicherAutomat checker = new MailadressenChecker(); + + for(String wort : args) { + if(checker.gehoertZuSprache(wort)) { + System.out.println("Akzeptiert: \"" + wort + + "\" ist eine gültige Mailadresse."); + } else { + System.out.println("FEHLER! \"" + wort + + "\" ist als Mailadresse ungültig!"); + } // end if + } // end for + } // end main +} // end class + diff --git a/Quellcodes/AuS_A_DFA_Implementieren/lsg/README.md b/Quellcodes/AuS_A_DFA_Implementieren/lsg/README.md new file mode 100644 index 0000000..c2be6f0 --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/lsg/README.md @@ -0,0 +1,3 @@ +# Tabellen Dfa + +Implementierung eines allgemeinen tabellengetriebenen DFA als Programmieraufgabe (samt einiger Beispiel-DFA). \ No newline at end of file diff --git a/Quellcodes/AuS_A_DFA_Implementieren/lsg/lehrkraft-liesmich.txt b/Quellcodes/AuS_A_DFA_Implementieren/lsg/lehrkraft-liesmich.txt new file mode 100644 index 0000000..63abfcd --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/lsg/lehrkraft-liesmich.txt @@ -0,0 +1,36 @@ +Dieser Ordner enthält ein Java-Projekt, in dem die Implementierung +eines tabellengetriebenen DEA geübt wird. Es kann mit BlueJ oder +einem anderen Java-Werkzeug bearbeitet werden. + +Das Projekt liegt in drei Ausstattungen vor; Sie als Lehrkraft +entscheiden, welche Variante(n) Sie den Schülern vorlegen. + +Der Ordner... + +.skelett +-------- +enthält das Rohprojekt ohne Lösungshinweise oder Tipps, aber mit +Spezifikation, Arbeitsanweisungen und JUnit-Test. Das Projekt +sollte fehlerfrei compilieren und lauffähig sein, aber natürlich +schlagen die Tests fehl. + +.tipp +----- +enthält das gleiche Projekt, aber zusätzlich mit Tipps für die +Bearbeitung durch Schüler. + +.lsg +---- +enthält die Musterlösung des fertig ausprogrammierten Projekts. +Hier sollten alle Tests erfolgreich laufen. + +Zu bearbeiten sind die Datei EndlicherAutomat.java, in der der +eigentliche Tabellenmechanismus implementiert wird, und eine Auswahl +der Beispiel-DFA für + +Mailadressen +007-Suche +oder Binärzahlen + +bzw. eigene DFA für andere Sprachen nach Wahl. + diff --git a/Quellcodes/AuS_A_DFA_Implementieren/lsg/package.bluej b/Quellcodes/AuS_A_DFA_Implementieren/lsg/package.bluej new file mode 100644 index 0000000..a54f5f2 --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/lsg/package.bluej @@ -0,0 +1,75 @@ +#BlueJ package file +dependency1.from=DFA_BeispieleTest +dependency1.to=BinaerzahlenPruefer +dependency1.type=UsesDependency +dependency2.from=DFA_BeispieleTest +dependency2.to=MailadressenChecker +dependency2.type=UsesDependency +dependency3.from=DFA_BeispieleTest +dependency3.to=KGB_007Sucher +dependency3.type=UsesDependency +editor.fx.0.height=0 +editor.fx.0.width=0 +editor.fx.0.x=0 +editor.fx.0.y=0 +objectbench.height=119 +objectbench.width=770 +package.divider.horizontal=0.5785785785785785 +package.divider.vertical=0.7519685039370079 +package.editor.height=375 +package.editor.width=643 +package.editor.x=100 +package.editor.y=100 +package.frame.height=600 +package.frame.width=800 +package.numDependencies=3 +package.numTargets=6 +package.showExtends=true +package.showUses=true +project.charset=UTF-8 +readme.height=58 +readme.name=@README +readme.width=47 +readme.x=10 +readme.y=10 +target1.height=62 +target1.name=bin +target1.type=PackageTarget +target1.width=80 +target1.x=70 +target1.y=10 +target2.height=50 +target2.name=KGB_007Sucher +target2.showInterface=false +target2.type=ClassTarget +target2.width=140 +target2.x=10 +target2.y=130 +target3.height=50 +target3.name=DFA_BeispieleTest +target3.showInterface=false +target3.type=UnitTestTargetJunit4 +target3.width=160 +target3.x=180 +target3.y=240 +target4.height=50 +target4.name=EndlicherAutomat +target4.showInterface=false +target4.type=ClassTarget +target4.width=160 +target4.x=180 +target4.y=30 +target5.height=50 +target5.name=MailadressenChecker +target5.showInterface=false +target5.type=ClassTarget +target5.width=180 +target5.x=170 +target5.y=130 +target6.height=50 +target6.name=BinaerzahlenPruefer +target6.showInterface=false +target6.type=ClassTarget +target6.width=180 +target6.x=380 +target6.y=130 diff --git a/Quellcodes/AuS_A_DFA_Implementieren/readme.adoc b/Quellcodes/AuS_A_DFA_Implementieren/readme.adoc new file mode 100644 index 0000000..849992d --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/readme.adoc @@ -0,0 +1,11 @@ += Material : + +|=== +|Zuordnung| +|Klassenstufe| +|Bildungsplanbezug | +|Werkzeug| +|Autoren| +|=== + +== Inhalt diff --git a/Quellcodes/AuS_A_DFA_Implementieren/roh/.classpath b/Quellcodes/AuS_A_DFA_Implementieren/roh/.classpath new file mode 100644 index 0000000..a177209 --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/roh/.classpath @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Quellcodes/AuS_A_DFA_Implementieren/roh/.project b/Quellcodes/AuS_A_DFA_Implementieren/roh/.project new file mode 100644 index 0000000..5557916 --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/roh/.project @@ -0,0 +1,17 @@ + + + tabellen-dfa + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/Quellcodes/AuS_A_DFA_Implementieren/roh/BinaerzahlenPruefer.java b/Quellcodes/AuS_A_DFA_Implementieren/roh/BinaerzahlenPruefer.java new file mode 100644 index 0000000..90e8318 --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/roh/BinaerzahlenPruefer.java @@ -0,0 +1,93 @@ +/** + * Implementiert einen DEA, der 0/1-Folgen darauf prüft, ob + * sie "gültige Binärzahlen" darstellen. + * Gültige Binärzahlen im Sinne dieser Aufgabe beginnen mit + * einer 1, auf die eine beliebige Folge von 0/1 folgt. Die + * einzelne Ziffer 0 ist ebenfalls erlaubt, jedoch keine + * Binärzahlen mit "überflüssigen" führenden Nullen. Die + * Binärzahl darf außerdem ein Vorzeichen führen (muss aber + * nicht). + * + * Gültig sind beispielsweise: + * 1 + * 0 + * 101 + * 11 + * 10 + * 1110010010 + * 111 + * -1 + * 1000000000 + * +1 + * 1001001100 + * + * Ungültig sind beispielsweise: + * 123 + * 00 + * hallo + * 0000000011111 + * 1+1 + * Binärzahl + * abc + * 321 + * 01010101010101 + * 0101 + * 01 + * 0101010101 + * + * @author Urs Lautebach + * @version 2021-Januar + */ + + +public class BinaerzahlenPruefer extends EndlicherAutomat { + + // Transitionstabelle anlegen mit den Zuständen auf der + // senkrechten und den Alphabetzeichen auf der Rechtsachse: + private static final int[][] transiTabelle = new int [][] { + }; + + // Alphabet für diesen Automaten als char[] + private static final char[] binaerzAlph = new char[] { + }; + + // Menge der Endzustände: + private static final int[] endzustaende = new int[] { + }; + + /** + * Konstruktor für einen BinaerzahlenPruefer: + */ + public BinaerzahlenPruefer() { + // Aufruf des super-Konstruktors: + super(binaerzAlph, transiTabelle, endzustaende); + } + + /** + * Die main-Methode erlaubt den Aufruf von + * Binaerzahlenpruefer von der Kommandozeile aus mit + * mehreren zu testenden Strings: + * + * java BinaerzahlenPruefer 1110101 0001 +1010 abc + * + * Die Ausgabe lautet dann + * Akzeptiert: "1110101" ist ein gueltiges Binärwort. + * FEHLER! "0001" ist kein gültiges Binärwort! + * Akzeptiert: "+1010" ist ein gueltiges Binärwort. + * FEHLER! "abc" ist kein gültiges Binärwort! + */ + public static void main(String[] args) { + // Endlichen Automaten instanziieren: + EndlicherAutomat binaerzahlenDFA = new BinaerzahlenPruefer(); + + for(String wort : args) { + if(binaerzahlenDFA.gehoertZuSprache(wort)) { + System.out.println("Akzeptiert: \"" + wort + + "\" ist ein gültiges Binärwort."); + } else { + System.out.println("FEHLER! \"" + wort + + "\" ist kein gültiges Binärwort!"); + } + } + } +} diff --git a/Quellcodes/AuS_A_DFA_Implementieren/roh/DEA_BeispieleTest.java b/Quellcodes/AuS_A_DFA_Implementieren/roh/DEA_BeispieleTest.java new file mode 100644 index 0000000..d544eb0 --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/roh/DEA_BeispieleTest.java @@ -0,0 +1,104 @@ +import static org.junit.Assert.*; +import org.junit.Test; +public class DEA_BeispieleTest { + + public DEA_BeispieleTest() { + } + + @Test + public void testBinaerzahlenPruefer() { + BinaerzahlenPruefer binZPruefer = new BinaerzahlenPruefer(); + assertTrue(binZPruefer.gehoertZuSprache("1")); + assertTrue(binZPruefer.gehoertZuSprache("0")); + assertTrue(binZPruefer.gehoertZuSprache("+1")); + assertTrue(binZPruefer.gehoertZuSprache("+0")); + assertTrue(binZPruefer.gehoertZuSprache("-1")); + assertTrue(binZPruefer.gehoertZuSprache("-0")); + assertTrue(binZPruefer.gehoertZuSprache("11")); + assertTrue(binZPruefer.gehoertZuSprache("10")); + assertTrue(binZPruefer.gehoertZuSprache("+11")); + assertTrue(binZPruefer.gehoertZuSprache("-11")); + assertTrue(binZPruefer.gehoertZuSprache("+10")); + assertTrue(binZPruefer.gehoertZuSprache("1111")); + assertTrue(binZPruefer.gehoertZuSprache("1000")); + assertTrue(binZPruefer.gehoertZuSprache("10100101010111000")); + assertTrue(binZPruefer.gehoertZuSprache("1111111")); + + assertFalse(binZPruefer.gehoertZuSprache("")); + assertFalse(binZPruefer.gehoertZuSprache("+")); + assertFalse(binZPruefer.gehoertZuSprache("-")); + assertFalse(binZPruefer.gehoertZuSprache("x")); + assertFalse(binZPruefer.gehoertZuSprache("binärzahl")); + assertFalse(binZPruefer.gehoertZuSprache("00")); + assertFalse(binZPruefer.gehoertZuSprache("+00")); + assertFalse(binZPruefer.gehoertZuSprache("0+0")); + assertFalse(binZPruefer.gehoertZuSprache("123")); + assertFalse(binZPruefer.gehoertZuSprache("0011")); + assertFalse(binZPruefer.gehoertZuSprache("011")); + assertFalse(binZPruefer.gehoertZuSprache("111111+")); + assertFalse(binZPruefer.gehoertZuSprache("000000+")); + assertFalse(binZPruefer.gehoertZuSprache("1+1")); + assertFalse(binZPruefer.gehoertZuSprache("0+1")); + assertFalse(binZPruefer.gehoertZuSprache("1+0")); + } + + @Test + public void testMailadressenChecker() { + MailadressenChecker mailAdrPruefer = new MailadressenChecker(); + assertTrue(mailAdrPruefer.gehoertZuSprache("b@b.bb")); + assertTrue(mailAdrPruefer.gehoertZuSprache("b@b.bbbb")); + assertTrue(mailAdrPruefer.gehoertZuSprache("b.b.b@bbb.bbb")); + assertTrue(mailAdrPruefer.gehoertZuSprache("bbbbbb@b.bb")); + assertTrue(mailAdrPruefer.gehoertZuSprache("b@b.b.b.b.bb")); + assertTrue(mailAdrPruefer.gehoertZuSprache("...@b.bb")); + assertTrue(mailAdrPruefer.gehoertZuSprache("...bbb@bb.bb")); + assertTrue(mailAdrPruefer.gehoertZuSprache("bbb...@bb.bb")); + + assertFalse(mailAdrPruefer.gehoertZuSprache("")); + assertFalse(mailAdrPruefer.gehoertZuSprache(".")); + assertFalse(mailAdrPruefer.gehoertZuSprache("b")); + assertFalse(mailAdrPruefer.gehoertZuSprache("@")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bb@")); + assertFalse(mailAdrPruefer.gehoertZuSprache("b@b.b@bbb")); + assertFalse(mailAdrPruefer.gehoertZuSprache("@bb")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bb@.")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bb@..")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bb@bb.b")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bb@bb.bb.b")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bbb@bbb")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bb.bb.bb.bb")); + assertFalse(mailAdrPruefer.gehoertZuSprache( + "bbb@bb.bb.bb@bb.bb")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bbb@bbb..bbb")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bbb@bbb.bbb..")); + } + + @Test + public void test007Sucher() { + KGB_007Sucher detektiv = new KGB_007Sucher(); + assertTrue(detektiv.gehoertZuSprache("007")); + assertTrue(detektiv.gehoertZuSprache("1007")); + assertTrue(detektiv.gehoertZuSprache("0071")); + assertTrue(detektiv.gehoertZuSprache("007007")); + assertTrue(detektiv.gehoertZuSprache("00007")); + assertTrue(detektiv.gehoertZuSprache("0000712")); + assertTrue(detektiv.gehoertZuSprache("2121200777")); + assertTrue(detektiv.gehoertZuSprache("0077")); + assertTrue(detektiv.gehoertZuSprache("212121007000000")); + assertTrue(detektiv.gehoertZuSprache("00712772712717")); + + assertFalse(detektiv.gehoertZuSprache("")); + assertFalse(detektiv.gehoertZuSprache("0")); + assertFalse(detektiv.gehoertZuSprache("1")); + assertFalse(detektiv.gehoertZuSprache("2")); + assertFalse(detektiv.gehoertZuSprache("7")); + assertFalse(detektiv.gehoertZuSprache("07")); + assertFalse(detektiv.gehoertZuSprache("001")); + assertFalse(detektiv.gehoertZuSprache("002")); + assertFalse(detektiv.gehoertZuSprache("000")); + assertFalse(detektiv.gehoertZuSprache("001002")); + assertFalse(detektiv.gehoertZuSprache("7001")); + assertFalse(detektiv.gehoertZuSprache("0707")); + } +} + diff --git a/Quellcodes/AuS_A_DFA_Implementieren/roh/EndlicherAutomat.java b/Quellcodes/AuS_A_DFA_Implementieren/roh/EndlicherAutomat.java new file mode 100644 index 0000000..f18fe3b --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/roh/EndlicherAutomat.java @@ -0,0 +1,146 @@ +/** + * Implementiert einen tabellengetriebenen endlichen Automaten. + * Instanzen dieser Klasse sind DEAs, die Wörter über einem + * vorgegebenen Alphabet akzeptieren oder ablehnen können. + * + * Die Aufgabe wurde vom schriftlichen Abitur 2020 in + * Baden-Württemberg inspiriert. + * + * @author Urs Lautebach + * @version 2020-Oktober + */ +public class EndlicherAutomat { + private int [][] uebergaenge = null; + private int[] endzustaende = null; + private char[] alphabet = null; + + /** + * Konstruiert einen tabellengetriebenen DEA. Nach dem + * erfolgreichen Aufruf dieses Konstruktors kann man den DEA + * anschließend durch einen Aufruf der Methode + * gehoertZuSprache(String):boolean + * beliebig viele Wörter testen lassen. + * + * @param tabelle Transitionstabelle des DEA. Die erste + * Dimension bezeichnet die Zustände; die zweite Dimension die + * Eingabezeichen. Die Einträge der Tabelle bezeichnen jeweils + * den Zustand, in den der Automat (abhängig vom vorherigen + * Zustand und der Eingabe) wechseln soll. + * + * Der Zustand 0 gilt als Fehlerzustand. Zustand 1 ist immer + * Startzustand (so dass der Startzustand, anders als bei der + * formalen Beschreibung eines DEA, nicht explizit angegeben + * werden muss). + * + * @param finalStates Ein int[], das die Endzustände enthält. + * + * @param alphabet ein Array von char, das das Alphabet des + * Automaten enthält. Die Reihenfolge der Zeichen im Alphabet + * muss der Reihenfolge der Zeichen an der entsprechenden + * Achse der Transitionstabelle entsprechen. + */ + EndlicherAutomat(final char[] alphabet, + final int[][] uebergaenge, final int[] endzustaende ) { + // Die Methode plausibilitaetPruefen ist fertig vorgegeben; + // sie fängt einige offensichtliche Fehler ab: + plausibilitaetPruefen(); + } + + /** + * Die Methode prüft, ob die Attribute transitionstabelle, + * alphabet und endzustaende zueinander passen. Sie ist fertig + * implementiert und dafür gedacht, am Ende des Konstruktors + * aufgerufen zu werden. + */ + private void plausibilitaetPruefen() { + if (uebergaenge == null) { + throw new IllegalArgumentException( + "Die Übergangstabelle ist null oder leer!"); + } + if (alphabet == null) { + throw new IllegalArgumentException( + "Das Alphabet ist null oder leer!"); + } + if (endzustaende == null) { + throw new IllegalArgumentException( + "endzustaende ist null oder leer!"); + } + int tabelleHoehe = this.uebergaenge.length; + int tabelleBreite = uebergaenge[0].length; + // Hat die Übergangstabelle eine Zeile pro Alphabetzeichen? + if (tabelleBreite != alphabet.length) { + throw new IllegalArgumentException("Das Alphabet hat " + + alphabet.length + " Zeichen, die " + + " Übergangstabelle aber " + tabelleBreite + + " Spalten!"); + } + // Sind alle Zeilen der Transitionstabelle gleich lang? + for (int[] zeile : uebergaenge) { + if (zeile.length != tabelleBreite) { + throw new IllegalArgumentException( + "Übergangstabelle ist nicht rechteckig!"); + } + } + // Hat einer der Endzustände eine unplausible Nummer? + for (int zustand : endzustaende) { + if (zustand < 1 || zustand > tabelleHoehe - 1) { + throw new IllegalArgumentException( + "Der Endzustand " + zustand + + " kommt in der Übergangstabelle nicht vor!"); + } + } + } + + /** + * Ermittelt die Nummer (den Index) des angegebenen Zeichens + * innerhalb des Alphabetes dieses Automaten. + * + * @param input Zeichen, dessen Position ermittelt werden soll. + * @returns Position des Zeichens input innerhalb des Alphabets + * dieses Automaten; oder -1, falls es im Alphabet nicht + * enthalten ist. + */ + public int indexImAlphabet(char input) { + // TODO: Implementieren Sie diese Methode. + return -54321; // dummy return + } + + /** + * Überprüft, ob der Zustand mit der übergebenen + * Zustandsnummer einer der Endzustände dieses Automaten ist. + * + * @param nr Nummer des fraglichen Zustandes. + * @return ob der Zustand ein Endzustand ist. + */ + public boolean istEndzustand(int nr) { + // TODO: Implementieren Sie diese Methode. + return false; // dummy return + } + + /** + * Die Methode kann mit einem String als Parameter aufgerufen + * werden, was oft bequemer ist als ein char[]. + * + * @param s String mit dem zu prüfenden Wort. + * @return ob s von diesem Automaten erkannt wird. + */ + public boolean gehoertZuSprache(String s) { + // Hier ist nichts zu implementieren. + return gehoertZuSprache(s.toCharArray()); + } + + /** + * Übergibt dem DEA ein ganzes Wort und ermittelt, ob er es + * akzeptiert oder nicht. + * + * @param eingabe Die komplette zu analysierende Eingabe als + * char[]. + * @returns true, wenn das Wort vom DEA akzeptiert wird; + * false sonst. + */ + public boolean gehoertZuSprache(final char[] wort) { + // TODO: Implementieren Sie diese Methode. + return false; // dummy return + } +} + diff --git a/Quellcodes/AuS_A_DFA_Implementieren/roh/KGB_007Sucher.java b/Quellcodes/AuS_A_DFA_Implementieren/roh/KGB_007Sucher.java new file mode 100644 index 0000000..04481cb --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/roh/KGB_007Sucher.java @@ -0,0 +1,80 @@ +/** + * Dieser DFA erkennt Ziffernfolgen, in denen sich irgendwo + * James Bond versteckt hat -- in denen also die Folge 007 + * vorkommt. + * + * Die folgenden Ziffernfolgen sollen beispielsweise akzeptiert + * werden: + * + * 12107117700710012021, + * 007, + * 0071121727, + * 0000000071121727, + * 007007007007, + * 1121271201212007. + * + * Die folgenden Ziffernfolgen sollen nicht akzeptiert werden: + * + * 12107110710012021, + * 07, + * 00, + * 07112172, + * 1121271201212000. + + * Um den Automaten und vor allem die Übergangstabelle + * übersichtlicher zu halten, verwendet er nicht alle Ziffern, + * sondern lediglich das eingeschränkte Alphabet {0, 1, 2, 7}. + * + * Es empfiehlt sich, den Automaten zunächst zu zeichnen, + * die Übergangstabelle auf Papier zu erstellen und dann zu + * programmieren. + * + * @author Urs Lautebach + * @version 2020-Okt + */ + +public class KGB_007Sucher extends EndlicherAutomat { + private static final char[] alphabet = new char[] { + }; + + private static final int[][] uebergaenge = new int[][] { + }; + + private static final int[] endzustaende = new int[] { + }; + + /** + * Instanziiert einen KGB_007Sucher-DFA. + */ + KGB_007Sucher() { + // Aufruf des super-Konstruktors: + super(alphabet, uebergaenge, endzustaende); + } + + /** + * Die main-Methode erlaubt den Aufruf des + * Binaerzahlenpruefers von der Kommandozeile aus mit: + * + * java KGB_007Sucher 127007117 1717771021 + * + * Die Ausgabe lautet dann + * + * Akzeptiert: "127007117" enthält 007. + * FEHLER! "1717771021" enthält kein 007! + */ + public static void main(String[] args) { + // Endlichen Automaten instanziieren: + EndlicherAutomat fahnder = new KGB_007Sucher(); + + for(String wort : args) { + if(fahnder.gehoertZuSprache(wort)) { + System.out.println("Akzeptiert: \"" + wort + + "\" enthält 007."); + } else { + System.out.println("FEHLER! \"" + wort + + "\" enthält kein 007!"); + } + } + } +} + diff --git a/Quellcodes/AuS_A_DFA_Implementieren/roh/MailadressenChecker.java b/Quellcodes/AuS_A_DFA_Implementieren/roh/MailadressenChecker.java new file mode 100644 index 0000000..a89776b --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/roh/MailadressenChecker.java @@ -0,0 +1,78 @@ +/** + * Die Klasse implementiert einen DEA für die Überprüfung äußerst + * einfacher Mailadressen, die nur aus dem Buchstaben b, Punkten + * und dem at-Zeichen bestehen dürfen. Der Nutzername darf fast + * beliebig aufgebaut sein; die Domain muss mindestens zweistufig + * sein und die Toplevel-Domain muss mindestens zwei Zeichen haben. + * + * Gültig sind damit beispielsweise: + * b@b.bb + * b@b.bbbb + * b.b.b@bbb.bbb + * bbbbbb@b.bb + * b@b.b.b.b.bb + * ...@b.bb + * ...bbb@bb.bb + * bbb...@bb.bb + * + * Ungültig sind beispielsweise: + * . + * b + * @ + * bb@ + * b@b.b@bbb + * b@b.b@bb.b + * @bb + * bb@. + * bb@.. + * + * Tatsächlich ist die Überprüfung realer Mailadressen wesentlich + * komlizierter als in diesem Beispiel. + * + * @author Urs Lautebach + * @version 2021-Janar + * + */ +public class MailadressenChecker extends EndlicherAutomat { + + private static final char[] alphabet = new char[] { + }; + private static final int[][] transitionen = new int[][] { + }; + + private static final int[] endzustaende = new int[] { + }; + + MailadressenChecker() { + // Aufruf des super-Konstruktors: + super(alphabet, transitionen, endzustaende); + } + + + /** + * Die main-Methode erlaubt den Aufruf des + * Binaerzahlenpruefers von der Kommandozeile mit beliebig + * vielen zu prüfenden Strings: + * java MailadressenChecker bbb.b@bb.bb a@b@c + * + * Die Ausgabe lautet dann + * + * Akzeptiert: "bbb.b@bb.bb" ist eine gültige Mailadresse. + * FEHLER! "a@b@c" ist als Mailadresse ungültig! + */ + public static void main(String[] args) { + // Endlichen Automaten instanziieren: + EndlicherAutomat checker = new MailadressenChecker(); + + for(String wort : args) { + if(checker.gehoertZuSprache(wort)) { + System.out.println("Akzeptiert: \"" + wort + + "\" ist eine gültige Mailadresse."); + } else { + System.out.println("FEHLER! \"" + wort + + "\" ist als Mailadresse ungültig!"); + } // end if + } // end for + } // end main +} // end class + diff --git a/Quellcodes/AuS_A_DFA_Implementieren/roh/README.md b/Quellcodes/AuS_A_DFA_Implementieren/roh/README.md new file mode 100644 index 0000000..c2be6f0 --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/roh/README.md @@ -0,0 +1,3 @@ +# Tabellen Dfa + +Implementierung eines allgemeinen tabellengetriebenen DFA als Programmieraufgabe (samt einiger Beispiel-DFA). \ No newline at end of file diff --git a/Quellcodes/AuS_A_DFA_Implementieren/roh/lehrkraft-liesmich.txt b/Quellcodes/AuS_A_DFA_Implementieren/roh/lehrkraft-liesmich.txt new file mode 100644 index 0000000..63abfcd --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/roh/lehrkraft-liesmich.txt @@ -0,0 +1,36 @@ +Dieser Ordner enthält ein Java-Projekt, in dem die Implementierung +eines tabellengetriebenen DEA geübt wird. Es kann mit BlueJ oder +einem anderen Java-Werkzeug bearbeitet werden. + +Das Projekt liegt in drei Ausstattungen vor; Sie als Lehrkraft +entscheiden, welche Variante(n) Sie den Schülern vorlegen. + +Der Ordner... + +.skelett +-------- +enthält das Rohprojekt ohne Lösungshinweise oder Tipps, aber mit +Spezifikation, Arbeitsanweisungen und JUnit-Test. Das Projekt +sollte fehlerfrei compilieren und lauffähig sein, aber natürlich +schlagen die Tests fehl. + +.tipp +----- +enthält das gleiche Projekt, aber zusätzlich mit Tipps für die +Bearbeitung durch Schüler. + +.lsg +---- +enthält die Musterlösung des fertig ausprogrammierten Projekts. +Hier sollten alle Tests erfolgreich laufen. + +Zu bearbeiten sind die Datei EndlicherAutomat.java, in der der +eigentliche Tabellenmechanismus implementiert wird, und eine Auswahl +der Beispiel-DFA für + +Mailadressen +007-Suche +oder Binärzahlen + +bzw. eigene DFA für andere Sprachen nach Wahl. + diff --git a/Quellcodes/AuS_A_DFA_Implementieren/roh/package.bluej b/Quellcodes/AuS_A_DFA_Implementieren/roh/package.bluej new file mode 100644 index 0000000..a54f5f2 --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/roh/package.bluej @@ -0,0 +1,75 @@ +#BlueJ package file +dependency1.from=DFA_BeispieleTest +dependency1.to=BinaerzahlenPruefer +dependency1.type=UsesDependency +dependency2.from=DFA_BeispieleTest +dependency2.to=MailadressenChecker +dependency2.type=UsesDependency +dependency3.from=DFA_BeispieleTest +dependency3.to=KGB_007Sucher +dependency3.type=UsesDependency +editor.fx.0.height=0 +editor.fx.0.width=0 +editor.fx.0.x=0 +editor.fx.0.y=0 +objectbench.height=119 +objectbench.width=770 +package.divider.horizontal=0.5785785785785785 +package.divider.vertical=0.7519685039370079 +package.editor.height=375 +package.editor.width=643 +package.editor.x=100 +package.editor.y=100 +package.frame.height=600 +package.frame.width=800 +package.numDependencies=3 +package.numTargets=6 +package.showExtends=true +package.showUses=true +project.charset=UTF-8 +readme.height=58 +readme.name=@README +readme.width=47 +readme.x=10 +readme.y=10 +target1.height=62 +target1.name=bin +target1.type=PackageTarget +target1.width=80 +target1.x=70 +target1.y=10 +target2.height=50 +target2.name=KGB_007Sucher +target2.showInterface=false +target2.type=ClassTarget +target2.width=140 +target2.x=10 +target2.y=130 +target3.height=50 +target3.name=DFA_BeispieleTest +target3.showInterface=false +target3.type=UnitTestTargetJunit4 +target3.width=160 +target3.x=180 +target3.y=240 +target4.height=50 +target4.name=EndlicherAutomat +target4.showInterface=false +target4.type=ClassTarget +target4.width=160 +target4.x=180 +target4.y=30 +target5.height=50 +target5.name=MailadressenChecker +target5.showInterface=false +target5.type=ClassTarget +target5.width=180 +target5.x=170 +target5.y=130 +target6.height=50 +target6.name=BinaerzahlenPruefer +target6.showInterface=false +target6.type=ClassTarget +target6.width=180 +target6.x=380 +target6.y=130 diff --git a/Quellcodes/AuS_A_DFA_Implementieren/tipp/.classpath b/Quellcodes/AuS_A_DFA_Implementieren/tipp/.classpath new file mode 100644 index 0000000..a177209 --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/tipp/.classpath @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Quellcodes/AuS_A_DFA_Implementieren/tipp/.project b/Quellcodes/AuS_A_DFA_Implementieren/tipp/.project new file mode 100644 index 0000000..5557916 --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/tipp/.project @@ -0,0 +1,17 @@ + + + tabellen-dfa + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/Quellcodes/AuS_A_DFA_Implementieren/tipp/BinaerzahlenPruefer.java b/Quellcodes/AuS_A_DFA_Implementieren/tipp/BinaerzahlenPruefer.java new file mode 100644 index 0000000..b015504 --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/tipp/BinaerzahlenPruefer.java @@ -0,0 +1,102 @@ +/** + * Implementiert einen DEA, der 0/1-Folgen darauf prüft, ob + * sie "gültige Binärzahlen" darstellen. + * Gültige Binärzahlen im Sinne dieser Aufgabe beginnen mit + * einer 1, auf die eine beliebige Folge von 0/1 folgt. Die + * einzelne Ziffer 0 ist ebenfalls erlaubt, jedoch keine + * Binärzahlen mit "überflüssigen" führenden Nullen. Die + * Binärzahl darf außerdem ein Vorzeichen führen (muss aber + * nicht). + * + * Gültig sind beispielsweise: + * 1 + * 0 + * 101 + * 11 + * 10 + * 1110010010 + * 111 + * -1 + * 1000000000 + * +1 + * 1001001100 + * + * Ungültig sind beispielsweise: + * 123 + * 00 + * hallo + * 0000000011111 + * 1+1 + * Binärzahl + * abc + * 321 + * 01010101010101 + * 0101 + * 01 + * 0101010101 + * + * @author Urs Lautebach + * @version 2021-Januar + */ + + +public class BinaerzahlenPruefer extends EndlicherAutomat { + /* Bei der Implementierung dieser Klasse muss man nur + * die Attribute alphabet, uebergaenge und endzustaende + * geeignet festlegen -- alles andere macht die + * Vaterklasse "EndlicherAutomat.java". + * + * Dort ist auch erklärt, wie die Nummerierung der + * Zustände, des Startzustand und der Übergangstabelle + * zu verstehen sind. + */ + + // Transitionstabelle anlegen mit den Zuständen auf der + // senkrechten und den Alphabetzeichen auf der Rechtsachse: + private static final int[][] transiTabelle = new int [][] { + }; + + // Alphabet für diesen Automaten als char[] + private static final char[] binaerzAlph = new char[] { + }; + + // Menge der Endzustände: + private static final int[] endzustaende = new int[] { + }; + + /** + * Konstruktor für einen BinaerzahlenPruefer: + */ + public BinaerzahlenPruefer() { + // Aufruf des super-Konstruktors: + super(binaerzAlph, transiTabelle, endzustaende); + } + + /** + * Die main-Methode erlaubt den Aufruf von + * Binaerzahlenpruefer von der Kommandozeile aus mit + * mehreren zu testenden Strings: + * + * java BinaerzahlenPruefer 1110101 0001 +1010 abc + * + * Die Ausgabe lautet dann + * Akzeptiert: "1110101" ist ein gueltiges Binärwort. + * FEHLER! "0001" ist kein gültiges Binärwort! + * Akzeptiert: "+1010" ist ein gueltiges Binärwort. + * FEHLER! "abc" ist kein gültiges Binärwort! + */ + public static void main(String[] args) { + // Endlichen Automaten instanziieren: + EndlicherAutomat binaerzahlenDFA = new BinaerzahlenPruefer(); + + for(String wort : args) { + if(binaerzahlenDFA.gehoertZuSprache(wort)) { + System.out.println("Akzeptiert: \"" + wort + + "\" ist ein gültiges Binärwort."); + } else { + System.out.println("FEHLER! \"" + wort + + "\" ist kein gültiges Binärwort!"); + } + } + } +} diff --git a/Quellcodes/AuS_A_DFA_Implementieren/tipp/DEA_BeispieleTest.java b/Quellcodes/AuS_A_DFA_Implementieren/tipp/DEA_BeispieleTest.java new file mode 100644 index 0000000..d544eb0 --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/tipp/DEA_BeispieleTest.java @@ -0,0 +1,104 @@ +import static org.junit.Assert.*; +import org.junit.Test; +public class DEA_BeispieleTest { + + public DEA_BeispieleTest() { + } + + @Test + public void testBinaerzahlenPruefer() { + BinaerzahlenPruefer binZPruefer = new BinaerzahlenPruefer(); + assertTrue(binZPruefer.gehoertZuSprache("1")); + assertTrue(binZPruefer.gehoertZuSprache("0")); + assertTrue(binZPruefer.gehoertZuSprache("+1")); + assertTrue(binZPruefer.gehoertZuSprache("+0")); + assertTrue(binZPruefer.gehoertZuSprache("-1")); + assertTrue(binZPruefer.gehoertZuSprache("-0")); + assertTrue(binZPruefer.gehoertZuSprache("11")); + assertTrue(binZPruefer.gehoertZuSprache("10")); + assertTrue(binZPruefer.gehoertZuSprache("+11")); + assertTrue(binZPruefer.gehoertZuSprache("-11")); + assertTrue(binZPruefer.gehoertZuSprache("+10")); + assertTrue(binZPruefer.gehoertZuSprache("1111")); + assertTrue(binZPruefer.gehoertZuSprache("1000")); + assertTrue(binZPruefer.gehoertZuSprache("10100101010111000")); + assertTrue(binZPruefer.gehoertZuSprache("1111111")); + + assertFalse(binZPruefer.gehoertZuSprache("")); + assertFalse(binZPruefer.gehoertZuSprache("+")); + assertFalse(binZPruefer.gehoertZuSprache("-")); + assertFalse(binZPruefer.gehoertZuSprache("x")); + assertFalse(binZPruefer.gehoertZuSprache("binärzahl")); + assertFalse(binZPruefer.gehoertZuSprache("00")); + assertFalse(binZPruefer.gehoertZuSprache("+00")); + assertFalse(binZPruefer.gehoertZuSprache("0+0")); + assertFalse(binZPruefer.gehoertZuSprache("123")); + assertFalse(binZPruefer.gehoertZuSprache("0011")); + assertFalse(binZPruefer.gehoertZuSprache("011")); + assertFalse(binZPruefer.gehoertZuSprache("111111+")); + assertFalse(binZPruefer.gehoertZuSprache("000000+")); + assertFalse(binZPruefer.gehoertZuSprache("1+1")); + assertFalse(binZPruefer.gehoertZuSprache("0+1")); + assertFalse(binZPruefer.gehoertZuSprache("1+0")); + } + + @Test + public void testMailadressenChecker() { + MailadressenChecker mailAdrPruefer = new MailadressenChecker(); + assertTrue(mailAdrPruefer.gehoertZuSprache("b@b.bb")); + assertTrue(mailAdrPruefer.gehoertZuSprache("b@b.bbbb")); + assertTrue(mailAdrPruefer.gehoertZuSprache("b.b.b@bbb.bbb")); + assertTrue(mailAdrPruefer.gehoertZuSprache("bbbbbb@b.bb")); + assertTrue(mailAdrPruefer.gehoertZuSprache("b@b.b.b.b.bb")); + assertTrue(mailAdrPruefer.gehoertZuSprache("...@b.bb")); + assertTrue(mailAdrPruefer.gehoertZuSprache("...bbb@bb.bb")); + assertTrue(mailAdrPruefer.gehoertZuSprache("bbb...@bb.bb")); + + assertFalse(mailAdrPruefer.gehoertZuSprache("")); + assertFalse(mailAdrPruefer.gehoertZuSprache(".")); + assertFalse(mailAdrPruefer.gehoertZuSprache("b")); + assertFalse(mailAdrPruefer.gehoertZuSprache("@")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bb@")); + assertFalse(mailAdrPruefer.gehoertZuSprache("b@b.b@bbb")); + assertFalse(mailAdrPruefer.gehoertZuSprache("@bb")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bb@.")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bb@..")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bb@bb.b")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bb@bb.bb.b")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bbb@bbb")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bb.bb.bb.bb")); + assertFalse(mailAdrPruefer.gehoertZuSprache( + "bbb@bb.bb.bb@bb.bb")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bbb@bbb..bbb")); + assertFalse(mailAdrPruefer.gehoertZuSprache("bbb@bbb.bbb..")); + } + + @Test + public void test007Sucher() { + KGB_007Sucher detektiv = new KGB_007Sucher(); + assertTrue(detektiv.gehoertZuSprache("007")); + assertTrue(detektiv.gehoertZuSprache("1007")); + assertTrue(detektiv.gehoertZuSprache("0071")); + assertTrue(detektiv.gehoertZuSprache("007007")); + assertTrue(detektiv.gehoertZuSprache("00007")); + assertTrue(detektiv.gehoertZuSprache("0000712")); + assertTrue(detektiv.gehoertZuSprache("2121200777")); + assertTrue(detektiv.gehoertZuSprache("0077")); + assertTrue(detektiv.gehoertZuSprache("212121007000000")); + assertTrue(detektiv.gehoertZuSprache("00712772712717")); + + assertFalse(detektiv.gehoertZuSprache("")); + assertFalse(detektiv.gehoertZuSprache("0")); + assertFalse(detektiv.gehoertZuSprache("1")); + assertFalse(detektiv.gehoertZuSprache("2")); + assertFalse(detektiv.gehoertZuSprache("7")); + assertFalse(detektiv.gehoertZuSprache("07")); + assertFalse(detektiv.gehoertZuSprache("001")); + assertFalse(detektiv.gehoertZuSprache("002")); + assertFalse(detektiv.gehoertZuSprache("000")); + assertFalse(detektiv.gehoertZuSprache("001002")); + assertFalse(detektiv.gehoertZuSprache("7001")); + assertFalse(detektiv.gehoertZuSprache("0707")); + } +} + diff --git a/Quellcodes/AuS_A_DFA_Implementieren/tipp/EndlicherAutomat.java b/Quellcodes/AuS_A_DFA_Implementieren/tipp/EndlicherAutomat.java new file mode 100644 index 0000000..a1015bd --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/tipp/EndlicherAutomat.java @@ -0,0 +1,167 @@ +/** + * Implementiert einen tabellengetriebenen endlichen Automaten. + * Instanzen dieser Klasse sind DEAs, die Wörter über einem + * vorgegebenen Alphabet akzeptieren oder ablehnen können. + * + * Die Aufgabe wurde vom schriftlichen Abitur 2020 in + * Baden-Württemberg inspiriert. + * + * @author Urs Lautebach + * @version 2020-Oktober + */ +public class EndlicherAutomat { + private int [][] uebergaenge = null; + private int[] endzustaende = null; + private char[] alphabet = null; + + /** + * Konstruiert einen tabellengetriebenen DEA. Nach dem + * erfolgreichen Aufruf dieses Konstruktors kann man den DEA + * anschließend durch einen Aufruf der Methode + * gehoertZuSprache(String):boolean + * beliebig viele Wörter testen lassen. + * + * @param tabelle Transitionstabelle des DEA. Die erste + * Dimension bezeichnet die Zustände; die zweite Dimension die + * Eingabezeichen. Die Einträge der Tabelle bezeichnen jeweils + * den Zustand, in den der Automat (abhängig vom vorherigen + * Zustand und der Eingabe) wechseln soll. + * + * Der Zustand 0 gilt als Fehlerzustand. Zustand 1 ist immer + * Startzustand (so dass der Startzustand, anders als bei der + * formalen Beschreibung eines DEA, nicht explizit angegeben + * werden muss). + * + * @param finalStates Ein int[], das die Endzustände enthält. + * + * @param alphabet ein Array von char, das das Alphabet des + * Automaten enthält. Die Reihenfolge der Zeichen im Alphabet + * muss der Reihenfolge der Zeichen an der entsprechenden + * Achse der Transitionstabelle entsprechen. + */ + EndlicherAutomat(final char[] alphabet, + final int[][] uebergaenge, final int[] endzustaende ) { + // Die Methode plausibilitaetPruefen ist fertig vorgegeben; + // sie fängt einige offensichtliche Fehler ab: + plausibilitaetPruefen(); + } + + /** + * Die Methode prüft, ob die Attribute transitionstabelle, + * alphabet und endzustaende zueinander passen. Sie ist fertig + * implementiert und dafür gedacht, am Ende des Konstruktors + * aufgerufen zu werden. + */ + private void plausibilitaetPruefen() { + if (uebergaenge == null) { + throw new IllegalArgumentException( + "Die Übergangstabelle ist null oder leer!"); + } + if (alphabet == null) { + throw new IllegalArgumentException( + "Das Alphabet ist null oder leer!"); + } + if (endzustaende == null) { + throw new IllegalArgumentException( + "endzustaende ist null oder leer!"); + } + int tabelleHoehe = this.uebergaenge.length; + int tabelleBreite = uebergaenge[0].length; + // Hat die Übergangstabelle eine Zeile pro Alphabetzeichen? + if (tabelleBreite != alphabet.length) { + throw new IllegalArgumentException("Das Alphabet hat " + + alphabet.length + " Zeichen, die " + + " Übergangstabelle aber " + tabelleBreite + + " Spalten!"); + } + // Sind alle Zeilen der Transitionstabelle gleich lang? + for (int[] zeile : uebergaenge) { + if (zeile.length != tabelleBreite) { + throw new IllegalArgumentException( + "Übergangstabelle ist nicht rechteckig!"); + } + } + // Hat einer der Endzustände eine unplausible Nummer? + for (int zustand : endzustaende) { + if (zustand < 1 || zustand > tabelleHoehe - 1) { + throw new IllegalArgumentException( + "Der Endzustand " + zustand + + " kommt in der Übergangstabelle nicht vor!"); + } + } + } + + /** + * Ermittelt die Nummer (den Index) des angegebenen Zeichens + * innerhalb des Alphabetes dieses Automaten. + * + * @param input Zeichen, dessen Position ermittelt werden soll. + * @returns Position des Zeichens input innerhalb des Alphabets + * dieses Automaten; oder -1, falls es im Alphabet nicht + * enthalten ist. + */ + public int indexImAlphabet(char input) { + // TODO: Implementieren Sie diese Methode. + /* Man geht das Alphabet durch und prüft, welches seiner + * Zeichen dem "input" entspricht. Falls man es findet, + * gibt man dessen Index zurück. + */ + return -54321; // dummy return + } + + /** + * Überprüft, ob der Zustand mit der übergebenen + * Zustandsnummer einer der Endzustände dieses Automaten ist. + * + * @param nr Nummer des fraglichen Zustandes. + * @return ob der Zustand ein Endzustand ist. + */ + public boolean istEndzustand(int nr) { + // TODO: Implementieren Sie diese Methode. + return false; // dummy return + } + + /** + * Die Methode kann mit einem String als Parameter aufgerufen + * werden, was oft bequemer ist als ein char[]. + * + * @param s String mit dem zu prüfenden Wort. + * @return ob s von diesem Automaten erkannt wird. + */ + public boolean gehoertZuSprache(String s) { + // Hier ist nichts zu implementieren. + return gehoertZuSprache(s.toCharArray()); + } + + /** + * Übergibt dem DEA ein ganzes Wort und ermittelt, ob er es + * akzeptiert oder nicht. + * + * @param eingabe Die komplette zu analysierende Eingabe als + * char[]. + * @returns true, wenn das Wort vom DEA akzeptiert wird; + * false sonst. + */ + public boolean gehoertZuSprache(final char[] wort) { + // TODO: Implementieren Sie diese Methode. + /* + * Um zu prüfen, ob wort akzeptiert wird, muss der DEA... + * + * - in Zustand 1 beginnen; + * + * - das Eingabewort Zeichen für Zeichen durchgehen; + * + * - prüfen, ob das Zeichen im Alphabet vorkommt; + * + * - nach dessen Position im Alphabet und altem Zustand + * einen neuen Zustand aus der Tabelle uebergaenge wählen; + * + * - false antworten, wenn er im Fehlerzustand landet, + * + * - am Schluss der Eingabe entscheiden, ob der erreichte + * Zustand ein Endzustand ist. + */ + return false; // dummy return + } +} + diff --git a/Quellcodes/AuS_A_DFA_Implementieren/tipp/KGB_007Sucher.java b/Quellcodes/AuS_A_DFA_Implementieren/tipp/KGB_007Sucher.java new file mode 100644 index 0000000..7025f54 --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/tipp/KGB_007Sucher.java @@ -0,0 +1,85 @@ +/** + * Dieser DFA erkennt Ziffernfolgen, in denen sich irgendwo + * James Bond versteckt hat -- in denen also die Folge 007 + * vorkommt. + * + * Die folgenden Ziffernfolgen sollen beispielsweise akzeptiert + * werden: + * + * 12107117700710012021, + * 007, + * 0071121727, + * 0000000071121727, + * 007007007007, + * 1121271201212007. + * + * Die folgenden Ziffernfolgen sollen nicht akzeptiert werden: + * + * 12107110710012021, + * 07, + * 00, + * 07112172, + * 1121271201212000. + + * Um den Automaten und vor allem die Übergangstabelle + * übersichtlicher zu halten, verwendet er nicht alle Ziffern, + * sondern lediglich das eingeschränkte Alphabet {0, 1, 2, 7}. + * + * Es empfiehlt sich, den Automaten zunächst zu zeichnen, + * die Übergangstabelle auf Papier zu erstellen und dann zu + * programmieren. + * + * @author Urs Lautebach + * @version 2020-Okt + */ + +public class KGB_007Sucher extends EndlicherAutomat { + /* Bei der Implementierung dieser Klasse muss man nur + * die Attribute alphabet, uebergaenge und endzustaende + * geeignet festlegen -- alles andere macht die + * Vaterklasse. + */ + private static final char[] alphabet = new char[] { + }; + + private static final int[][] uebergaenge = new int[][] { + }; + + private static final int[] endzustaende = new int[] { + }; + + /** + * Instanziiert einen KGB_007Sucher-DFA. + */ + KGB_007Sucher() { + // Aufruf des super-Konstruktors: + super(alphabet, uebergaenge, endzustaende); + } + + /** + * Die main-Methode erlaubt den Aufruf des + * Binaerzahlenpruefers von der Kommandozeile aus mit: + * + * java KGB_007Sucher 127007117 1717771021 + * + * Die Ausgabe lautet dann + * + * Akzeptiert: "127007117" enthält 007. + * FEHLER! "1717771021" enthält kein 007! + */ + public static void main(String[] args) { + // Endlichen Automaten instanziieren: + EndlicherAutomat fahnder = new KGB_007Sucher(); + + for(String wort : args) { + if(fahnder.gehoertZuSprache(wort)) { + System.out.println("Akzeptiert: \"" + wort + + "\" enthält 007."); + } else { + System.out.println("FEHLER! \"" + wort + + "\" enthält kein 007!"); + } + } + } +} + diff --git a/Quellcodes/AuS_A_DFA_Implementieren/tipp/MailadressenChecker.java b/Quellcodes/AuS_A_DFA_Implementieren/tipp/MailadressenChecker.java new file mode 100644 index 0000000..b5149ed --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/tipp/MailadressenChecker.java @@ -0,0 +1,83 @@ +/** + * Die Klasse implementiert einen DEA für die Überprüfung äußerst + * einfacher Mailadressen, die nur aus dem Buchstaben b, Punkten + * und dem at-Zeichen bestehen dürfen. Der Nutzername darf fast + * beliebig aufgebaut sein; die Domain muss mindestens zweistufig + * sein und die Toplevel-Domain muss mindestens zwei Zeichen haben. + * + * Gültig sind damit beispielsweise: + * b@b.bb + * b@b.bbbb + * b.b.b@bbb.bbb + * bbbbbb@b.bb + * b@b.b.b.b.bb + * ...@b.bb + * ...bbb@bb.bb + * bbb...@bb.bb + * + * Ungültig sind beispielsweise: + * . + * b + * @ + * bb@ + * b@b.b@bbb + * b@b.b@bb.b + * @bb + * bb@. + * bb@.. + * + * Tatsächlich ist die Überprüfung realer Mailadressen wesentlich + * komlizierter als in diesem Beispiel. + * + * @author Urs Lautebach + * @version 2021-Janar + * + */ +public class MailadressenChecker extends EndlicherAutomat { + /* Bei der Implementierung dieser Klasse muss man nur + * die Attribute alphabet, uebergaenge und endzustaende + * geeignet festlegen -- alles andere macht die + * Vaterklasse. + */ + + private static final char[] alphabet = new char[] { + }; + private static final int[][] transitionen = new int[][] { + }; + + private static final int[] endzustaende = new int[] { + }; + + MailadressenChecker() { + // Aufruf des super-Konstruktors: + super(alphabet, transitionen, endzustaende); + } + + + /** + * Die main-Methode erlaubt den Aufruf des + * Binaerzahlenpruefers von der Kommandozeile mit beliebig + * vielen zu prüfenden Strings: + * java MailadressenChecker bbb.b@bb.bb a@b@c + * + * Die Ausgabe lautet dann + * + * Akzeptiert: "bbb.b@bb.bb" ist eine gültige Mailadresse. + * FEHLER! "a@b@c" ist als Mailadresse ungültig! + */ + public static void main(String[] args) { + // Endlichen Automaten instanziieren: + EndlicherAutomat checker = new MailadressenChecker(); + + for(String wort : args) { + if(checker.gehoertZuSprache(wort)) { + System.out.println("Akzeptiert: \"" + wort + + "\" ist eine gültige Mailadresse."); + } else { + System.out.println("FEHLER! \"" + wort + + "\" ist als Mailadresse ungültig!"); + } // end if + } // end for + } // end main +} // end class + diff --git a/Quellcodes/AuS_A_DFA_Implementieren/tipp/README.md b/Quellcodes/AuS_A_DFA_Implementieren/tipp/README.md new file mode 100644 index 0000000..c2be6f0 --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/tipp/README.md @@ -0,0 +1,3 @@ +# Tabellen Dfa + +Implementierung eines allgemeinen tabellengetriebenen DFA als Programmieraufgabe (samt einiger Beispiel-DFA). \ No newline at end of file diff --git a/Quellcodes/AuS_A_DFA_Implementieren/tipp/lehrkraft-liesmich.txt b/Quellcodes/AuS_A_DFA_Implementieren/tipp/lehrkraft-liesmich.txt new file mode 100644 index 0000000..63abfcd --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/tipp/lehrkraft-liesmich.txt @@ -0,0 +1,36 @@ +Dieser Ordner enthält ein Java-Projekt, in dem die Implementierung +eines tabellengetriebenen DEA geübt wird. Es kann mit BlueJ oder +einem anderen Java-Werkzeug bearbeitet werden. + +Das Projekt liegt in drei Ausstattungen vor; Sie als Lehrkraft +entscheiden, welche Variante(n) Sie den Schülern vorlegen. + +Der Ordner... + +.skelett +-------- +enthält das Rohprojekt ohne Lösungshinweise oder Tipps, aber mit +Spezifikation, Arbeitsanweisungen und JUnit-Test. Das Projekt +sollte fehlerfrei compilieren und lauffähig sein, aber natürlich +schlagen die Tests fehl. + +.tipp +----- +enthält das gleiche Projekt, aber zusätzlich mit Tipps für die +Bearbeitung durch Schüler. + +.lsg +---- +enthält die Musterlösung des fertig ausprogrammierten Projekts. +Hier sollten alle Tests erfolgreich laufen. + +Zu bearbeiten sind die Datei EndlicherAutomat.java, in der der +eigentliche Tabellenmechanismus implementiert wird, und eine Auswahl +der Beispiel-DFA für + +Mailadressen +007-Suche +oder Binärzahlen + +bzw. eigene DFA für andere Sprachen nach Wahl. + diff --git a/Quellcodes/AuS_A_DFA_Implementieren/tipp/package.bluej b/Quellcodes/AuS_A_DFA_Implementieren/tipp/package.bluej new file mode 100644 index 0000000..a54f5f2 --- /dev/null +++ b/Quellcodes/AuS_A_DFA_Implementieren/tipp/package.bluej @@ -0,0 +1,75 @@ +#BlueJ package file +dependency1.from=DFA_BeispieleTest +dependency1.to=BinaerzahlenPruefer +dependency1.type=UsesDependency +dependency2.from=DFA_BeispieleTest +dependency2.to=MailadressenChecker +dependency2.type=UsesDependency +dependency3.from=DFA_BeispieleTest +dependency3.to=KGB_007Sucher +dependency3.type=UsesDependency +editor.fx.0.height=0 +editor.fx.0.width=0 +editor.fx.0.x=0 +editor.fx.0.y=0 +objectbench.height=119 +objectbench.width=770 +package.divider.horizontal=0.5785785785785785 +package.divider.vertical=0.7519685039370079 +package.editor.height=375 +package.editor.width=643 +package.editor.x=100 +package.editor.y=100 +package.frame.height=600 +package.frame.width=800 +package.numDependencies=3 +package.numTargets=6 +package.showExtends=true +package.showUses=true +project.charset=UTF-8 +readme.height=58 +readme.name=@README +readme.width=47 +readme.x=10 +readme.y=10 +target1.height=62 +target1.name=bin +target1.type=PackageTarget +target1.width=80 +target1.x=70 +target1.y=10 +target2.height=50 +target2.name=KGB_007Sucher +target2.showInterface=false +target2.type=ClassTarget +target2.width=140 +target2.x=10 +target2.y=130 +target3.height=50 +target3.name=DFA_BeispieleTest +target3.showInterface=false +target3.type=UnitTestTargetJunit4 +target3.width=160 +target3.x=180 +target3.y=240 +target4.height=50 +target4.name=EndlicherAutomat +target4.showInterface=false +target4.type=ClassTarget +target4.width=160 +target4.x=180 +target4.y=30 +target5.height=50 +target5.name=MailadressenChecker +target5.showInterface=false +target5.type=ClassTarget +target5.width=180 +target5.x=170 +target5.y=130 +target6.height=50 +target6.name=BinaerzahlenPruefer +target6.showInterface=false +target6.type=ClassTarget +target6.width=180 +target6.x=380 +target6.y=130