doppelte Zeilen
Transcription
doppelte Zeilen
Beispiellösung zur „Doppelte-Zeilen“-Aufgabe 1 H. Jakobs Aufgabenstellung Es sollen mit Hilfe eines CProgramms Zeilen von der Standardeingabe gelesen weden. Alle mehr als einmal hintereinander vorkommenden Zeilen soll einmal ausgegeben werden. Alle anderen Zei len werden unterdrückt. Die Zeilen haben eine Länge von weniger als 100 Zeichen. 2 Lösungsansatz 2.1 Variablen Da die mehrfach vorkommenden Zeilen garantiert hintereinander liegen (Eingabe ist sortiert), ist es nicht notwendig, alle bisherigen Zeilen zu speichern, sondern jede Zeile muss lediglich mit ihrer Vorgängerzeile verglichen werden. Dazu muss man jede Zeile nur so lange speichern, bis man sie mit ihrer Nachfolgerzeile vergleichen kann. Es ergibt sich daraus die Notwendigkeit, zwei Zeilen speichern zu können, d. h. man muss zwei ZeichenkettenVariablen haben. 2 Zeichenketten mit je ca. 100 Zeichen, genannt zeile_alt und zeile_neu 2.2 Ablauf Zunächst wird eine Zeile eingelesen. Diese Zeile hat keine Vorgängerzeile und kann daher auch nicht mit ihr verglichen werden. Für alle weiteren Zeilen gilt, dass sie eingelesen und mit ihrer Vor gängerzeile verglichen wird. Jede Zeile bekommt nach diesem Vergleich automatisch die Rolle der Vorgängerzeile zugewiesen für die Zeile, die danach eingelesen wird. Es ergibt sich damit folgender Algorithmus: Pseudocode (links) und Struktogramm (rechts) sind alternativ zu ver wenden, also nicht gleichzeitig. Es dient hier nur zum Vergleich. lies Zeile in Variable zeile_alt wiederhole lies Zeile in Variable zeile_neu falls EOF verlasse_Schleife falls zeile_neu gleich zeile_alt gib zeile_neu aus ende_falls kopiere zeile_neu nach zeile_alt ende_wiederhole Der Fall einer sich öfter als einmal wiederholenden Zeile ist damit aller dings noch nicht gelöst. Die Anzahl der Wiederholungen wird mit dem obigen Algorithmus um genau 1 reduziert, so dass einmal vorkommende Zeilen gänzlich unterdrückt werden, aber beispielsweise fünfmal vorkom mende Zeilen viermal ausgegeben werden. Zusätzlich als weitere Alternative auf der nächsten Seite ein Programmablaufplan mit dem selben Algorithmus. doppelte_Zeilen.sxw Seite 1 von 6 19.03.04 10:03 Beispiellösung zur „Doppelte-Zeilen“-Aufgabe H. Jakobs 2.3 verbesserter Ablauf Es ist also zu überlegen, wie sichergestellt werden kann, dass sich beliebig oft wiederholende Zeilen nur ein einziges Mal ausgegeben werden. Hier könnte man sich die ausgegebene Zeile merken und die Ausgabe immer dann unterdrücken, wenn die eigentlich auszugebende Zeile mit der gerade vorher ausgegebenen übereinstimmt. Die hinzukommende Variable soll vom selben Typ wie die bisherigen sein und den Namen zeile_ausgegeben tragen. Der verbesserte Algorithmus sieht demnach so aus: lies Zeile in Variable zeile_alt wiederhole lies Zeile in Variable zeile_neu falls EOF verlasse_Schleife falls zeile_neu gleich zeile_alt falls zeile_neu ungleich zeile_ausgegeben gib zeile_neu aus ende_falls kopiere zeile_neu nach zeile_ausgegeben ende_falls kopiere zeile_neu nach zeile_alt ende_wiederhole erster Entwurf als PAP Es stellt sich die Frage der Initialisierung der Variablen. zeile_alt und zeile_neu brauchen nicht initialisiert zu werden, weil sie am Anfang des Programms durch die Einlesevorgänge ohnehin mit neuen Werten versorgt werden. Aber die Variable zeile_ausgegeben wird gelesen (beim Vergleich mit zeile_neu), bevor ihr ein Wert zugewiesen wird (beim Kopieren des Inhalts von zeile_neu). Also muss sie vom Programm voher initialisiert werden, damit sie einen definierten Inhalt hat. Verwendet man hier die leere Zeile, so würde die erste doppelte Zeile nicht ausgegeben, wenn es sich dabei um eine leere Zeile handelt. Initialisiert man dagegen mit einer Zeile, die in den zu verarbeitenden Daten nicht vokommt, gibt es dieses Problem nicht. Gibt es aber eine Zeile, die garantiert nicht in den Daten vorkommen kann? Da wir diese Frage verneinen müssen, suchen wir nach einer anderen Lösung. 2.4 nochmals verbesserter Ablauf Vielleicht sollten wir nach dem Ausgeben einer Zeile einfach alle folgenden Zeilen, die identisch sind, in einer weiteren Schleife überlesen, bevor wir mit der Hauptschleife weitermachen? Vorteil wäre, dass wir nicht einmal eine dritte Variable benötigen. Direkt hinter der Ausgabe würde folgendes eingefügt: wiederhole lies Zeile in Variable zeile_neu falls EOF verlasse_Schleife falls zeile_alt ungleich zeile_neu verlasse_Schleife ende_wiederhole doppelte_Zeilen.sxw Seite 2 von 6 19.03.04 10:03 Beispiellösung zur „Doppelte-Zeilen“-Aufgabe H. Jakobs Hiermit sind die obigen Probleme gelöst. Auch mehrfach vokommende Zeilen werden nur einmal ausgegeben. Allerdings wird der Programmablauf durch die zweite Schleife etwas aufwendiger. 3 Programm in C Nachdem benötigte Variablen und der Programmablauf entworfen sind, kann jetzt alles in einer beliebigen prozeduralen Sprache codiert werden. Für die Ein und Ausgabe wird die HeaderDatei stdio.h benötigt, für die Zeichenkettenoperationen Vergleich und Kopieren die HeaderDatei string.h. Die genauen Parameter und Rückgabewerte der beiden Funktionen strcmp() und strcpy() sollten noch einmal nachgeschlagen werden – und zwar in der Originalliteratur, also den manPages! Evtl. ist noch nicht bekannt, wie man das Ende der Daten (sog. endoffile, EOF) erkennt. Auch das ist auf den manPages der zugehörigen Funktion (hier fgets()) erläutert. Bitte nachschlagen! #include <stdio.h> #include <string.h> int main() { char neu[100], alt[100]; /* max. je 99 Zeichen */ fgets (alt, sizeof(alt), stdin); while (1) { if (fgets (neu, sizeof(neu), stdin) == NULL) break; if (strcmp (alt, neu) == 0) { printf ("%s", alt); while (strcmp (alt, neu) == 0) { if (fgets (neu, sizeof(neu), stdin) == NULL) break; } /* while */ } /* if */ strcpy (alt, neu); } /* while */ return 0; } /* main */ 4 Programm als ShellScript Zum Vergleich hier ein ShellScript, welches dasselbe tut: doppelte_Zeilen.sxw Seite 3 von 6 19.03.04 10:03 Beispiellösung zur „Doppelte-Zeilen“-Aufgabe H. Jakobs #!/bin/sh read zeile_alt while read zeile_neu; do if test "$zeile_alt" = "$zeile_neu"; then echo "$zeile_alt" while read zeile_neu; do test "$zeile_alt" != "$zeile_neu" && break done fi zeile_alt="$zeile_neu" done Bei Shell Scripts hat man den Vorteil, dass die Zeilenlänge nicht grundsätzlich begrenzt ist, aber den Nachteil einer erheblich schlechteren Performance. Vergleichstests bleiben hier noch zu tun, um festzustellen, ob die Leistung genügt. 5 Programm als TclScript Zum Vergleich hier ein TclScript, welches dasselbe tut: #!/bin/sh #\ exec tclsh "$0" "$@" gets stdin zeile_alt while {1} { gets stdin zeile_neu if [eof stdin] break if {$zeile_alt == $zeile_neu} { puts $zeile_alt while {1} { gets stdin zeile_neu if [eof stdin] break if {$zeile_alt != $zeile_neu} break } } set zeile_alt $zeile_neu } Bei TclScripts (ähnlich wie bei Perl, Python und RubyScripts) ist die Zeilenlänge nicht grund sätzlich begrenzt, sondern kann problemlos mehrere Megabytes betragen. Die Performance ist recht gut, weit besser als bei Shell Scripts, aber nicht ganz so gut wie bei einem CProgramm. Auch hier bleiben noch Tests durchzuführen. doppelte_Zeilen.sxw Seite 4 von 6 19.03.04 10:03 Beispiellösung zur „Doppelte-Zeilen“-Aufgabe 6 H. Jakobs Tests 6.1 Erstellung von Testdaten Zum Testen des Programms sind passende Testdaten notwendig, um zu verhindern, dass man stän dig irgendwelche neuen Zeilen eingeben muss, was erstens Zeit kostet und zweitens keine unm mittelbar reproduzierbaren Ergebnisse liefert. Die Testdaten sollten jeweils einige einfach, einige doppelt und einige noch häufiger vorkommende Zeilen enthalten. Beispiel: eins zwei zwei drei drei drei nochmal nochmal nochmal nochmal nochmal nochmal zwei zwei eins drei drei drei 6.2 Verwendung der Testdaten Die Testdaten werden dem Programm über die Standardeingabe übermittelt. Hierzu kann man entweder das Hilfsprogramm cat benutzen und weiterpipen oder aber direkt eine Ein gabeumlenkung verwenden: ~> cat testdaten.txt | doppelt ~> doppelt < testdaten.txt 6.3 Dokumentation der Tests Über die durchgeführten Tests ist Buch zu führen, d. h. beispielsweise ein Bildschirmschnappschuss eines Testlaufs ist der Programmdokumentation beizufügen. Beispiel dafür: ~> cat probe.txt eins zwei zwei drei drei vier vier vier vier eins ~> ./doppelt < probe.txt doppelte_Zeilen.sxw Seite 5 von 6 19.03.04 10:03 Beispiellösung zur „Doppelte-Zeilen“-Aufgabe H. Jakobs zwei drei vier ~> [email protected] doppelte_Zeilen.sxw Seite 6 von 6 19.03.04 10:03