Programmieren - alles kontrollieren 4.934 Themen, 20.613 Beiträge

DOS-Programm: Sortier-Ergebnisse weichen von einander ab

dromedar / 14 Antworten / Flachansicht Nickles

Immer noch arbeite ich an und mit einem DOS-Programm, erstellt mit Turbo-Pascal 7.0. (Leider kenne ich nichts, was eine Umstellung auf Windows (Delphi) halbwegs automatisch ermöglicht.)
Auf meinem neuen Computer (Windows XP Prof.) stelle ich nun fest, dass Sortier-Ergebnisse, Ablauf mit denselben Daten auf demselben Weg, von einander abweichen: Mal sind sie in Ordnung, mal ist die Sortierung fehlerhaft. Kann die Ursache dafür sein, dass der Computer wegen seiner höheren Geschwindigkeit die Ursache dafür ist? Wenn ja: Wie kann ich diese für DOS-Programme drosseln? Oder wo kann die Ursache sonst liegen?
Für Hilfe wäre ich sehr dankbar.

bei Antwort benachrichtigen
dromedar Nachtrag zu: „DOS-Programm: Sortier-Ergebnisse weichen von einander ab“
Optionen

> Der Punkt Patch der Zeitmessung in der Runtime-Libray von TurboPascal ist z.B. hier beschrieben:
> http://www.webplain.de/turbopascal/error200.php

Früher verwendete ich gelegentlich Zeitmessungen, jetzt aber nicht mehr. Deswegen brauche ich dafür wohl nichts zu tun.

> Die ursprüngliche Version der CRT-Bibliothek brach mit einem Error 200 auf CPUs mit mehr als 200MHz ab.

> Ich setze einen Patch aus der c't ein:
> http://www.heise.de/ct/hotline/Nicht-schon-wieder-Runtime-Error-200-307662.html
> Bzw. ich nutze Borland Pascal 7.0 da war der Quelltext der CRT-Bibliothek im Lieferumfang und man konnte die Stelle entsprechend ändern und neu kompilieren.

