Die Ansätze von >>hier<< habe ich mal zu einer ausgewachsenen UDF ausgebaut (Siehe Anhang).
Worum geht es?: Arrays sind verdammt fix beim direkten Zugriff auf einzelne ihrer Elemente. Sobald aber Werte hinzugefügt oder gelöscht werden sollen sind diese Vorgänge (über ReDim) unglaublich träge.
Je größer die Arrays werden umso schlimmer wird das dann. Arrays eignen sich daher für eine Vielzahl an Anwendungsfällen nicht.
Ein gebräuchlicher Lösungsansatz ist jedoch nur Zweierpotenzen als Arraygröße zuzulassen. Das verschafft Luft für neue Elemente und Arrays können von nun an auch dynamisch verwendet werden. Nun kann man ein Array auch sinnvoll wie ein Stack oder eine Queue verwenden.
Auf dieser Idee aufbauend habe ich eine UDF erstellt welche die Nutzung dieses Prinzips für 1D-Arrays genauso einfach machen soll wie die bisherigen _Array-Funktionen in AutoIt und im Grunde als ihr Ersatz dienen soll.
Als zusätzliches Schmankerl habe ich dann noch die von Python bekannten Map-, Reduce- und Filter-Funktionen für AutoIt umgesetzt.
Die UDF enthält somit folgende Funktionen:
--------------- Funktionen für dynamische Arrays -----------------------------------
_DynArrayPush - neuen Wert ans Ende anhängen (vgl. Stack)
_DynArrayPop - letzten Wert zurückgeben und aus dem Array entfernen (vgl. Stack)
_DynArrayEnqueue - Wert ans Ende des Arrays hinzufügen (vgl. Queue)
_DynArrayDequeue - ersten Wert des Arrays entfernen und zurückgeben (vgl. Queue)
_DynArrayInsert - einen oder mehrere Werte innerhalb des Arrays einfügen
_DynArrayDelete - einen oder mehrere Werte innerhalb des Arrays entfernen
_DynArrayFromArray - Konvertiert ein normales Array in ein dynamisches Array
_DynArrayToArray - Konvertiert ein dynamisches Array in ein normales Array
_DynArrayFromString - Erzeugt ein dynamisches Array aus einem String
_DynArrayToString - Konvertiert ein dynamisches Array in einen String, getrennt durch ein Trennzeichen
_DynArraySearch - Werte in einem unsortierten Array finden
_DynArrayBinarySearch - Werte in einem sortierten Array finden
_DynArraySort - Array sortieren
_DynArrayIsSorted - prüft ob ein dynamisches Array bereits nach einer freien Sortierfunktion sortiert wurde
_DynArrayUnique - entfernt doppelte Einträge aus einem dynamischen Array
_DynArrayMap - Wendet eine Funktion auf alle Elemente eines dynamischen Arrays an
_DynArrayFilter - Filtert die Werte eines dynamischen Arrays anhand einer externen Funktion
_DynArrayReduce - Reduziert die Elemente eines dynamischen Arrays auf einen Wert mit Hilfe einer externen Funktion
_DynArrayFileList - Dateiauflistung in Ordnern+Unterordnern mit frei individualisierbarer Filterfunktion
----------- Hilfsfunktionen und allgemein verwendbare Funktionen --------------------------
_ArrayMap - Wendet eine Funktion auf alle Elemente eines Arrays an
_ArrayFilter - Filtert die Werte eines Arrays anhand einer externen Funktion
_ArrayReduce - Reduziert die Elemente eines Arrays auf einen Wert mit Hilfe einer externen Funktion
__DynArrayIsValid - Prüft ob ein Array einem korrekten dynamischen Array entspricht
__DynArrayRepair - versucht ein korrumpiertes dynamisches Array zu reparieren
__DynArrayIsPowerOfTwo - Prüft ob eine Zahl eine Zweierpotenz ist
__DynArrayGetNextPowerOfTwo - gibt die nächste aufgerundete Zweierpotenz zu einer Zahl zurück
__DynArrayGetPreviousPowerOfTwo - gibt die vorhergehende abgerundete Zweierpotenz zu einer Zahl zurück
_ArraySortFlexible - Sortiert ein Array mit einer beliebigen Vergleichsfunktion
_ArraySortInsertion - Sortiert ein Array mit einer beliebigen Vergleichsfunktion per Insertion-Sort
_ArrayHeapSortBinary - Sortiert ein Array mit einer beliebigen Vergleichsfunktion per Binary-Min-Heap-Sort
_ArrayHeapSortTernary - Sortiert ein Array mit einer beliebigen Vergleichsfunktion per Ternary-Min-Heap-Sort
__ArrayDualPivotQuicksort - Sortiert ein Array mit einer beliebigen Vergleichsfunktion per Dual-Pivot-Quicksort
_ArraySortWithList - Sortiert ein Array über die Sortierfunktion des Arraylist-COM-Objektes
_ArrayGetNthBiggestElement - gibt das n-größte Element in einem unsortierten Array ohne es zu sortieren (z.B. für Median)
_ArrayAinATo2d - konvertiert ein "Arrays in Array"-Konstrukt in ein 2D-Array
_Array2dToAinA - konvertiert ein 2D-Array in ein "Arrays-in-Array"-Konstrukt
_ArrayGetMax - ermittelt den Maximalwert in einem Array (auch mit eigener Vergleichsfunktion möglich)
_ArrayGetMin - ermittelt den Minimalwert in einem Array (auch mit eigener Vergleichsfunktion möglich)
_ArrayBinarySearchFlex - führt eine binäre Suche in einem Array mit Hilfe einer eigenen Vergleichsfunktion durch
_ArrayIsSorted - prüft ob ein Array bereits nach einer freien Sortierfunktion sortiert wurde
Alles anzeigen
Um die Möglichkeiten der UDF zu demonstrieren gehen wir mal anhand von Beispielen durch:
#include "DynArray.au3"; dynamisches Array aus bestehendem konventionellem Array erstellen:
Global $a_Array[] = [1,2,3,4,5]
_DynArrayFromArray($a_Array) ; Array konvertieren
_ArrayDisplay($a_Array, "das erzeugte dynamische Array")
; Neues dynamisches Array erstellen und dynamisch befüllen:
Global $a_Array[] = [0] ; neues leeres dynamisches Array
For $i = 1 To 25
_DynArrayPush($a_Array, "Wert " & $i)
Next
_ArrayDisplay($a_Array, "das dynamisch erzeugte dynamische Array")
; dynamisches Array aus String erstellen mit Integer als Elementdatentypen:
Global $a_Array = _DynArrayFromString("20|3|15|7Test|Test7|62", "|", Int)
_ArrayDisplay($a_Array, "das aus einem String erzeugte Integerarray")
Alles anzeigen
#include "DynArray.au3"
Global $a_Array[] = [0] ; neues leeres dynamisches Array
; Dem Array dynamisch Elemente hinzufügen
For $i = 1 To 25
_DynArrayPush($a_Array, "Test " & $i)
Next
_ArrayDisplay($a_Array, "Das Array vor dem Einfügen")
; Einzelne Elemente innerhalb des Arrays einfügen:
_DynArrayInsert($a_Array, "Insert 1", 4)
_DynArrayInsert($a_Array, "Insert 2", 5)
; Mehrere Elemente auf einmal einfügen:
Local $a_Temp[] = ["Insert 3", "Insert 4", "Insert 5", "Insert 6"]
_DynArrayInsert($a_Array, $a_Temp, 6)
_ArrayDisplay($a_Array, "Das Array nach _DynArrayInsert")
Alles anzeigen
#include "DynArray.au3"
Global $a_Array[] = [1,2,3,4,5,7,8,9,10]
_DynArrayFromArray($a_Array) ; Array konvertieren
_ArrayDisplay($a_Array, "Array vor dem Löschen")
; Elemente löschen:
_DynArrayDelete($a_Array, 4, 3)
_ArrayDisplay($a_Array, "Das Array nach _DynArrayDelete")
Alles anzeigen
#include "DynArray.au3"
Global $a_Array[] = [0] ; neuer leerer Stack
; dynamisch Elemente hinzufügen
For $i = 1 To 50
_DynArrayPush($a_Array, "Test " & $i)
Next
_ArrayDisplay($a_Array, "Gefüllter Stack")
; Elemente abbauen bis Stack leer ist
While $a_Array[0] > 0
ConsoleWrite(_DynArrayPop($a_Array) & @CRLF)
WEnd
Alles anzeigen
#include "DynArray.au3"
Global $a_Array[] = [0] ; neue leere Queue
; dynamisch Elemente hinzufügen
For $i = 1 To 50
_DynArrayEnqueue($a_Array, "Test " & $i)
Next
_ArrayDisplay($a_Array, "Gefüllte Queue")
; Elemente abarbeiten bis Queue leer ist
Do
ConsoleWrite(_DynArrayDequeue($a_Array) & @CRLF)
Until $a_Array[0] = 0
Alles anzeigen
#include "DynArray.au3"
Global $a_Array[] = [0] ; neues leeres dynamisches Array
; Dem Array dynamisch Elemente hinzufügen
For $i = 1 To 120 ; Array mit zufälligen Werten befüllen
_DynArrayPush($a_Array, Random(1, 50, 1))
Next
_ArrayDisplay($a_Array, "das unsortierte Array")
; Wert in einem unsortierten Array suchen:
$d_Found = _DynArraySearch($a_Array, 10)
If Not @error Then MsgBox(0,"Gefunden", 'Wert "10" am Index ' & $d_Found & ' gefunden.')
; Array sortieren:
_DynArraySort($a_Array)
_ArrayDisplay($a_Array, "Das sortierte Array")
; Wert in einem sortierten Array suchen:
$d_Found = _DynArrayBinarySearch($a_Array, 20)
If Not @error Then MsgBox(0,"Gefunden", 'Wert "20" am Index ' & $d_Found & ' gefunden.')
Alles anzeigen
#include "DynArray.au3"
; dynamisches Array aus String erstellen:
$a_Array = _DynArrayFromString("image20.jpg;image1.jpg;image11.jpg;image2.jpg;image3.jpg;image10.jpg;image12.jpg;image21.jpg;image22.jpg;image23.jpg", ";")
_ArrayDisplay($a_Array, "das unsortierte Array")
; Array normal sortieren:
_DynArraySort($a_Array)
_ArrayDisplay($a_Array, "Das normal sortierte Array")
; Array wie im Explorer (natürlich) sortieren:
_DynArraySort($a_Array, __MyNaturalCompare)
_ArrayDisplay($a_Array, "Das natürlich sortierte Array")
; Prüfen ob Array wirklich natürlich sortiert wurde:
MsgBox(0, "Prüfung", "Sortiert: " & _DynArrayIsSorted($a_Array, __MyNaturalCompare))
; Vergleichsfunktion als Wrapper für StrCmpLogicalW welche wie der Explorer intuitiver sortiert falls Zahlenwerte in den Strings vorkommen
Func __MyNaturalCompare(Const ByRef $A, Const ByRef $B)
Local Static $h_DLL_Shlwapi = DllOpen("Shlwapi.dll")
Local $a_Ret = DllCall($h_DLL_Shlwapi, "int", "StrCmpLogicalW", "wstr", $A, "wstr", $B)
If @error Then Return SetError(1, @error, 0)
If Not IsString($A) Or Not IsString($B) Then Return $A > $B ? 1 : $A < $B ? -1 : 0
Return $a_Ret[0]
EndFunc ;==>MyCompare
Alles anzeigen
#include "DynArray.au3"
; 2D-Array erstellen
Global $Array[1000][10]
For $i = 0 To 999
For $j = 0 To 9
$Array[$i][$j] = Chr(Random(65, 90, 1))
Next
Next
_ArrayDisplay($Array, "Das unsortierte Array")
; 2D-Array sortieren
_ArraySortFlexible($Array, _SortByColumns)
_ArrayDisplay($Array, "Das sortierte Array")
; eigene Vergleichsfunktion welche Schrittweise alle Spalten vergleicht
Func _SortByColumns(ByRef $A, ByRef $B)
For $i = 0 To UBound($A) -1
If $A[$i] > $B[$i] Then Return 1
If $A[$i] < $B[$i] Then Return -1
Next
Return 0
EndFunc
Alles anzeigen
#include "DynArray.au3"
; dynamisches Array mit den Werten 1-10 erstellen:
Global $a_Array[] = [1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15]
_DynArrayFromArray($a_Array) ; Array konvertieren
_ArrayDisplay($a_Array, "Ausgangswerte")
; Funktion auf Array mappen:
_DynArrayMap($a_Array, Square)
_ArrayDisplay($a_Array, "Quadrierte Werte")
;Wurzel ziehen:
_DynArrayMap($a_Array, Sqrt)
_ArrayDisplay($a_Array, "Wurzel der quadrierten Werte")
; Funktion welche das Quadrat von $value zurückgibt
Func Square(Const $value)
Return $value * $value
EndFunc ;==>Square
Alles anzeigen
#include "DynArray.au3"
; Array mit Zahlen bis 100 erstellen:
Global $a_Array[100]
For $i = 0 To 99
$a_Array[$i] = $i +1
Next
_DynArrayFromArray($a_Array)
_ArrayDisplay($a_Array, "Array mit allen Zahlen")
; Nur Primzahlen behalten:
_DynArrayFilter($a_Array, IsPrime)
_ArrayDisplay($a_Array, "Array nur mit Primzahlen")
; Funktion welche prüft ob eine Zahl eine Primzahl ist
Func IsPrime($io_number)
Local $Num = Abs($io_number)
If $Num < 2 And $Num > -2 Then Return 1
If Mod($Num, 2) = 0 Then Return 0
For $a = 3 To Int(($Num / 2) + 1) Step 2
If Mod($Num, $a) = 0 Then Return 0
Next
Return 1
EndFunc ;==>IsPrime
Alles anzeigen
#include "DynArray.au3"
; Array mit Zahlen bis 99 erstellen (hier mal mit _ArrayMap):
Global $a_Array[100] = []
_ArrayMap($a_Array, Index, False, True, True)
_DynArrayFromArray($a_Array, True, True)
_ArrayDisplay($a_Array, "Array mit Zahlen von 1-99")
; Die Quadratsumme der Werte des Arrays berechnen:
MsgBox(0,"_DynArrayReduce", "Quadratsumme: " & _DynArrayReduce($a_Array, Squaresum))
; Reduce-Funktion zur Bildung der Quadratsumme:
Func Squaresum(ByRef $x, Const $y)
$x += $y * $y
EndFunc ;==>Sqaresum
; Mapfunktion welche als Wert den Index des Elementes zurückgibt
Func Index(Const $value, Const $Index)
Return $Index
EndFunc ;==>Index
Alles anzeigen
#include "DynArray.au3"
#include <Date.au3>
; Alle dll-Dateien in System32 die "win" im Dateinamen tragen
$a_List = _DynArrayFileList("C:\Windows\System32", "win.*\.dll$", 1)
_ArrayDisplay($a_List)
; Alle dll-Dateien in System32 und SysWOW64 die mit "win" anfangen und die innerhalb des letzten Jahres geändert wurden:
$a_List = _DynArrayFileList("C:\Windows\System32|C:\Windows\SysWOW64", cb_MyFunc, 1)
_ArrayDisplay($a_List)
; Callback-Funktion welche True zurückgibt, wenn eine Datei den Anforderungen entspricht und False wenn nicht
Func cb_MyFunc(Const $s_Path)
Local Static $Now = _NowCalc()
If StringRegExp($s_Path, "\\win[^\\]*\.dll$") Then
If _DateDiff("Y", StringRegExpReplace(FileGetTime($s_Path, 0, 1), "(\d{4})(\d{2})(\d{2}).+", "$1/$2/$3"), $Now) < 1.0 Then Return True
EndIf
Return False
EndFunc ;==>cb_MyFunc
Alles anzeigen
Wer mal wissen möchte, was das ganze für einen Unterschied beim dynamischen Hinzufügen von Werten macht kann folgendes kleines Testskript ausführen:
#include "DynArray.au3"
Global $N = 1e4
$iT = TimerInit()
Global $a_Array[1] = [0]
For $i = 1 To $N
_DynArrayPush($a_Array, "Test " & $i)
Next
$iT = TimerDiff($iT)
ConsoleWrite(StringFormat("% 15s: \t%7.1f ms\n", "_DynArrayPush()", $iT))
_ArrayDisplay($a_Array)
$iT = TimerInit()
Global $a_Array[] = []
For $i = 1 To $N
_ArrayAdd($a_Array, "Test " & $i)
Next
$iT = TimerDiff($iT)
ConsoleWrite(StringFormat("% 15s: \t%7.1f ms\n", "_ArrayAdd()", $iT))
_ArrayDisplay($a_Array)
Alles anzeigen
Bei mir war _DynArrayPush dabei um den Faktor 20 schneller. Dieser Vorteil steigt weiter mit der Elementanzahl.
Changelog:
2020/06/16 |
|