Am einfachsten lässt sich ein Array mit einer
Tabelle vergleichen. Auch der Begriff Matrix ist in anderen Sprachen
üblich.
Die einfachste Form ist eine einspaltige Liste. Nach oben sind
physikalisch Grenzen gesetzt, aber selbst die sind mental kaum
vorstellbar.
Ich beschränke mich hier auf Ein- und Zweidimensionale Array.
Inhalt:
Beginnen wir mit einem Ein-Dimensionalen Array
Dies entspricht einer einspaltigen Tabelle.
Bevor ich ein Array mit Daten füllen kann,
muss ich es deklarieren, also einen Variablennamen vergeben und die
Anzahl der Elemente bestimmen.
Es empfiehlt sich an den Beginn des Variablennamens 'a' oder 'ar' zu
setzen um immer sofort zu wissen, dass dieses ein Array ist.
Jedes Element in einem Array wird
über seinen Index angesprochen. Das ist gewissermaßen die Hausnummer
des Elements.
Hierbei ist zu beachten, dass im Array der erste Index = 0 ist. Man
spricht hier von einem Null-Basierten Index.
Deklarieren wir ein Array mit 5 Elementen:
Local $arTest[5] ; Die Anzahl der Elemente wird in eckigen Klammern hinter dem Variablennamen angegeben.
Der kleinste, deklarierbare Wert ist ein Array mit einem Element:
Local $arTest[1]
Angesprochen wird dieses Element mit: $arTest[0]
Um nun Werte
zuzuweisen, wird die Arrayvariable mit dem Index des Elements angegeben
und dann der Wert zugewiesen.
Dran denken - Element Nr. 1 hat den Index 0.
$arTest[0]
= 'Wert 1'
$arTest[1]
= 'Wert 2'
$arTest[2]
= 'Wert 3'
$arTest[3]
= 'Wert 4'
$arTest[4]
= 'Wert 5'
Wer nicht mit dem Index 0 arbeiten möchte, kann an dieser Position ja einen Zähler platzieren oder einfach leer lassen.
Die identische Wertzuweisung kann ich auch in
einer Schleife ausführen.
Hierbei lasse ich den Index in der Schleife bis zum Endwert erhöhen.
Da ich nicht immer weiß, wie viel Elemente das Array enthält, verwende
ich die Funktion UBound() zur Größenbestimmung.
Für unser Array liefert: UBound($arTest) den Wert 5.
Das letzte Element muss also 1 kleiner als UBound
sein, da wir mit 0 beginnen zu adressieren.
For
$i =
0 To
UBound($arTest)
-1
$arTest[$i]
= 'Wert '
&
$i +1
; Da die Ziffer hinter Wert nicht mit 0,
sondern mit 1 beginnt, muss ich 1 größer als der Schleifenzähler setzen
Next
Viele Funktionen geben
als Ergebnis ein Array zurück. Dabei wird häufig die Arrayposition [0]
genutzt um die Anzahl der Arrayelemente anzugeben.
Ein Bsp. hierfür ist StringSplit().
Nehmen wir an, wir haben einen String mit durch '|' getrennten Werten.
$string = 'a|b|d|f|h|i|k'
Wir möchten jetzt jeden Wert einzeln haben:
$arSplit = StringSplit($string, '|')
Und das ist jetzt der Inhalt von $arSplit
$arSplit[0] = 7
$arSplit[1]
= 'a'
$arSplit[2]
= 'b'
$arSplit[3]
= 'd'
$arSplit[4] = 'f'
$arSplit[5]
= 'h'
$arSplit[6]
= 'i'
$arSplit[7]
= 'k'
Es empfiehlt sich, wenn
man mit Funktionen arbeitet die ein Array zurückgeben sollen, vor der
Weiterverarbeitung zu prüfen ob auch ein Array vorhanden ist.
Bzw. auszuwerten ob das Array Werte enthält. Da StringSplit()
im Fehlerfall ein Array mit dem Originalstring zurückgibt könnte das so
aussehen:
$arSplit
= StringSplit($string,
'|')
If
$arSplit[0]
= 1
Then ; Wenn String
nicht gesplittet wurde (Seperator nicht enthalten, String leer), dann
Fehler
MsgBox(0, '', 'Fehler')
Else
For $i
= 1
To UBound($arSplit)
-1
; Hier könnte man auch
$arSplit[0] statt UBound($arSplit)
-1
verwenden, da die Anzahl dort geführt wird
MsgBox(0, 'Ergebnis Splitten',
'Wert Nr.:' &
$i
& ' = ' & $arSplit[$i])
Next
EndIf
Bei Funktionen, die nur im Erfolgsfall ein Array zurückgeben, empfiehlt sich folgende Überprüfung:
$arReturn
= Funktionsaufruf()
If
(Not IsArray($arReturn )) Then ;
Wenn kein Array, dann Fehler
Der Ausdruck (Not IsArray($arReturn )) muss nicht zwingend in Klammern gesetzt werden. Es dient hier zur Verbesserung der Übersicht.
Anmerkung:
Auch wenn Funktionen ein Array zurückgeben, sollte die Arrayvariable
vorher deklariert werden. Allerdings ist es hier ausreichend den Namen
zu deklarieren. Die Größe des Array wird von der Funktion festgelegt.
Für StringSplit()
ist dies nicht zwingend, aber _FileListToArray() bringt einen Fehler, wenn die Arrayvariable
vorab nicht deklariert wurde. Also ist es günstiger sich generell
anzugewöhnen, Variablen vorab zu deklarieren.
Beispiel:
Mit dem folgenden Beispiel will ich zeigen, wie man Programmieraufwand
durch Einsatz eines Array verringern kann.
Eine GUI mit Controls wird deklariert. Die Controls sollen per Button wahlweise aktiv/deaktiv sein. Die Art der Controls lasse ich mal offen.
• Variante ohne Array
Local
$aktiv =
True
$GUI =
GUICreate('Titel')
$control1
= GUICtrlCreate...
$control2 =
GUICtrlCreate...
$control3 =
GUICtrlCreate...
$control4 =
GUICtrlCreate...
$control5 =
GUICtrlCreate...
$control6 =
GUICtrlCreate...
$control7 =
GUICtrlCreate...
$button =
GUICtrlCreate...
;...
If
$msg
=
$button
Then
_SwitchActiv()
;...
Func
_SwitchActiv()
Local
$state
If
$aktiv
Then
$state
=
$GUI_DISABLE ; aktueller Status 'aktiv' - dann 'disable' setzen
Else
$state
=
$GUI_ENABLE
; aktueller Status 'inaktiv' - dann 'enable'
setzen
EndIf
$aktiv
= Not
$aktiv
; Wert umkehren (True
zu False und umgekehrt)
GUICtrlSetState($control1, $state)
GUICtrlSetState($control2, $state)
GUICtrlSetState($control3, $state)
GUICtrlSetState($control4, $state)
GUICtrlSetState($control5, $state)
GUICtrlSetState($control6, $state)
GUICtrlSetState($control7, $state)
EndFunc
; ==>_SwitchActiv
• Variante mit Array
Local
$aktiv =
True
Local $arControl[8]
$GUI =
GUICreate('Titel')
$arControl[0]
=
GUICtrlCreate...
$arControl[1]
= GUICtrlCreate...
$arControl[2]
=
GUICtrlCreate...
$arControl[3]
=
GUICtrlCreate...
$arControl[4]
=
GUICtrlCreate...
$arControl[5]
=
GUICtrlCreate...
$arControl[6] = GUICtrlCreate...
$arControl[7] = GUICtrlCreate... ; Button
;...
If
$msg
=
$arControl[7]
Then
_SwitchActiv()
;...
Func
_SwitchActiv()
Local
$state
If
$aktiv
Then
$state
=
$GUI_DISABLE
Else
$state
=
$GUI_ENABLE
EndIf
$aktiv
= Not
$aktiv
For $i =
0
To
6
GUICtrlSetState($arControl[$i], $state)
Next
EndFunc
; ==>_SwitchActiv
Es ist sichtbar, dass der Einsatz eines Array mit
steigender Anzahl von Controls immer effektiver wird. Zum Setzen des
Status ist, egal wieviel Controls vorhanden sind, nur eine Schleife mit
3 Codezeilen nötig.
Es erfordert natürlich etwas abstraktes Denken um sich hinter
Array+Index etwas vorstellen zu können. Zum Anfang empfehle ich,
einfach jede Zeile im Code mit einem kurzen Kommentar zu versehen. Dann
kann man sich viel schneller einlesen.
Auch sollte der Name des Array immer aussagekräftig sein.
Skriptpuristen schreiben da gerne mal: $a1,
$a2 etc. Das spart zwar Schreibarbeit,
beeinträchtigt aber die Lesbarkeit des Codes. Und gerade, wenn ich
vielleicht um Hilfe zu suchen, mein Skript jemand Anderem vorlege, soll
dieser auch die Chance haben sich zügig einzulesen.
Das Mehrdimensionale Array am Beispiel eines 2-Dimensionalen Array
Das 2-Dimensionale Array kann man vergleichen mit
einer mehrspaltigen Tabelle. Eine Dimension entspricht dann den Zeilen,
während die andere die Spalten verkörpert.
Im Allgemeinen ist es üblich, dass die erste Dimension die Zeilen
(Anzahl Elemente) und die zweite Dimension die Spalten (Werte je
Element) enthält.
Daraus ergibt sich folgende Deklaration:
Local $ar2Dimensional[$ElementeZahl][$Spaltenzahl]
Hier muß das kleinste deklarierbare Array 1
Element und 1 Spalte enthalten. Natürlich macht es wenig Sinn, ein
mehrdimensionales Array mit nur einer Spalte zu deklarieren, dann kann
ich ja gleich ein 1-D Array verwenden.
Also ist die kleinste 'sinnvolle' Deklaration für ein 2-D Array: 1
Element und 2 Spalten.
Hier mal ein Array mit 5 Elementen und 2 Spalten:
Local $ar2D[5][2]
Auch hier gilt
selbstverständlich für Zeilen- und auch Spaltenwert, dass das erste
Element über den Index 0 angesprochen wird.
Ich weise jetzt allen Elementen Werte zu:
Zeile 1
$ar2D[0][0] = 'Zeile1/Spalte1'
$ar2D[0][1] = 'Zeile1/Spalte2'
Zeile 2
$ar2D[1][0] = 'Zeile2/Spalte1'
$ar2D[1][1] = 'Zeile2/Spalte2'
Zeile 3
$ar2D[2][0] = 'Zeile3/Spalte1'
$ar2D[2][1] = 'Zeile3/Spalte2'
Zeile 4
$ar2D[3][0] = 'Zeile4/Spalte1'
$ar2D[3][1] = 'Zeile4/Spalte2'
Zeile 5
$ar2D[4][0] = 'Zeile5/Spalte1'
$ar2D[4][1] = 'Zeile5/Spalte2'
Nun kommt der Punkt, an dem die 'Knoten' im Gehirn entstehen.
Ich möchte ja den Vorteil des Array nutzen und in
einer Schleife alles abarbeiten.
Aber wie muß ich vorgehen?
Am Besten überlege ich, wie ich von Hand Daten in
eine Tabelle eingebe:
- Schreib Zeile 1, Spalte 1
- dann schreib Zeile 1, Spalte 2
- dann die nächste Zeile, Spalte 1 und
- in dieser Zeile die Spalte 2
- bis zur letzten Zeile
Als Pseudo-Code:
Beginne bei StartZeile bis EndZeile
Schreib Spalte 1 dieser Zeile
Schreib Spalte 2 dieser Zeile
Nächste Zeile
Bei 2 Spalten ist das so machbar. Habe ich aber
mehrere Spalten, deren Anzahl ich auch vorab nicht weiß, müssen die
Spalten auch in einer Schleife bearbeitet werden:
Beginne bei StartZeile bis EndZeile
Beginne bei erster Spalte
bis letzte Spalte
Schreib Spaltenwert
Nächste Spalte
Nächste Zeile
Und so sieht das in AutoIt-Code aus:
For
$i =
0 To
UBound($ar2D) -1
; $i ist der Zähler für die Zeilen
For
$k =
0 To
UBound($ar2D,
2) -1
; $k ist der Zähler für die Spalten
$ar2D[$i][$k]
= 'Zeile'
& $i
& '/Spalte'
& $k
Next
Next
Wie ihr seht verwende ich in der äußeren Schleife
UBound($ar2D) und in der
inneren Schleife UBound($ar2D,
2) .
Die äußere Schleife gilt für die erste Dimension, deren Wert
standardmäßig von der Funktion UBound(
) zurückgegeben wird.
Um die Spaltenzahl zu ermitteln, muß ich aber der Funktion
UBound(
) mitteilen, für welche Dimension ich den
Wert ermitteln möchte, in unserem Fall ist dies die 2.
Nicht durcheinander kommen. Hier wird nicht der Index
verwendet! Für die erste Dimension die 1, für die zweite die 2 usw.
Ich erwähnte oben, dass es üblich ist, dass die
erste Dimension die Zeilen (Anzahl Elemente) und die zweite Dimension
die Spalten (Werte je Element) enthält.
Das ist aber nicht zwingend! Man kann ein Array auch genau
entgegengesetzt bestücken. Also die Elemente in den Spalten und
zugehörige Werte in den Zeilen.
Darauf gehe ich jetzt aber nicht näher ein, man muß halt in der
Schleifenabarbeitung dann in der äußeren Schleife die Spalten und in
der inneren Schleife die Zeilen führen.
Auch einige Funktionen liefern von Haus aus ein 2-D Array zurück.
Nehmen wir als Bsp.
IniReadSection("filename",
"section")
So sieht unsere INI-Datei aus:
[sektion]
schluesselA=1
schluesselB=2
schluesselC=3
schluesselD=4
Jetzt lesen wir die Sektion ein:
#include
<array.au3>
Local $val
; Variable für Array deklarieren
$val =
IniReadSection($pathINI,
"sektion")
; Sektion
einlesen
If
(Not IsArray($val))
Then
; Wenn zurückgegebene Variable kein Array
ist
MsgBox(0,
'', 'INI-Sektion konnte nicht gelesen werden')
Exit
EndIf
_ArrayDisplay($val,
'Inhalt INI-Sektion: [sektion]')
; eingelesenes Array anzeigen
Schauen wir uns den Inhalt des Array an:
$val[0][0] = 4 ; Anzahl der eingelesenen Schlüssel-Wert Paare
$val[1][0]
= 'schluesselA'
; Name des Schlüssels
$val[1][1]
= 1
; Wert des Schlüssels
$val[2][0]
= 'schluesselB'
$val[2][1]
= 2
$val[3][0]
= 'schluesselC'
$val[3][1]
= 3
$val[4][0]
= 'schluesselD'
$val[4][1]
= 4
Möchte ich jetzt auf den Wert von 'schluesselC' zugreifen, muß ich das Array nach dem Schlüssel durchsuchen und mir den zugehörigen Wert ausgeben lassen:
Local
$SuchWert, $SuchSchluessel =
'schluesselC'
For $i
= 1
To UBound($val) -1
If
$val[$i][0] = $SuchSchluessel Then
$SuchWert =
$val[$i][1]
ExitLoop
EndIf
Next
MsgBox(0,
'',
'gesuchter Wert ist: ' &
$SuchWert)
Ich vergleiche also in jedem Element des Array ob
der enthaltene Schlüsselname (Position[n][0]) mit meinem gesuchten
Namen übereinstimmt.
Wird der Schlüssel gefunden, lese ich den zugehörigen Wert aus
(Position[n][1]).
Array - Statisch oder Dynamisch
Ein Array ist statisch, wenn wir eine feste Größe
für das Array definieren und dann damit arbeiten.
Dynamisch ist ein Array dann, wenn wir zur Laufzeit (also während das
Programm abgearbeitet wird) die Größe des Array anpassen.
• statisch
Local
$array[20]
; Array hat 20
Elemente
Local
$zaehler = -1
; Indexwert vor erstem Eintrag
Ich kann jetzt bis zu
20 Werte in das Array eintragen. Der
letzte Wert ist also: $array[19]
= 'letzter Wert'
Immer wieder dran denken: Array-Adressierung beginnt
mit Index 0.
Ich werde das sicher noch öfter wiederholen, denn
erfahrungsgemäß sind Indexfehler die häufigsten Fehler bei der Arbeit
mit Array.
; im Programm
gibt der User Daten in ein Input ein, die dem Array zugefügt werden
sollen
$newData =
GUICtrlRead($Input)
If $newData
<> '' Then
; prüfen ob nicht leer
$zaehler
+= 1
; Indexwert um eins erhöhen, für ersten Eintrag jetzt 0
If
$zaehler <
20 Then
; wenn Index kleiner als
20 ist
$array[$zaehler]
= $newData
; Daten an Indexposition eintragen
Else
; sonst Fehlermeldung
MsgBox(0, 'FEHLER',
'Keine weiteren Einträge möglich! - Array ist
voll.')
EndIf
EndIf
• dynamisch
Local
$array[1]
; Array hat zum Programmstart 1 Element
$newData =
GUICtrlRead($Input)
If $newData
<> '' Then
; prüfen ob nicht leer
; der
erste Eintrag soll in das bestehende 1. Array-Element eingetragen
werden, z.Zt. das letzte
; für weiter Einträge muß
das Array vorher um ein Element vergrößert werden
If
$array[Ubound($array)-1] <> '' Then
; ist das letzte Element NICHT leer (dann
ist es NICHT das erste)
ReDim $array[Ubound($array)+1]
; Arraygröße neu: 1 größer als aktuelle
Größe
Ubound($array)
EndIf
$array[Ubound($array)-1] = $newData
; Daten in das letzte Element schreiben, im
ersten Durchlauf ist dies das erste.
EndIf
Die aktuelle Arraygröße ist
immer mit
Ubound($array)
feststellbar.
Mit ReDim wird das
Array neu bestimmt. Wichtig dabei - die enthaltenen Daten gehen nicht
verloren.
Wichtig nochmal für das Befüllen des ersten
Elements. Bei der Array-Deklaration ist $array[0]
ohne Inhalt.
Dieses Element ist zum Programmstart auch das letzte
Element, weil:
Ubound($array)
gibt 1 zurück.
Da der Index für das letzte
Element
Obergrenze -1 ist, gilt: $Index
= Ubound($array) -1 ;
( 1 -1 = 0 )
Im ersten Durchlauf
ist somit
$array[Ubound($array)-1] identisch mit $array[0], dem ersten
Element.
Die Inhaltsprüfung ergibt KEINEN Inhalt und somit
wird die Arraygröße NICHT verändert, sondern in das letzte Element
geschrieben.
In allen weiteren Durchläufen ist das letzte Element
MIT Inhalt, somit wird das Array um ein Element vergrößert und dann in
das neue,
letzte Element geschrieben.
Es läßt sich nicht generell sagen, wann man mit
statischen und wann mit dynamischen Array arbeiten soll.
Ein Entscheidungskriterium ist die Datenmenge. Die Neudeklaration der
Arraygröße beansprucht natürlich auch Zeit.
Insofern kann es durchaus günstiger sein, einfach vorab ein Array mit
z.B. 1.000 Elementen zu definieren, wenn man weiß, dass diese Anzahl
nicht überschritten wird.
Es können bei der Deklaration des Array
sofort Werte zugewiesen werden. Hier die Vorgehensweise für 1D-Array
und 2D-Array.
Local $aTest1[5] = [1,2,3,4,5]
Local $aTest2[5][2] = [ [1,1], [2,2], [3,3], [4,4], [5,5] ]
Local $aTest3[5][3] = [ [1,1,1], [2,2,2], [3,3,3], [4,4,4], [5,5,5] ]
Minimierung Globaler Variablen durch Array
Die Anzahl Global deklarierter Variablen
sollte man so gering, wie möglich halten. Falls es erforderlich ist
Variablen Global zu verwenden, bietet es sich an, dafür ein Array zu
nutzen.
Hier im Bsp. werden die ID's für GUI und GUI-Ctrls im Array hinterlegt.
Vorab werden die GUI/ GUICtrl -Variablen mit Enum
durchnummeriert. Enum startet (wenn nicht anders vorgegeben) mit
0, das ist praktisch, da das erste Arrayelement den Index 0 hat. Wir
beenden die Enumeration mit der Variablen $Anzahl,
welche uns die Anzahl der Arrayelemente angibt.
Enum $guiMain, $btRead, $btClose, $btSafe, $inBetrag, $inWaehrung, $Anzahl
Global $aGui[$Anzahl]
Nun werden wie üblich GUI und GUICtrl's erstellt. Die Reihenfolge
spielt keine Rolle, da der verwendete Variablenname aus der Enumeration
eindeutig die Position im Array bestimmt.
$aGui[$guiMain] = GUICreate('Test')
$aGui[$btRead] = GUICtrlCreateButton('Read'...)
$aGui[$btClose] = GUICtrlCreateButton('Close'...)
$aGui[$btSafe] = GUICtrlCreateButton('Safe'...)
$aGui[$inBetrag] = GUICtrlCreateInput('Betrag'...)
$aGui[$inWaehrung] = GUICtrlCreateInput('Währung'...)
Der Vorteil:
Obwohl die ID's im Array gespeichert sind, kann ich sie mit: Index =
Variablenname verständlich ansprechen. Es ist durchaus vergleichbar mit
einem assoziativen Array. Das erleichtert vor allem bei großen Skripten
das Lesen.
Auch hier läßt sich aber Deklaration und Wertzuweisung in einem Schritt
erledigen.
Wichtig!! Dann muß aber zwingend darauf geachtet werden, dass alle
Elemente genau in der Reihenfolge der Enumeration zugewiesen werden!
Global
$aGui[$Anzahl] = [ _
GUICreate('Test'), _
GUICtrlCreateButton('Read'...), _
GUICtrlCreateButton('Close'...), _
GUICtrlCreateButton('Safe'...), _
GUICtrlCreateInput('Betrag'...), _
GUICtrlCreateInput('Währung'...)]
Der weitere Zugriff gestaltet sich, wie üblich
z.B.
While True
Switch GUIGetMsg()
Case $aGui[$btClose], $GUI_EVENT_CLOSE
Exit
Case $aGui[$btRead]
ConsoleWrite(GUICtrlRead($aGui[$inBetrag]) & ' ' & GUICtrlRead($aGui[$inWaehrung]) & @CRLF)
Case $aGui[$btSafe]
; Speicherfunktion
EndSwitch
WEnd
Dies soll euch den Einstieg in die Arbeit mit
Arrays erleichtern. Ich habe mich bewußt etwas beschränkt um nicht mit
zu vielen Details Verwirrung zu stiften.
Ich würde mich aber freuen, wenn ihr mir mitteilt, zu welchen Punkten
ihr nähere Erläuterungen wünscht. Wenn man erst mal mit umgehen kann
ist es gar nicht so einfach, sich auf die Probleme der Anfangszeit
zurück zu besinnen.
Eure Fragen mit den Erklärungen, würde ich dann in Form einer FAQ
anhängen.
Euer BugFix Kontakt: Mail BugFix