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

vorheriges
Index
nächstes