Dynamisches Feld 3/10
Design und Technik
QBs Möglichkeiten
QB bietet möglichkeiten Datenfelder dynamisch zu verwlten. Dazu braucht man lediglich den Meta-Befehl 'REM $DYNAMIC' und den Befehl 'REDIM'.
REM $DYNAMIC DIM feld(5) AS INTEGER ... REDIM feld(9)
Der Nachteil ist, dass dabei die Werte des Feldes verloren gehen. Deshalb basteln wir uns Funktionen, die uns beim Umgang mit Datenfeldern helfen.
Welche Funktionen brauchen wir? Wir müssen einen Wert einfügen können, einen Wert wieder löschen können und es wäre auch praktisch die Größe des Feldes beliebig Variaieren zu können. Und wenn wir schonmal dabei sind implementieren wir auch gleich Funktionen, um das Feld als Stack zu verwenden.
Ein Stack ist eine der klassischen Speicherstrukturen der Informatik. Stack bedeutet zu deutsch Stapel. Man kann oben ertwas drauflegen und man kann auch nur von oben etwas runternehmen. Ds ganz nennt sich LIFO: Last In, First Out.
Da diese Funktionen leichter sind, als die allgemeinen einfüge- und lösch-Funtionen, fangen wir mit ihnen an. Für einen Stack benötigen wird zunächst eine Funktin 'push', die einen Wert "oben drauf legt", also am ende Anfügt. Wie das geht önnen Sie sihc sicher denken. Daten sichern, Feld vergrößern, Daten zurückschreiben und letzten eintrag auf denzusätzlichen Wert setzen. Hier folg die Implementation:
SUB mqbArrPushInt (array() AS INTEGER, value AS INTEGER) DIM length AS INTEGER length = UBOUND(array) DIM temp(length) AS INTEGER FOR i = 0 TO length temp(i) = array(i) NEXT REDIM array(length + 1) FOR i = 0 TO length array(i) = temp(i) NEXT array(length + 1) = value END SUB
Die Gegenfunktion 'pop', die einen Wert vom Stapel runterholt, funktioniert so: Sichere Daten, verkleinere das Feld, schreibe Daten zurpck und gebe den überflüssigen Wert zurück. Das sieht wie folgt aus:
FUNCTION mqbArrPopInt% (array() AS INTEGER) DIM newLength AS INTEGER newLength = UBOUND(array) - 1 IF newLength >= 0 THEN DIM temp(newLength) AS INTEGER FOR i = 0 TO newLength temp(i) = array(i) NEXT mqbArrPopInt = array(newLength + 1) REDIM array(newLength) FOR i = 0 TO newLength array(i) = temp(i) NEXT END IF END FUNCTION
Hier kann man ausnutzen, dass QB in jeder Funktion eine Temporäre Variable anlegt, die den selben Namen, wie die Funktion trägt. diese Variable ist bekanntlich später der zurückgegebene Wert.
Einfügen und Löschen
Nun zu den Funktionen zum Einfügen und Löschen. Zum löschen sichern wir die Daten, bis auf das zu löschende Element, verkleinern das Feld nd schreiben die Daten zurück.
SUB mqbArrEraseInt (array() AS INTEGER, index AS INTEGER) DIM newLength AS INTEGER newLength = UBOUND(array) - 1 IF newLength >= 0 THEN DIM temp(newLength) AS INTEGER FOR i = 0 TO index - 1 temp(i) = array(i) NEXT FOR i = index TO newLength temp(i) = array(i + 1) NEXT REDIM array(newLength) FOR i = 0 TO newLength array(i) = temp(i) NEXT END IF END SUB
Nun zum Einfügen: Hierbei muss man etwas beachten. Angenommen ich habe ein Feld mit 5 Elementen. Wenn ich sage: füge ein Element an Position drei ein, müssen alle folgenden Daten verschoben werden, sage ich jedoch füge an Stelle 20 eine Element ein, so muss nichts verschoben werden, sondern lediglich eine Reihe von Nullen angehengt werden und anschließend der Wert.
SUB mqbArrInsertInt (array() AS INTEGER, index AS INTEGER, value AS INTEGER) DIM length AS INTEGER length = UBOUND(array) IF index <= length THEN DIM temp(length) AS INTEGER FOR i = 0 TO length temp(i) = array(i) NEXT REDIM array(length + 1) FOR i = 0 TO index - 1 array(i) = temp(i) NEXT array(index) = value FOR i = index + 1 TO length + 1 array(i) = temp(i - 1) NEXT ELSE DIM temp(length) AS INTEGER FOR i = 0 TO length temp(i) = array(i) NEXT REDIM array(index) FOR i = 0 TO length array(i) = temp(i) NEXT array(index) = value END IF END SUB
Bleibt noch das Vergrößern bzw. verkleinern:
DEFINT A-Z SUB mqbArrResizeInt (array() AS INTEGER, size AS INTEGER) DIM length AS INTEGER length = UBOUND(array) IF length > size THEN DIM temp(size) AS INTEGER FOR i = 0 TO size temp(i) = array(i) NEXT REDIM array(size) FOR i = 0 TO size array(i) = temp(i) NEXT ELSE DIM temp(length) AS INTEGER FOR i = 0 TO length temp(i) = array(i) NEXT REDIM array(size) FOR i = 0 TO length array(i) = temp(i) NEXT END IF END SUB
Blick in die Zukunft
Nun ist die Verwaltung dynamischer Datenfelder wesentlich einfacher und bequemer. Da sie nun so einfach zu verwenden sind, werden Sie sie vermutlich auch wesentlich öfter einsetzen. Aber das ist bei weitem noch nicht alles. Später weren wir eine vergleichbare Stuktur mittes eine XMS-Bibliothek implementieren und des weiteren eine doppelt verkettete Liste. Beide werden wesentlich mehr Möglichkeiten bieten und vor allem einen Geschwindigkeitsvorteil beim wachsen haben.
Die Funktinen, die Sie hier finden sind auch für die anderen Standardtypen definiert.
Weitere Algorithmen
MQB bietet weitere Funktionen zum Arbeiten mit Feldern. Diese finden Sie in 'Algo'. Jede der Funktionen existiert für alle ganzzahligen Typen. Ich beschreibe Sie mit ihrem allgemeinen Namen, ohne Suffix für den Typ. 'mqbCopy' existiert also beispielsweise als 'mqbCopyInt'.
'mqbCopy' kopiert ein Feld in ein anderes. Dabei wird automatisch die Größe des kleinern Feldes bestimmt.
'mqbCount' zählt, wie oft der angegebene Wert im Feld vorkommt.
'mqbEqual' vergleicht zwei Felder miteinander. Zurückgegeben wird '-1', falls die Felder identisch sind, sonst '0'.
'mqbFind' sucht nach dem angegebenen Wert und gibt den Index des erten Vorkommens. Ist der Wert nicht vorhanden wird '-1' zurückgegeben. 'mqbFindLast' macht das selbe, liefert jedoch den index des letzten Vorkommens.
'mqbIota' füllt das Feld mit einer steigenden Zahlenfolge beginnend beim übergebenen Startwert. Also z.B. '0', '1', '2', ...
'mqbMaxElement' liefert den Index des größten Elements, 'mqbMinElement' den des kleinsten Wertes.
'mqbReplace' ersetzt im angegebenen Feld einen Wert durch einen anderen. 'mqbReplaceCopy' copiert das Feld in ein neues und ersetzt dabei einen Wert durch einen anderen.
'mqbReverse' dreht die Reihenfolge der Elemente des Feldes um. 'mqbReverseCopy' erstelt eine umgedrehte Kopie.
Neben diesen Prozeduren existieren ähnliche Prozeduren mit einem 'I' zwischen 'mqb' und dem Rest, also z.B. 'mqbICopy', außer für 'mqbIota'. Der Unterschied ist, dass zusätzlich ein Bereich angegeben werden muss, auf den der Algorithmus angewendet wird.
Die Angabe erfolgt folgendermaßen: 'feld1(), startindex1, endindex1, feld2, startindex2, optionaleWerte, ...'. Der Endindex des zweiten Feldes wird automatisch berechnet.
DIM feld1(10) AS INTEGER DIM feld2(10) AS INTEGER mqbIotaInt feld1(), 1 ' 1 2 3 4 5 6 7 8 9 10 11 | feld1 ' 0 0 0 0 0 0 0 0 0 0 0 | feld2 ' ' 0 1 2 3 4 5 6 7 8 9 10 | Index mqbICopyInt feld1(), 2, 5, feld2(), 3 ' 1 2 3 4 5 6 7 8 9 10 11 | feld1 ' \\ \\ ' \\ \\ ' \\ \\ ' 0 0 0 3 4 5 6 0 0 0 0 | feld2 ' ' 0 1 2 3 4 5 6 7 8 9 10 | Index