Arrays "freigeben"

  • Hey,

    ich hätte mal eine "allgemeine" Frage zu AutoIt. Ich habe zum "Spaß" mal _ArrayDelete() in einfach und für 1D-Arrays gebaut und wollte, weil ich eh immer ein "Temp-Array" brauche, einfach ein ganz neues Array "bauen", anstatt ReDim() zu nutzen^^...

    Wenn Arrays lokal sind, "leben" sie ja auch nur, solange die Funktion genutzt wird, dann wird der Speicher wieder frei gegeben?
    Und wenn ich ein altes Array überschreiben lasse, ist das alte auch im "Nirwana", oder?!

    [autoit]

    Func _ArrayDel($aArray, $iIndex)
    If Not IsArray($aArray) Then Return 0
    Local $iSize = UBound($aArray) - 1
    If $iSize <= 0 Then Return 0
    Local $aArrayNew[$iSize]

    [/autoit][autoit][/autoit][autoit]

    For $i = 0 To $iIndex - 1
    $aArrayNew[$i] = $aArray[$i]
    Next

    [/autoit][autoit][/autoit][autoit]

    For $i = $iIndex + 1 To $iSize
    $aArrayNew[$i - 1] = $aArray[$i]
    Next

    [/autoit][autoit][/autoit][autoit]

    Return $aArrayNew
    EndFunc

    [/autoit]

    So hatte ich mir das gedacht... Wo ist der Vorteil von "ReDim" und der Variante, wie das in der offiziellen UDF gemacht wird?^^ ReDim ist ja auch sehr rechenintensiv, müsste das so nicht eig. auch schneller sein?!^^ Was übersehe ich :P?

    Ich habe mal von BugFix gelesen, wenn man ne UDF macht, sollte man ohne Includes arbeiten, sonst ist es unsauber xD... Und da ich grade ne _ArrayFilter/_ArrayClean (wie auch immer ich das dann nenne) - Funktion mache, wollte ich halt ein paar Funktionen "nachbauen"^^...

    Spoiler anzeigen
    [autoit]


    Dim $a[6] = [1, "Test", "ABC", -5, "", " "]

    [/autoit] [autoit][/autoit] [autoit]

    _ArrayDis($a)
    ;~ $a = _ArrayClean($a, '\d+', 1) ; Entferne alle Zahlen aus dem Array
    ;~ $a = _ArrayClean($a, '-\d+', 0) ; Lasse nur alle neg. Zahlen im Array
    $a = _ArrayClean($a, '[^\s]', 0) ; Lösche alle leeren Indizies
    _ArrayDis($a)

    [/autoit] [autoit][/autoit] [autoit]

    ; #FUNCTION# ====================================================================================================================
    ; Name ..........: _ArrayClean
    ; Description ...: Clean up an array.
    ; Syntax ........: _ArrayClean($aArray, $sPattern[, $bDel = 1])
    ; Parameters ....: $aArray - An array.
    ; $sPattern - The RegExp-Pattern.
    ; $bDel - [optional] Default is 1.
    ; 0 = Delete index, if string didnt match the pattern.
    ; 1 = Delete index, if string matched the pattern.
    ; Return values .: New Array
    ; 0 - If $aArray wasnt an array or all elements are deleted.
    ; Author ........: Acanis
    ; ===============================================================================================================================

    [/autoit] [autoit][/autoit] [autoit]

    Func _ArrayClean($aArray, $sPattern, $bDel = 1)
    If Not IsArray($aArray) Then Return 0
    Local $i

    [/autoit] [autoit][/autoit] [autoit]

    If $bDel Then
    While $i < UBound($aArray)
    If StringRegExp($aArray[$i], $sPattern, 0) Then
    $aArray = _ArrayDel($aArray, $i)
    Else
    $i += 1
    EndIf
    Wend
    Else
    While $i < UBound($aArray)
    If Not StringRegExp($aArray[$i], $sPattern, 0) Then
    $aArray = _ArrayDel($aArray, $i)
    Else
    $i += 1
    EndIf
    Wend
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    Return $aArray
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _ArrayDel($aArray, $iIndex)
    If Not IsArray($aArray) Then Return 0
    Local $iSize = UBound($aArray) - 1
    If $iSize <= 0 Then Return 0
    Local $aArrayNew[$iSize]

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 0 To $iIndex - 1
    $aArrayNew[$i] = $aArray[$i]
    Next

    [/autoit] [autoit][/autoit] [autoit]

    For $i = $iIndex + 1 To $iSize
    $aArrayNew[$i - 1] = $aArray[$i]
    Next

    [/autoit] [autoit][/autoit] [autoit]

    Return $aArrayNew
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _ArrayDis($aArray)
    If Not IsArray($aArray) Then Return MsgBox(4096, "Fehler", "Der übergebene Paramter ist kein Array!")
    Local $sString = ""

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 0 To UBound($aArray) - 1
    $sString &= "|" & $aArray[$i] & "|" & @CRLF
    Next

    [/autoit] [autoit][/autoit] [autoit]

    MsgBox(4096, "Dein Array", $sString)
    EndFunc

    [/autoit]

    Bin da auch gern für Verbesserungsvorschläge offen, wobei ich das für 2D-Arrays eh nochmal anpassen müsste, denke ich :P...


    LG,
    Aca

    Einmal editiert, zuletzt von Acanis (25. September 2012 um 10:13)

  • Wo ist der Vorteil von "ReDim" und der Variante, wie das in der offiziellen UDF gemacht wird? ReDim ist ja auch sehr rechenintensiv, müsste das so nicht eig. auch schneller sein?! Was übersehe ich ?

    ReDim ist nicht wirklich rechenintensiv sondern der eventuell entstehende Kopieraufwand benötigt unter Umständen viel Zeit.
    Machen wir es doch mal an einem Beispiel:
    Im Speicher liegt ein Array mit 1000 Elementen. Dahinter ist im Speicher kein Platz mehr weil da ein anderer Wert steht.
    Nun soll das Array um 1 Element vergrößert werden (per ReDim)
    Da dahinter kein Platz mehr ist muss nun das gesamte Array Element für Element in einen Speicherbereich kopiert werden welcher Platz für 1001 Elemente bietet.
    Das frisst Zeit.
    Andersherum: Das Array soll um ein Element gekürzt werden: HIer gibt es natürlich keine Platzprobleme.
    Das Array bleibt an der selben Stelle nur dessen Größenangabe wird offiziell geändert - mehr nicht. Das geht also verdammt fix.
    Pauschal ist ein ReDim also nicht unbedingt langsam.

    Nun zu deinem Code und wie er sich im Vergleich zur offiziellen UDF-Funktion schlägt:
    Was passiert bei dir?:

    • Ein Array wird als Kopie übergeben (da es nicht mit ByRef übergeben wird). Das heißt das Array wird einmal 1:1 komplett Element für Element kopiert - das dauert.
    • Nun erstellst du ein neues Array und kopierst die Inhalte des Arrays Element für Element bis auf das eine Element in das neue Array.
    • Zusammengefasst: Wenn dein Array n-Elemente hat führst du 2*n-1 Kopien durch.


    Was passiert hingegen bei der offiziellen UDF?:

    • Ein Verweis auf ein Array wird übergeben - es wird nichts kopiert (ByRef)
    • Ab dem Element welches gelöscht wird jedes Element nur um eine einzige Stelle nach vorn verschoben.
    • per ReDim wird das letzte Element abgeschnitten


    Im Extremfall (das letzte Element soll gelöscht werden) hast du 2*n-1 Elementkopien und die UDF-Funktion hingegen nur ein einziges ReDim was in dem Fall verdammt flott geht.


    Ich habe mal von BugFix gelesen, wenn man ne UDF macht, sollte man ohne Includes arbeiten, sonst ist es unsauber xD

    Ich denke er wird es sicherlich in einem gewissen Zusammenhang gemeint haben.
    Die Standard-UDFs sind natürlich o.k. denn dafür wurden sie ja geschrieben.
    Es ist natürlich nie so gedacht gewesen das man, obwohl die Funktionen alle zur Verfügung stehen, für die eigene Verwendung alles nochmal selbst schreiben muss.

  • Ah, okey, danke :)...
    Beim Verkleinern ist ReDim also auf jeden Fall die bessere Wahl.

    Trotzdem muss ich, da ich ja nicht das letzte Element abschneide, muss ich ja ein Hilfsarray kopieren. (Aber die UDF-Methode scheint ja echt ganz gut durchdacht zu sein *hmpf* :D)

    Mit ByRef kann ich beim Erzeugen des Hilfs-Arrays also Rechenleistung sparen? Ala Beispiel:

    [autoit]

    Func swap(ByRef $a, ByRef $b) ;swap the contents of two variables
    Local $t
    $t = $a
    $a = $b
    $b = $t
    EndFunc ;==>swap

    [/autoit]

    Ich schau mir auf jeden Fall die UDF-Funktion nochmal an, danke schon mal für die Aufklärung :D!


    LG,
    Aca


  • Beim Verkleinern ist ReDim also auf jeden Fall die bessere Wahl.
    Trotzdem muss ich, da ich ja nicht das letzte Element abschneide, muss ich ja ein Hilfsarray kopieren.

    In der Regel sind die nativen Funktionen und Standard-UDFs besser, solange sie genau das tun, für was du sie brauchst.
    Bei der UDF wird nicht in ein Hilfsarray kopiert.
    Da wird einfach ab dem zu löschenden index alles um einen verschoben und danach das array um 1 verkleinert.

  • Trotzdem muss ich, da ich ja nicht das letzte Element abschneide, muss ich ja ein Hilfsarray kopieren.

    Nicht wenn du es wie in der UDF machst.
    Dann nimmst du einfach direkt das originale Array und verschiebst nur die Elemente nach dem Element was gelöscht werden soll um eins nach vorn.
    So ist kein Hilfsarray notwendig.

    Mit ByRef kann ich beim Erzeugen des Hilfs-Arrays also Rechenleistung sparen?

    Es gibt zwei Arten Variablen an Funktionen als Parameter zu übergeben:

    • By Value: Hierbei wird eine lokale Variable erstellt die den Inhalt des übergebenen Parameters erhalten wird. Änderungen an dieser lokalen Variable haben keine Auswirkungen auf die Variable außerhalb der Funktion welche als Parameter übergeben wurde.
    • By Reference: Hierbei wird ein Verweis auf die Variable übergeben. Vereinfacht gesagt ist es ein und die selbe Variable. Änderungen haben also auch Auswirkungen auf die äußere Variable.

    Gerade bei Arrays oder anderen großen Datenstrukturen ist die Übergabe per ByRef also flotter da hierbei das Kopieren wegfällt.

  • Und "By Value" ist "Standard", wenn man Funktionen mit Parametern macht?

    Und dann kann man ja, wenn man ein Array "bearbeitet", wie hier beim Filtern, eig. immer besser mit ByRef arbeiten, hmmm :)

  • Und "By Value" ist "Standard", wenn man Funktionen mit Parametern macht?

    Und dann kann man ja, wenn man ein Array "bearbeitet", wie hier beim Filtern, eig. immer besser mit ByRef arbeiten, hmmm :)


    Exakt. Es gibt eine kleine Ausnahme bei DLLStructs, diese kann man sozusagen nur ByRef übergeben. (Nicht wirklich, aber es macht keinen wirklichen Unterschied. Die AutoIt-Variable enthält die Struktur-Definition und einen Pointer auf die Struktur im Speicher. Nun ist es egal, ob diese Infos referenziert oder kopiert werden, der Pointer im Speicher bleibt der gleiche.)

  • Alles klar, danke^^... Habe das gleich mal übernommen und dazu noch nen Bubblesort für Arrays umgesetzt^^

    Spoiler anzeigen
    [autoit]

    ; #FUNCTION# ====================================================================================================================
    ; Name ..........: _ArraySort1D
    ; Description ...: Sort an 1D-Array.
    ; Syntax ........: _ArraySort1D(Byref $aArray)
    ; Parameters ....: $aArray - [in/out] An array.
    ; Author ........: Acanis
    ; ===============================================================================================================================
    Func _ArraySort1D(ByRef $aArray) ; Bubblesort (http://de.wikipedia.org/wiki/Bubblesort)
    If Not IsArray($aArray) Then Return
    Local $n = UBound($aArray), $bSwapped

    [/autoit] [autoit][/autoit] [autoit]

    Do
    $bSwapped = False
    For $i = 0 To $n - 2
    If $aArray[$i] > $aArray[$i + 1] Then
    _Swap1D($aArray, $i, $i + 1)
    $bSwapped = True
    EndIf
    Next
    $n -= 1
    Until $bSwapped = False
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    ; #FUNCTION# ====================================================================================================================
    ; Name ..........: _ArraySort2D
    ; Description ...: Sort ab 2D-Array.
    ; Syntax ........: _ArraySort2D(Byref $aArray)
    ; Parameters ....: $aArray - [in/out] An array.
    ; Author ........: Acanis
    ; ===============================================================================================================================
    Func _ArraySort2D(ByRef $aArray) ; Bubblesort (http://de.wikipedia.org/wiki/Bubblesort)
    If Not IsArray($aArray) Then Return
    Local $n = UBound($aArray), $bSwapped

    [/autoit] [autoit][/autoit] [autoit]

    Do
    $bSwapped = False
    For $i = 0 To $n - 2
    If $aArray[$i][0] >= $aArray[$i + 1][0] Then
    _Swap2D($aArray, $i, $i + 1)
    $bSwapped = True
    EndIf
    Next
    $n -= 1
    Until $bSwapped = False
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    ; #FUNCTION# ====================================================================================================================
    ; Name ..........: _Swap1D
    ; Description ...:
    ; Syntax ........: _Swap1D(Byref $aSwap, $a, $b)
    ; Parameters ....: $aSwap - [in/out] An array.
    ; $a - First index of Array.
    ; $b - Second index of Array.
    ; Author ........: Acanis (, Oscar)
    ; ===============================================================================================================================
    Func _Swap1D(ByRef $aSwap, $a, $b)
    Local $sTmp

    [/autoit] [autoit][/autoit] [autoit]

    $sTmp = $aSwap[$a]
    $aSwap[$a] = $aSwap[$b]
    $aSwap[$b] = $sTmp
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    ; #FUNCTION# ====================================================================================================================
    ; Name ..........: _Swap2D
    ; Description ...:
    ; Syntax ........: _Swap2D(Byref $aSwap, $a, $b)
    ; Parameters ....: $aSwap - [in/out] An array of unknowns.
    ; $a - First index of Array.
    ; $b - Second index of Array.
    ; Author ........: Acanis (, Oscar)
    ; ===============================================================================================================================
    Func _Swap2D(ByRef $aSwap, $a, $b) ; Based on a script by Oscar
    Local $sTmp

    [/autoit] [autoit][/autoit] [autoit]

    If $aSwap[$a][0] > $aSwap[$b][0] Then
    For $i = 0 To UBound($aSwap, 2) - 1
    $sTmp = $aSwap[$a][$i]
    $aSwap[$a][$i] = $aSwap[$b][$i]
    $aSwap[$b][$i] = $sTmp
    Next
    Else ; $aSwap[$a][0] = $aSwap[$b][0]
    If $aSwap[$a][1] > $aSwap[$b][1] Then
    For $i = 0 To UBound($aSwap, 2) - 1
    $sTmp = $aSwap[$a][$i]
    $aSwap[$a][$i] = $aSwap[$b][$i]
    $aSwap[$b][$i] = $sTmp
    Next
    EndIf
    EndIf
    EndFunc

    [/autoit]

    Und die Array-Filter:

    Spoiler anzeigen
    [autoit]

    ; #FUNCTION# ====================================================================================================================
    ; Name ..........: _ArrayFilter
    ; Description ...: Clean up an array. (using #include <Array.au3>!)
    ; Syntax ........: _ArrayFilter(ByRef $aArray, $sPattern[, $bDel = 1])
    ; Parameters ....: $aArray - An array.
    ; $sPattern - The RegExp-Pattern.
    ; $bDel - [optional] Default is 1.
    ; 0 = Delete index, if string didnt match the pattern.
    ; 1 = Delete index, if string matched the pattern.
    ; Return values .: Nothing
    ; Author ........: Acanis
    ; ===============================================================================================================================

    [/autoit] [autoit][/autoit] [autoit]

    Func _ArrayFilter(ByRef $aArray, $sPattern, $bDel = 1)
    If Not IsArray($aArray) Then Return
    Local $i

    [/autoit] [autoit][/autoit] [autoit]

    If $bDel Then
    While $i < UBound($aArray)
    If StringRegExp($aArray[$i], $sPattern, 0) Then
    _ArrayDelete($aArray, $i)
    Else
    $i += 1
    EndIf
    Wend
    Else
    While $i < UBound($aArray)
    If Not StringRegExp($aArray[$i], $sPattern, 0) Then
    _ArrayDelete($aArray, $i)
    Else
    $i += 1
    EndIf
    Wend
    EndIf
    EndFunc

    [/autoit]


    Wobei ich bei der sehr guten ArrayUnique von UEZ auch gesehen habe, dass man eig. 1D und 2D gut in eine Func packen kann, mach ich vllt. auch noch :P...

    Spoiler anzeigen
    [autoit]

    ; #FUNCTION# =====================================================================================================================
    ; Name.............: ArrayUnique
    ; Description ...: Returns the Unique Elements of a 1-dimensional or 2-dimensional array.
    ; Syntax...........: _ArrayUnique($aArray[, $iBase = 0, oBase = 0, $iCase = 0])
    ; Parameters ...: $aArray - The Array to use
    ; $iBase - [optional] Is the input Array 0-base or 1-base index. 0-base by default
    ; $oBase - [optional] Is the output Array 0-base or 1-base index. 0-base by default
    ; $iCase - [optional] Flag to indicate if the operations should be case sensitive.
    ; 0 = not case sensitive, using the user's locale (default)
    ; 1 = case sensitive
    ; 2 = not case sensitive, using a basic/faster comparison
    ; Return values: Success - Returns a 1-dimensional or 2-dimensional array containing only the unique elements of that Dimension
    ; Failure - Returns 0 and Sets @error:
    ; 0 - No error.
    ; 1 - Returns 0 if parameter is not an array.
    ; 2 - Array has more than 2 dimensions
    ; 3 - Array is already unique
    ; 4 - when source array is selected as one base but UBound(array) - 1 <> array[0] / array[0][0]
    ; 5 - Scripting.Dictionary cannot be created for 1D array unique code
    ; Author .........: UEZ 2010 for 2D-array, Yashied for 1D-array (modified by UEZ)
    ; Version ........: 0.96 Build 2010-11-20 Beta
    ; ================================================================================================================================
    Func ArrayUnique($aArray, $iBase = 0, $oBase = 0, $iCase = 0)
    If Not IsArray($aArray) Then Return SetError(1, 0, 0) ;not an array
    If UBound($aArray, 0) > 2 Then Return SetError(2, 0, 0) ;array is greater than a 2D array
    If UBound($aArray) = $iBase + 1 Then Return SetError(3, 0, $aArray) ;array is already unique because of only 1 element
    Local $dim = UBound($aArray, 2), $i
    If $dim Then ;2D array
    If $iBase And UBound($aArray) - 1 <> $aArray[0][0] Then Return SetError(4, 0, 0)
    Local $oD = ObjCreate('Scripting.Dictionary')
    If @error Then Return SetError(5, 0, 0)
    Local $i, $j, $k = $oBase, $l, $s, $aTmp, $flag, $sSep = Chr(01)
    Local $aUnique[UBound($aArray)][$dim]
    If Not $oBase Then $flag = 2
    For $i = $iBase To UBound($aArray) - 1
    For $j = 0 To $dim - 1
    $s &= $aArray[$i][$j] & $sSep
    Next
    If Not $oD.Exists($s) And StringLen($s) > 3 Then
    $oD.Add($s, $i)
    $aTmp = StringSplit(StringTrimRight($s, 1), $sSep, 2)
    For $l = 0 To $dim - 1
    $aUnique[$k][$l] = $aTmp[$l]
    Next
    $k += 1
    EndIf
    $s = ""
    Next
    $oD.RemoveAll
    If $k > 0 Then
    If $oBase Then $aUnique[0][0] = $k - 1
    ReDim $aUnique[$k][$dim]
    Else
    ReDim $aUnique[1][$dim]
    EndIf
    Else ;1D array
    If $iBase And UBound($aArray) - 1 <> $aArray[0] Then Return SetError(4, 0, 0)
    Local $sData = '', $sSep = ChrW(160), $flag
    For $i = $iBase To UBound($aArray) - 1
    If Not IsDeclared($aArray[$i] & '$') Then
    Assign($aArray[$i] & '$', 0, 1)
    $sData &= $aArray[$i] & $sSep
    EndIf
    Next
    If Not $oBase Then $flag = 2
    Local $aUnique = StringSplit(StringTrimRight($sData, 1), $sSep, $flag)
    EndIf
    Return SetError(0, 0, $aUnique)
    EndFunc ;==>ArrayUnique

    [/autoit]


    Und noch die Filterfunktion für 2D-Arrays machen :D...

    Vielleicht brauchts ja mal wieder wer^^.


    LG,
    Aca

    PS: Hab den Thread mal auf erledigt gesetzt, hat mir geholfen und hab das nu besser verstanden, danke nochmal :D!