In AutoIt werden fast außschließlich Arrays (Felder) als Datenstruktur eingesetzt.
Arrays sind aber nicht immer die beste Wahl.
Im Prinzip stellt ein Array einen Speicherbereich dar in dem alle Werte hintereinander stehen.
Das führt dazu das man die einzelnen Werte mit einem Index ansprechen kann.
Allerdings hat dies auch einen entscheidenden Nachteil:
Arrays sind absolut undynamisch.
Wenn man ein Element hinzufügen möchte, muss das komplette Array dann in einen Speicherbereich kopiert werden der dessen Größe aufnehmen kann.
Ebenso verhält es sich beim Löschen eines Elementes - mindestens die nachfolgenden Elemente müssten alle um eine Position verrückt werden.
Das führt dazu das bei solchen Aktionen Arrays enorm uneffizient sind und man nach Alternativen Ausschau halten sollte welche dieses Problem nicht haben und wenn es geht noch andere Vorteile bieten.
Die wichtigsten dieser Alternativen möchte ich hier nun vorstellen und zeigen wie man sie in AutoIt verwenden kann:
Die Liste
Bei einer Liste stehen die einzelnen Elemente nicht hintereinander im Speicher sondern alle verteilt.
Damit die Liste zu einer Einheit wird enthalten die einzelnen Elemente zusätzlich zu ihrem Wert noch eine Information darüber wo sich das nächste Element im Speicher befindet (Pointer).
Das führt dazu das man ohne viel Aufwand Elemente löschen oder hinzufügen kann da einfach nur ein Wert erstellt oder gelöscht werden muss und ein entsprechender Verweis beim vorherigen Element geändert werden muss.
Eine Liste ist in diesen Fällen enorm schneller als ein Array.
Prinzipiell sind Listen aber nicht indiziert wie z.B. Arrays - möchte man auf die Elemente zugreifen müsste man theoretisch immer die Liste vom Anfang durchlaufen.
In der Praxis gibt es aber ein paar Tricks wie man dies umgehen kann.
Verwendung in AutoIt:
System.Collections.ArrayList
#include <Array.au3>
[/autoit] [autoit][/autoit] [autoit];----------------------------------------------------------------------
;------------------------Array-List------------------------------------
;----------------------------------------------------------------------
; http://msdn.microsoft.com/de-de/library/….arraylist.aspx
;////Array-List Erstellen///////////
$AList = ObjCreate("System.Collections.ArrayList")
;////Einträge hinzufügen///////////
$AList.add ("Test1")
$AList.add ("Test6")
$AList.add ("Test4")
$AList.add ("Test2")
$AList.add ("Test3")
$AList.add ("Test5")
;////ArrayList sortieren///////////
$AList.sort
;////alle Elemente durchgehen///////////
For $element In $AList
ConsoleWrite($element & @CRLF)
Next
;////Element löschen///////////
$AList.Remove("Test2") ;Löscht das angegebene Element (soweit vorhanden)
;////löscht Eintrag an gegebenen Index///////////
$AList.RemoveAt (3) ;Entfernt das Element an der 4. Position
;////gibt Eintrag an gegebenen Index zurück///////////
$Item = $AList.Item(2) ;gibt den Index des Items an der 3. Stelle zurück
;////Eintrag an bestimmter Stelle einfügen///////////////
$AList.Insert (2, "Eingefügt!") ;fügt Wert an 3. Position ein.
;////Anzahl der Elemente bestimmen///////////
$Count = $AList.Count
;////Überprüft ob ein Element vorhanden ist///////////
If $AList.Contains ("Test200") Then MsgBox(0, "", "Element vorhanden!") ;hier im Beispiel ist Element nicht vorhanden
;////Listen zusammenfügen///////////
$queue = ObjCreate("System.Collections.Queue")
$queue.Enqueue ("QueueWert1")
$queue.Enqueue ("QueueWert2")
$queue.Enqueue ("QueueWert3")
$queue.Enqueue ("QueueWert4")
$AList.AddRange ($queue)
;////Umwandlung in Array///////////
$Array = $AList.ToArray ;kopiert Werte in ein Array (Liste ist immer noch vorhanden)
_ArrayDisplay($Array, "Test")
;////Löscht Liste///////////
$AList.Clear
;////Index eines Elementes bestimmen///////////
$Index = _ArrayList_GetIndexOf($AList, "Test1")
Func _ArrayList_GetIndexOf(ByRef $ArrayList, $element)
Local $counter = 0
If Not IsObj($ArrayList) Then
SetError(1)
Return 0
EndIf
For $elem In $AList
If $elem = $element Then Return $counter
$counter += 1
Next
SetError(2)
Return 0
EndFunc
Natürlich können die Elemente dieser Strukturen auch Arrays sein - das hat dann einen ähnlichen Effekt wie ein 2-Dim-Array.
Hier mal ein Beispiel dazu:
Spoiler anzeigen
$TestList = ObjCreate("System.Collections.ArrayList")
[/autoit] [autoit][/autoit] [autoit]For $i = 0 To 25
Dim $TestArray[4] = [$i, 2 * $i, 3 * $i, 4 * $i]
$TestList.add ($TestArray)
Next
For $element In $TestList
ConsoleWrite(StringFormat("%2d",$element[0]) & ' ' & StringFormat("%2d",$element[1]) & ' ' & StringFormat("%2d",$element[2]) & ' ' & StringFormat("%2d",$element[3]) & @CRLF)
Next
Die Warteschlange (Queue)
Eine Queue ist im Prinzip eine Art Liste.
Allerdings hat man hierbei immer nur Zugriff auf ein Element - nämlich das was als erstes hinzugefügt wurde.
Eine Queue arbeitet nämlich im FirstIn-FirstOut-Prinzip.
Als praktisches Beispiel gilt der Namensgeber.
Alle Elemente werden hinzugefügt.
Dann arbeitet man der Reihe nach die Elemente ab in der Reihenfolge wie sie hinzugefügt wurden.
Holt man das erste Element heraus steht nun das vormals 2. als nächstes bereit usw.
Dennoch kann man munter weiter Elemente hinzufügen.
Verwendung in AutoIt:
System.Collections.Queue
#include <Array.au3>
[/autoit] [autoit][/autoit] [autoit];----------------------------------------------------------------------
;------------------------Queue-----------------------------------------
;----------------------------------------------------------------------
; http://msdn.microsoft.com/de-de/library/…ions.queue.aspx
;////Queue Erstellen///////////
$queue = ObjCreate("System.Collections.Queue")
;////Eintrag an das Ende der Warteschlange hinzufügen///////////
$queue.Enqueue ("Erster Eintrag")
$queue.Enqueue ("Zweiter Eintrag")
$queue.Enqueue ("Dritter Eintrag")
$queue.Enqueue ("Vierter Eintrag")
;////Überprüft ob ein Element vorhanden ist///////////
If $queue.Contains ("Fünfter Eintrag") Then MsgBox(0, "", "is drin!")
;////Erstes Element anzeigen (ohne löschen)///////////
ConsoleWrite( $queue.Peek & @CRLF )
;////Anzahl der Elemente bestimmen///////////
$Count = $queue.Count
;////Erstes Element herausholen///////////
$firstelement = $queue.Dequeue
;////Umwandlung in Array///////////
$Array = $queue.ToArray
_ArrayDisplay($Array, "Test")
Der Stapelspeicher (Stack)
Ein Stack ist im Prinzip eine invertierte Queue denn er arbeitet im LastIn-FirstOut-Prinzip.
Im Gegensatz zur Queue kann man hier also nur das letzte Element was man hinzugefügt hatte herausholen.
Verwendung in AutoIt:
System.Collections.Stack
#include <Array.au3>
[/autoit] [autoit][/autoit] [autoit];----------------------------------------------------------------------
;------------------------Stack-----------------------------------------
;----------------------------------------------------------------------
; http://msdn.microsoft.com/de-de/library/…ions.stack.aspx
;////Stack Erstellen///////////
$Stack = ObjCreate("System.Collections.Stack")
;////Eintrag an den Stack anfügen///////////
$Stack.Push ("Erster Eintrag")
$Stack.Push ("Zweiter Eintrag")
$Stack.Push ("Dritter Eintrag")
$Stack.Push ("Vierter Eintrag")
;////Oberstes Element herausholen///////////
$firstelement = $Stack.Pop
;////Erstes Element anzeigen (ohne löschen)///////////
ConsoleWrite( $Stack.Peek & @CRLF)
;////Anzahl der Elemente bestimmen///////////
$Count = $Stack.Count
;////Überprüft ob ein Element vorhanden ist///////////
If $Stack.Contains ("Fünfter Eintrag") Then MsgBox(0, "", "Element vorhanden")
;////Umwandlung in Array///////////
$Array = $Stack.ToArray
_ArrayDisplay($Array, "Test")
Die Hash-Tabelle
Eine HashTable ist eine indizierte Datenstruktur.
Das heißt einem Index wird ein Wert zugeordnet.
Wenn man den Namen des Index kennt kann man damit sehr schnell auf seinen entsprechenden Wert zugreifen.
Eine bessere Vorstellung davon sollte man anhand der Beispielimplementierung in AutoIt erhalten:
System.Collections.Hashtable
;----------------------------------------------------------------------
;------------------------Hash-Table------------------------------------
;----------------------------------------------------------------------
; http://msdn.microsoft.com/de-de/library/….hashtable.aspx
;////Hash-Table Erstellen///////////
$hash = ObjCreate("System.Collections.Hashtable")
;////Einträge hinzufügen///////////
$hash.add ("Berlin", 3395189)
$hash.add ("Hamburg", 1743627)
$hash.add ("München", 1259677)
$hash.add ("Köln", 983347)
$hash.add ("Dresden", 504795)
;Beispiel hier: Einwohnerzahl wird der jeweiligen Stadt zugeordnet.
;////Anzahl der Elemente bestimmen///////////
$Count = $hash.Count
;////Überprüft ob ein Element vorhanden ist///////////
If $hash.Contains ("Bonn") Then MsgBox(0, "", "Bonn ist eingetragen") ;Bonn ist im Bsp. nicht enthalten
;////Löscht ein Element aus der Hash-Table///////////
$hash.remove ("München")
;////Elemente aufrufen///////////
ConsoleWrite("Einwohner(Berlin): " & $hash("Berlin") & @CRLF)
ConsoleWrite("Einwohner(Dresden): " & $hash("Dresden") & @CRLF)
;////Leert die Hash-Table///////////
$hash.Clear
Das Dictionary
Das Dictionary ist wie die Hash-Tabelle ein assoziatives Array:
Scripting.Dictionary
;//// Dictionary Erstellen ///////////
; http://msdn.microsoft.com/en-us/library/…4(v=vs.84).aspx
$oDict = ObjCreate("Scripting.Dictionary")
;//// Einträge hinzufügen ///////////
$oDict.add ("Berlin", 3395189)
$oDict.add ("Hamburg", 1743627)
; Alternativ (keine Fehler falls schon existiert und auch Werte damit veränderbar):
$oDict("München") = 1259677
$oDict("Köln") = 983347
$oDict("Dresden") = 529781
;//// Anzahl der Elemente bestimmen ///////////
MsgBox(0,"Anzahl Elemente in Dictionary", $oDict.Count)
;//// Überprüft ob ein Element vorhanden ist ///////////
If $oDict.Exists ("Bonn") Then MsgBox(0, "", "Bonn ist eingetragen") ;Bonn ist im Bsp. nicht enthalten
;//// Löscht ein Element aus dem Dictionary ///////////
$oDict.remove ("München")
;//// Elemente aufrufen ///////////
ConsoleWrite("Einwohner(Berlin): " & $oDict("Berlin") & @CRLF)
ConsoleWrite("Einwohner(Dresden): " & $oDict("Dresden") & @CRLF & @CRLF)
;//// alle Schlüssel durchgehen ///////////
For $k in $oDict.Keys
ConsoleWrite("Einwohner(" & $k & "): " & $oDict($k) & @CRLF)
Next
; Alle Werte statt Schlüssel erreicht man mit $oDict.Items
;//// Leert das Dictionary ///////////
$oDict.RemoveAll
Dictionary und Hash-Tabelle unterscheiden sich lediglich hinsichtlich der internen Implementierung.
Das führt dazu dass ein Dictionary bei einer kleinen Anzahl an Elementen performanter beim Hinzufügen von Elementen ist als die Hash-Tabelle während es sich bei einer großen Anzahl an Elementen andersherum verhält.
Dies dauert bei einem Dictionary immer länger je mehr Elemente in der Struktur stehen. Bei der Hash-Table bleibt der Zeitaufwand dafür immer der selbe.
Bei mir liegt diese Grenze grob bei etwa 400.000 Elementen.
Will man selbst ermitteln ab wann sich eines von beiden eher lohnt kann man z.B. folgendes Skript verwenden ($N anpasen):
Speedvergleich Dictionary-Hashtable
Global $oDic = ObjCreate("Scripting.Dictionary")
Global $oHash = ObjCreate("System.Collections.Hashtable")
Global Const $N = 1e6
[/autoit] [autoit][/autoit] [autoit]$iT = TimerInit()
For $i = 1 To $N
$oDic.add($i, "Teststring")
Next
$iT = TimerDiff($iT)
ConsoleWrite(StringFormat("Dictionary:\t%7.1f ms\n", $it))
$iT = TimerInit()
For $i = 1 To $N
$oHash.add($i, "Teststring")
Next
$iT = TimerDiff($iT)
ConsoleWrite(StringFormat("Hash-Table:\t%7.1f ms\n", $it))
Die Sorted-List
Die Sorted-List ist wie die Hash-Table und das Dictionary ein assoziatives Array (Schlüssel-Wert-Paare).
Im Unterschied zu diesen stehen die Elemente immer in einer, nach dem Schlüssel, sortierten Reihenfolge.
System.Collections.SortedList
;----------------------------------------------------------------------
;------------------------Sorted-List------------------------------------
;----------------------------------------------------------------------
; http://msdn.microsoft.com/de-de/library/…sortedlist.aspx
;//// Sorted-List erstellen ///////////
$SList = ObjCreate("System.Collections.SortedList")
;//// Elemente hinzufügen ///////////
$SList.add("Berlin", 3395189)
$SList.add("Hamburg", 1743627)
; Alternativ (keine Fehler falls schon existiert und auch Werte damit veränderbar):
$SList("München") = 1259677
$SList("Köln") = 983347
$SList("Dresden") = 529781
$SList("Stuttgart") = 606.588
$SList("Dortmund") = 580.444
;//// Test ob Schlüssel oder Wert existiert ///////////
If $SList.ContainsKey("Bonn") Then MsgBox(0, "", "Bonn ist eingetragen")
If $SList.ContainsValue(100000) Then MsgBox(0, "", "Eine Stadt mit 100.000 Einwohnern ist eingetragen")
;//// Elemente anhand Schlüssel oder Index löschen ///////////
$SList.Remove("München") ; Eintrag "München" wird gelöscht
$SList.RemoveAt(3) ; der 2. Eintrag (Hamburg) wird gelöscht
;//// Elemente anhand Schlüssel oder Index suchen ///////////
ConsoleWrite("Index von Köln: " & $SList.IndexOfKey("Köln") & @CRLF)
ConsoleWrite("Index der Stadt mit 529.781 Einwohnern: " & $SList.IndexOfValue(529781) & @CRLF & @CRLF)
;//// Alle Elemente durchlaufen ///////////
For $i = 0 To $SList.Count - 1
ConsoleWrite($SList.GetKey($i) & @TAB & $SList.Getbyindex($i) & @CRLF)
Next
;//// Sorted-List leeren ///////////
$SList.Clear
Nun ich hoffe die hier vorgestellten Alternativen regen den ein oder anderen dazu an wenigstens mal zu schauen was damit geht und gegebenfalls das nächste mal einfach mal auf ein Array zu verzichten...
Zu einer effektiven Programmgestaltung können diese Datenstrukturen eine Menge beitragen.
Und um die Sache zum Ausprobieren noch ein bisschen schmackhafter zu machen habe ich hier noch eine Funktion erstellt welche ein 1-Dim-Array direkt in eine Liste, Queue oder Stack umwandelt:
Spoiler anzeigen
#include <Array.au3>
[/autoit] [autoit][/autoit] [autoit]Global $Arr[5] = [1, 2, 3, 4, 5]
[/autoit] [autoit][/autoit] [autoit]$Queue = _ArrayToList($Arr, "Queue", 0, UBound($Arr) - 1, 1)
[/autoit] [autoit][/autoit] [autoit]If Not @error Then
$Temp = $Queue.ToArray
_ArrayDisplay($Temp)
EndIf
;=================================================================================================
;
; Function Name: _ArrayToList()
; Description: Konvertiert ein 1-dimensionales Array in eine Liste, Queue oder Stack
;
; Parameter(s): $Array - Das zu konvertierende Array
; [Typ] - gewünschter Datenstrukturtyp:
; "List" = ArrayList (Default)
; "Queue" = Warteschlange
; "Stack" = Stapelspeicher
; [$von] - Start array index (Default = 0)
; [$bis] - End array index (Default = Ubound - 1)
; [DeleteArray] - löscht das umzuwandelnde Array nach Abarbeitung (Default = 0)
;
; Requirement(s): None.
; Return Value(s): bei Erfolg - Die gewünschte Datenstruktur
; Bei Fehler - 0
; @Error=1 - kein korrektes Array als Parameter angegeben
; @Error=2 - Bereichsfehler eines angegebenen Index
; @Error=3 - Falsche Angabe für den $Typ-Parameter
;
; Author(s): [email='AspirinJunkie@german-nlite.de'][/email]
;
; Note(s): Durch Vertauschen des $von mit dem $bis Parameter kann die
; Reihenfolge der Elemente vertauscht werden.
;
;=================================================================================================
Func _ArrayToList(ByRef $Array, $Type = "List", $von = 0, $bis = Default, $DeleteArray = 0)
Local $i, $Liste, $Step = 1
If Not IsArray($Array) Then
SetError(1)
Return 0
EndIf
If $bis = Default Then $bis = UBound($Array) - 1
[/autoit] [autoit][/autoit] [autoit]If $bis > UBound($Array) - 1 Or $von > UBound($Array) - 1 Or 0 > $von Or 0 > $bis Then
SetError(2)
Return 0
EndIf
If $von > $bis Then $Step = -1
[/autoit] [autoit][/autoit] [autoit]Switch $Type
[/autoit] [autoit][/autoit] [autoit]Case "List"
$Liste = ObjCreate("System.Collections.ArrayList")
For $i = $von To $bis Step $Step
$Liste.Add ($Array[$i])
Next
Case "Queue"
$Liste = ObjCreate("System.Collections.Queue")
For $i = $von To $bis Step $Step
$Liste.Enqueue ($Array[$i])
Next
Case "Stack"
$Liste = ObjCreate("System.Collections.Stack")
For $i = $von To $bis Step $Step
$Liste.Push ($Array[$i])
Next
Case Else
SetError(3)
Return 0
EndSwitch
[/autoit] [autoit][/autoit] [autoit]If $DeleteArray Then $Array = 0
[/autoit] [autoit][/autoit] [autoit]Return $Liste
EndFunc ;==>_ArrayToList
;=================================================================================================
P.S.: Hoffe mal BugFix liest das hier nicht - ich will nicht wissen was los ist wenn er sieht das ich öffentlich das Array diffamiere.... [Blockierte Grafik: http://forum.team-ingame.de/images/smilies/sofa.gif]