doppelte Zeilen

Transcription

doppelte Zeilen
Beispiellösung zur „Doppelte-Zeilen“-Aufgabe
1
H. Jakobs
Aufgabenstellung
Es sollen mit Hilfe eines C­Programms 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 Zeichenketten­Variablen 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 Header­Datei
stdio.h benötigt, für die Zeichenkettenoperationen Vergleich und Kopieren die Header­Datei
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 man­Pages!
Evtl. ist noch nicht bekannt, wie man das Ende der Daten (sog. end­of­file, EOF) erkennt. Auch das
ist auf den man­Pages 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 Shell­Script
Zum Vergleich hier ein Shell­Script, 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 Tcl­Script
Zum Vergleich hier ein Tcl­Script, 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 Tcl­Scripts (ähnlich wie bei Perl­, Python­ und Ruby­Scripts) 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 C­Programm. 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

Documents pareils