Einen solchen Abbruch habe ich noch nie erlebt. Laut Aida32 hat die CPU meines Computers eine vorgesehene Taktung von 2333 MHz (2,5 x 933). Allerdings setze ich in meinem Programm nicht die Borland-Unit crt ein, sondern stattdessen die Unit OPcrt von Enz EDV-Beratung Gmbh (laut c't-Archiv 4/1995 Seite 28 bankrott)von 1990: "Bei OPcrt handelt es sich um einen vollständigen Ersatz der Standard Turbo Pascal Unit CRT. Obwohl ... vollständige Kompatibilität mit dieser Unit besteht, bietet OPcrt ... Erweiterungen und Steigerungen. ... " Wahrscheinlich verarbeitet sie deswegen die höhere Geschwindigkeit ohne dieses Problem.

> function VglRS (p10, p20: pointer): boolean;
> var kl : shortint;
> v1, v2 : v_ispieler_m;
> begin
> VglRS := t;
> Move (p10^, v1, SizeOf (v_ispieler_m));
> Move (p20^, v2, SizeOf (v_ispieler_m));
> kl := KzlKleiner (v1.ikzl, v2.ikzl);
> if kl = -1 then Exit;
> if kl = 0 then if KzlKleiner (v1.iclub, v2.iclub) = -1 then Exit;
> VglRS := f;
> end; {VglRS}

> Auf den ersten Blick ist allerdings nichts zu erkennen, was da zu Unschärfen führen sollte. Eine Anmerkung habe ich allerdings: ich finde es ungünstig, das die Parameter von VglRS untypisierte Zeiger sind und dann der zugewiesene Speicher mehr oder weniger "blind" in echte Objekte (oder Records) kopiert wird.

Diese Funktion ist eine von elf ähnlichen Vergleichs-Funktionen, die unterschiedliche Records bearbeiten, aber alle aus der Baum-Unit aufgerufen werden. Vorher bekommt die Baum-Unit (im Beispiel unten rsBptr) vom Hauptprogramm als Parameter den zu bearbeitenden Datensatz zugewiesen (ispieler) sowie die far-Adresse der Vergleichs-Funktion (VglRS), die zu verwenden ist, und falls erforderlich die Datensatzlänge (SizeOf). Die globale Variable lby ist hier der zurückgegebene Code. Beispiel:
Aufruf: InsVar (rsBptr, ispieler, SizeOf (v_ispieler_m), VglRS, lby);
procedure InsVar (var baum5: p_baum_u; var v0; siz0: integer;
KleinF: FuncKlein; var okay9: byte);
{fügt die Variable v0 mit der Größe siz0 Bytes in den Baum ein -
bei Gleichheit wird der bestehende Eintrag überschrieben
okay9 = 0, wenn Variable v0 eingefügt
1, wenn bestehender Eintrag überschrieben
2, wenn Speicherplatz nicht ausreicht}
var pk1, pk2 : pKnoten;
dir : (l, r);
pv : ^t_objekt;
procedure NewNode (var B_neu: pKnoten; last: p_baum_u);
var pk1 : pKnoten;
begin
if MaxAvail 1 then Exit;
New (pk1);
with pk1^ do begin
size := siz0; left := nil; right := nil; back := last;
GetMem (inhalt, size); Move (v0, inhalt^, size); end {with};
B_neu := pk1;
end; {NewNode}
begin {InsVar}
pk1 := pKnoten (baum5); pk2 := pk1; pv := @v0; okay9 := 0;
while pk1 nil do with pk1^ do begin
pk2 := pk1;
if KleinF (pv, inhalt) then begin dir := l; pk1 := left; end
else if KleinF (inhalt, pv) then begin dir := r; pk1 := right; end
else begin
if pk1^.size siz0 then Writeln ('TYPE MISMATCH ERROR!')
else Move (v0, inhalt^, size);
okay9 := 1; Exit; end {else};
end {while};
NewNode (pk1, pk2); if okay9 > 1 then Exit;
if pk2 nil then if dir = l then pk2^.left := pk1
else pk2^.right := pk1
else baum5 := pk1;
end; {InsVar}

> Ich hätte p10 und p20 als Zeiger vom Typ v_ispieler_m deklariert, da hätte man sich die Kopieroperation mit move(..) sparen können. Damit umgeht man jede Typprüfung von TurboPascal. Da könnte ich jetzt nicht garantieren, dass das wirklich CPU- und plattformunabhängig klappt. Es sollte, aber wer weiss das schon wirklich?

Das wäre nicht so zweckmäßig, weil die Baum-Unit nicht nur Datensätze vom Typ v_ispieler_m bearbeitet, sondern auch eine ganze Anzahl mehr.

> VglRS := t(rue) ist die Vorbelegung bzw. initialisierung des Rückgabeergebnisses.
> Das wird zurück gegeben, wenn der Vergleich ein Kleiner zurückliefert (-1) oder wenn das Ergebnis Gleich ist. Für die restlichen Ergebnisse (also +1) wird f(alse) zurückgegeben.

> OK, das würde passen. Bei kleiner oder gleich muss nicht getauscht werden. (Ich vermute du nutzt ein Bubblesort das darauf basiert zwei Objektinstanzen zu vergleichen und bei Bedarf zu vertauschen.)

Anderswo schon, aber hier nicht. Hier werden Datensätze als "Knoten" gespeichert zusammen mit den Adressen des vorangehenden und des nachfolgenden Knotens (links und rechts).

> Was genau meinst du mit dem "Datensatz mit ZIP"? (ZIP-Postalcode?) Sortierst du da Adressen und Namen? Ist das reproduzierbar?

ZIP ist das Namenskürzel des Teilnehmers Zipp Heinrich. Im aktuellen Fall wurden die Datensätze nach den Namenskürzeln sortiert. Die fehlerhafte Sortierung ist nicht reproduzierbar.

> Deine Funktion VglRS(..) scheint mir bisher einfach nur die Funktion KzlKleiner(..) zu kapseln. (Sie ist eine Funktion, die einen Grösser/Kleiner-Vergleich durchführt udn dazu eine Funktion aufruft, die einen Grölsser/Kleiner-Vergleicht durchführt aufruft.)

> Falls das Problem im Grösser/Kleiner-Vergleich liegt, dann müsste man sich die Funktion KzlKleiner(..) ansehen.

function KzlKleiner (kzl10, kzl20: a_kzl_g): shortint;
{Rückgabe 1 / 0 / -1 für kzl10 > / = / var i : byte;
begin
for i := 1 to cLKzl_gz do begin
if kzl10[i] = #0 then kzl10[i] := ' ';
if kzl20[i] = #0 then kzl20[i] := ' ';
if kzl10[i] > kzl20[i] then begin KzlKleiner := 1; Exit; end;
if kzl10[i] KzlKleiner := 0;
end; {KzlKleiner}
Es ist a_kzl_g ein array [1 .. cLKzl_gz] of char, cLKzl_gz die Konstante 5.

> Die Funktion Swap(..) dürfte zwei Elemente tauschen, vermutlich nach dem Vergleich. Ein Bubblesort erfordert ja bei n zu sortierenden Elementen, n Durchläufe durch den Array. Zu Beschleunigungszwecken geht man dann davon aus, dass nach jedem Durchlauf (i) die ersten i-1 Elemente bereits korrekt sortiert sind und fängt daher mit dem i-ten Element in der Liste an (vergleicht es mit dem Element i-1) und arbeitet sich zum Ende der Liste durch.
> Wenn da das Startelement des jeweiligen Durchlaufs falsch bestimmt wird, dann kann es "vergessene" Element in der resultierenden Liste geben.

Die Prozedur Swap wird nur bei der Löschung eines Knotens aus dem Baum eingesetzt, um die Pointer in den Nachbarknoten (links und rechts) anzupassen.
procedure Swap(var X,Y);
var XPtr : pointer absolute X;
YPtr : pointer absolute Y;
TPtr : pointer;
begin
TPtr:=XPtr; XPtr:=YPtr; YPtr:=TPtr;
end {Swap};

> Wie genau arbeitet deine Sortierung?

Diese Frage verstehe ich leider nicht. Ihre Aufgabe ist es, die Datensätze genau in die Reihenfolge zu bringen, die durch das aktuelle Datenfeld bestimmt wird.

Trotzdem hoffe ich, dass ich helfen konnte mein Problem zu lösen.

Schönes Wochenende!

bei Antwort benachrichtigen