Performance-Analyse der neuen Datenstruktur "Map"

  • ! Aktualisierte Ergebnisse >>Hier<< !

    In den aktuellen Beta-Versionen von AutoIt (v3.3.13.16 Beta) findet sich ein neuer Datenstrukturtyp mitsamt zugehörigen Funktionen.
    Der Datentyp nennt sich Map und ist in erster Linie ein assoziatives Array.
    Also genau das was wir uns bisher über das Dictionary-Objekt extern reingeholt haben.
    Aber nicht nur das - es ist weitaus flexibler. Für die Keys können Integer und/oder Strings verwendet werden. Nimmt man Strings ist das Verhalten wie ein Dictionary.
    Nimmt man Integer - und vor allem in Verbindung mit der Funktion MapAppend() - dann hat man ein Array, das aber dynamisch. Das heißt das Hinzufügen von Elementen geht um Weiten schneller als ein _ArrayAdd.

    Wenn man nun aber keine Key-Value-Struktur benötigt sondern einfach eine dynamische Stringliste (z.B. beim schrittweisen Erzeugen einer Dateiliste in einem Verzeichnis) dann kann man ebenfalls die Map benutzen.
    Die Strings werden als Keys verwendet und ein Leerwert als Value. Anschließend holt man sich ein Array aus den Keys mit der Funktion MapKeys(). Das geht immer noch schneller als mit _ArrayAdd zu arbeiten. Alternativen hierzu wären die selbe Vorgehensweise mit dem Dictionary-Objekt oder ein String &= mit anschließendem StringSplit.

    Das sind jetzt viele Anwendungsmöglichkeiten und auf so eine interne Struktur habe ich schon länger gewartet.
    Also wollte ich jetzt mal herausfinden ob es denn von der Performance her gravierende Differenzen gibt, so dass ich Hinweise habe wann ich was am ehesten einsetzen kann.

    Hierzu habe ich den Zeitbedarf beim Hinzufügen und Auslesen betrachten. Angefangen mit wenigen Elementen habe ich schrittweise die Anzahl der Elemente in der jeweiligen Struktur erhöht um zu sehen wie der Zeitbedarf mit der Elementanzahl korreliert. Hier sind die Ergebnisse für das Hinzufügen:
    autoit.de/wcf/attachment/24589/
    Dass die Variante einen String mit Trennzeichen zusammensetzen und am Ende per StringSplit in ein Array zu konvertieren die schnellste Variante werden wird habe ich erwartet. Umso mehr hat mich gefreut, dass die Map, in Verbindung mit MapAppend, da ganz gut mithalten kann. Die Keys werden dabei einfach durchnummeriert (wie beim Array) und wir haben damit also nun eine performante ArrayList direkt in AutoIt.
    Es fällt auf, dass die Variante Dictionary-Elemente per .add-Methode hinzuzufügen (Beispiel: $oDic.add("Key", "Value")) langsamer ist, als die Variante per "="-Operator (Beispiel: $oDic("Key") = "Value"). Dies wird wahrscheinlich daran liegen, dass .add erst prüft ob das Element bereits existiert und einen Fehler wirft, während die =-Variante einfach bestehende Werte überschreibt. Ansonsten bewegen sich Map und Dictionary etwa auf dem selben Niveau. _ArrayAdd (=Array + ReDim) habe ich hier nicht mit reingenommen - dieses war schlicht zu langsam.
    Zusammenfassend, kann man sagen. Will man eine Key-Value-Struktur aufbauen machen Map und Dictionary keinen großen Unterschied beim Hinzufügen von Werten. Möchte man einfache Arraystrukturen mit durchnummerieren Index erzeugen hat man nun mit MapAppend eine gute einfache Möglichkeit hinzugewonnen.

    Das Aufbauen der Struktur ist aber erst ein Schuh. Der andere ist: Wie schnell bekomme ich die Daten da wieder raus.
    Prinzipiell kann man erst einmal auch ohne Benchmark sagen: Bei einer einfachen durchnummerierten Liste, bei dem man ein bestimmtes Element von einer bestimmten Stelle haben möchte, ist ein Array schlicht das performanteste. Möchte man aber Werte suchen oder deren Existenz überprüfen sind Map und Dictionary die 1. Wahl. Und Key-Value Beziehungen sollten ja schon per Definition den Dictionaries und Maps vorbehalten sein.
    Die Keys eines Dictionaries/Map kann man sich aber auch als Array zurückgeben. Dann hat man den schnellen Array-Zugriff auf die Keys in einer solchen Liste. Die Frage ist nur - wie groß ist dabei der Aufwand für das Umwandeln in ein Array?
    Hierzu nun die Ergebnisse. Dargestellt ist der jeweilige Zeitbedarf für einen zufälligen Zugriff auf N Elemente.
    autoit.de/wcf/attachment/24590/
    Dictionary und Map nehmen sich nicht wirklich viel. Es bleibt wohl eher Geschmackssache was man verwendet.
    Ebenso scheint der Zeitbedarf die Keys in ein Array zu wandeln nicht sehr groß zu sein im Vergleich zum eigentlichen Element-Zugriff.

    Jetzt muss man natürlich dazu sagen, dass die Map noch Beta ist und sich noch viel ändern kann.
    Aber als erste Einschätzung soll dies hier erst einmal genügen.

    • Offizieller Beitrag

    Hey AspirinJunkie,

    Ich habe ein großes Projekt wo ich mit einer ArrayList arbeite und habe mich sehr gefreut, als ich gesehen habe, dass AutoIt das nun auch selbst kann. Ich hatte leider bisher nicht genügend Zeit um zu testen ob es Performance Unterschiede gibt, ein riesen großes Dankeschön für deine Dokumentation. Ich werde, sobald die Stable draußen ist, auf Maps umsteigen. Danke dir :)

    Gruß,
    Spider

  • Tolle Analyse!

    Ich komme allerdings auf etwas andere Werte.
    Verglichen hab ich Map, Dictionary und String indem ich jeweil 1000000 Werte (bestehend aus einem String und einer Zahl) hinzugefügt habe

    Sieger ist Map mit etwa 1432ms, dicht gefolgt von String mit 1753ms und Letzter ist, weit abgeschlagen, Dictionary mit 26101ms

    Wenn man diesen Datensätzen in ein AutoIt-Array umwandeln will, dann sieht die Sache gleich anders aus:
    Jetzt führt Dictionary mit 88ms. Doppelt solange brauchte Map mit 170ms und StringSplit benötigte sogar 618ms!


    Map finde ich eine tolle Sache und auch die Beta allgemein hat es in sich und läuft etwa 30% schneller als die aktuelle Stable! :rock:

    E

  • Hi,

    Zitat

    Dass die Variante einen String mit Trennzeichen zusammensetzen und am Ende per StringSplit in ein Array zu konvertieren die schnellste Variante werden wird habe ich erwartet.


    Wenn es einem wirklich um Geschwindigkeit geht, spart man sich das Stringsplit und greift direkt mit den unschlagbar schnellen Stringfunktionen auf einzelne Elemente zu, Stichwort "durchnummerierte Liste" / "klassisches Array".
    Damit sind hunderte MB große Dateien in kürzester Zeit durchsucht. Ich bezweifle erstens, dass in einer anderen Form (egal ob Dictionary/Array/Map) sehr große Datenmengen überhaupt bewältigt werden können, zweitens werden die Antwortzeiten entsprechend der Verwaltung ansteigen. Nicht umsonst haben Datenbanken gigantische Analysefunktionen, um möglichst viel "indizieren" zu können^^
    Der direkte Index (lookup table) ist im Hinblick auf die Zugriffszeit durch nichts zu ersetzen.

    Das schöne an den Arrays/Maps/Dictionarys ist eben, mit einfachen Funktionen zu einem Ergebnis zu kommen. Auch wenn das alles in allem etwas "länger" dauert.....aber einen Tod muss man ja für die gewonnene Bequemlichkeit sterben ;)
    Die Kunst ist es nun, nur ein bisschen zu sterben und von den langsamen Funktionen die schnellste bequeme zu verwenden.

  • Verglichen hab ich Map, Dictionary und String indem ich jeweil 1000000 Werte (bestehend aus einem String und einer Zahl) hinzugefügt habe
    Sieger ist Map mit etwa 1432ms, dicht gefolgt von String mit 1753ms und Letzter ist, weit abgeschlagen, Dictionary mit 26101ms

    Die Frage wäre was hast du wie hinzugefügt.
    Hast du für die Map String-Keys oder Integer-Keys verwendet? Hast du MapAppend verwendet (was verdammt schnell ist aber den Key selbst wählt und damit den Key-Value-Aspekt killt)?
    Hast du Dictionary mit der .Add-Methode verwendet oder mit dem "="-Operator?
    Folgendes Skript:

    Spoiler anzeigen
    [autoit]

    Global $oDic = ObjCreate("Scripting.Dictionary")
    Global $oHash = ObjCreate("System.Collections.Hashtable")

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

    Global Const $N = 1000000

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

    $iT = TimerInit()
    Global $s_Str = ""
    For $i = 1 To $N
    $s_Str &= "Test" & ";"
    Next
    $a_String = StringSplit(StringTrimRight($s_Str, 1), ";", 2)
    $iT = TimerDiff($iT)
    ConsoleWrite(StringFormat("String:\t%7.1f ms\n", $it))

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

    Global $m_Map[]
    $iT = TimerInit()
    For $i = 1 To $N
    $m_Map[String($i)] = "Teststring"
    Next
    $iT = TimerDiff($iT)
    ConsoleWrite(StringFormat("Map:\t%7.1f ms\n", $it))

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

    Global $m_Map2[]
    $iT = TimerInit()
    For $i = 1 To $N
    MapAppend($m_Map2, "Teststring")
    Next
    $iT = TimerDiff($iT)
    ConsoleWrite(StringFormat("MapAppend:\t%7.1f ms\n", $it))

    [/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))

    [/autoit]

    brachte bei mir folgende Ergebnisse:

    Code
    String:	 1038.5 ms
    Map:	161635.7 ms
    MapAppend:	 1218.4 ms
    Dictionary:	67939.7 ms

    Die Umwandlung des Strings in ein Array ist hier übrigens schon mit drin.
    Ich vermute daher mal, dass du das Dictionary mit MapAppend verglichen hast - oder?

    Wenn es einem wirklich um Geschwindigkeit geht, spart man sich das Stringsplit und greift direkt mit den unschlagbar schnellen Stringfunktionen auf einzelne Elemente zu, Stichwort "durchnummerierte Liste" / "klassisches Array".

    Du meinst wenn ich z.B. dass 423. Element haben will dann nehme ich StringInStr in Verbindung mit StringMid anstatt einem $Array[422]?

    Der direkte Index (lookup table) ist im Hinblick auf die Zugriffszeit durch nichts zu ersetzen.

    Zugriffszeit bei welchem Szenario? Wenn man die Position (den Index) in der Struktur vorher kennt ist das definitiv richtig. Aber es gibt doch noch mehr Szenarien. Beispielsweise für für Key-Value-Fragestellungen. Dafür sind Dictionaries und Hash-Tables ja da. Dabei wird die Position aus dem Key berechnet. Bei einem durchnummerierten Array müsste man hierfür hingegen manuell durchgehen bis man das entsprechende Element gefunden hat.
    Wenn man hingegen die Position vorher weiß ist ein Array natürlich das beste wo gibt aber es gibt genügend Szenarien wo andere Strukturen um Welten performanter sind. Key-Value ist ein Szenario - ein anderes ist die Fragestellung: Existiert ein Element in der Struktur? Da stinkt eine durchnummerierte Liste einfach nur ab. Zum Thema dynamisch Elemente hinzufügen brauchen wir erst gar kein Wort verlieren.
    Es kommt immer auf den Anwendungsfall an - deswegen ist es ja so schön, dass es für jeden entsprechende Datenstrukturen gibt. Und die sind eben nicht nur aus Gründen der Bequemlichkeit da sondern durchaus wegen ihrer Performance für ihren jeweiligen Anwendungsfall.

    zweitens werden die Antwortzeiten entsprechend der Verwaltung ansteigen

    Dafür war das Diagramm gedacht: Zu schauen wie die Zugriffszeiten mit der Größe skalieren. Bei einem Array sind diese linear (also für einen Einzelzugriff konstant). Aber auch eine Hash-Table bietet das während Dictionary und Map sich exponential verhalten zu scheinen (vorsichtig ausgedrückt). Das heißt die Antwortzeiten sind sowohl beim Array als auch der Hash-Table konstant.

  • Zitat von AspirinJunkie

    Du meinst wenn ich z.B. dass 423. Element haben will dann nehme ich StringInStr in Verbindung mit StringMid anstatt einem $Array[422]?

    nein, DAS ist der Index.
    Es geht darum, bspw. Postleitzahl und Städtenamen zuzuordnen. Das könnte man natürlich auch indizieren^^, WENN es nicht mehrere Ortschaften zur selben Postleitzahl gäbe (Dictionary fällt schon aus! )
    Kein Mensch braucht ein Dictionary/Map wenn es ausschliesslich um den Index ginge. Der umgekehrte Weg ist doch das Problem. Welche Postleitzahl hat Dexheim?
    Da musst du nach dem Ort suchen und den gesamten Datensatz durchforsten.
    Das kannst du mit einer for/in/to Schleife machen und das Array durchlaufen... :rolleyes: das ist durch den IF-Vergleich dann (obwohl man einen "step 2" verwenden kann) immer noch 15x langsamer als ein stringinstr()
    Oder du benutzt wieder das "Key"/"Value"-Konstrukt, egal wie es sich nennt. Dann brauchst du sowohl für Key/Value als auch für Value/Key jeweils eine eigene Tabelle, immer mit der Vorgabe, dass weder Key noch Value doppelt vorkommen.

    Code
    Dictionary:	67939.7 ms


    Bei dafür geeigneten Daten mag das sowohl bequem als auch schnell ( ??? ) sein, aber wenn man sowieso TEXT hat (darunter fällt imho so gut wie jede Art von Datei ^^ ) , dann finde ich es ehrlich gesagt etwas seltsam (wie bspw hier gezeigt) , dass man csv-Dateien in Dictionarys verfrachtet, anstatt die enthaltenen Texte direkt zu bearbeiten/abzugleichen.
    Erinnert mich stark an die Scripte, die Farbwerte der einzelnen Pixel eines Bildes in ein 1024x768-Array zu schreiben und dann per Schleife dort "Pixel" zu suchen.

    Zitat von AspirinJunkie

    Key-Value ist ein Szenario - ein anderes ist die Fragestellung: Existiert ein Element in der Struktur? Da stinkt eine durchnummerierte Liste einfach nur ab. Zum Thema dynamisch Elemente hinzufügen brauchen wir erst gar kein Wort verlieren.

    da liegen wir doch garnicht weit auseinander :D
    Man sollte sich generell mal Gedanken über seine Datenstruktur bzw. Auswertung machen. Wenn ich sehe, was mit INI-Dateien und den dazugehörigen Bearbeitungsfunktionen verbrochen wird (nicht nur in diesem Forum), wird mir anders!
    Sicher gibt es nicht DEN richtigen Weg, aber man sollte die vorhandenen Möglichkeiten nutzen.
    Leider ist es wohl nicht mehr "in" mit einfachen Funktionen zu arbeiten. Ich bin mir ziemlich sicher dass das Potenzial bzw. die Mächtigkeit der "Low-Level"-Stringfunktionen kaum bekannt ist, bzw. diese Funktionen kaum benutzt werden.

    Daher finde ich es auch gut, wenn du diese Variante in deine Performance-Analyse mit einbeziehst :thumbup:

  • Das könnte man natürlich auch indizieren, WENN es nicht mehrere Ortschaften zur selben Postleitzahl gäbe (Dictionary fällt schon aus! )

    Dictionary schluckt auch ein Array als Value. Es ist also durchaus damit geeignet schnell alle Orte welche zu einer Plz gehören zu ermitteln.

    Ansonsten verstehe ich dein Anliegen schon muss aber sagen: Du versuchst Nachteile von Key-Value-Strukturen gegenüber einer Indizierung aufzuzeigen und machst dies anhand von Beispielen die für Key-Value-Strukturen ungeeignet sind.
    Wenn man beim Plz-Ort Beispiel bleibt: Möchte man den Ort zur Plz haben und nie andersherum dann ist ein Dictionary geeignet. Die Zugriffszeiten sind fix und der Speicherbedarf ist geringer als bei einem extra Index. Will man zusätzlich auch in die andere Richtung suchen ist ein Dictionary hingegen ungeeignet - so einfach ist das. Für den Fall würde ich dann persönlich aber weg von den Autoit-internen Strukturen gehen und auf eine DB wie Sqlite ausweichen (eben weil man da ja ganz einfach einen Index für zu tätigenden Fragestellungen erstellen kann). Vielleicht kannst du ja mal ein reines AutoIt-Beispiel für den Fall Plz-Ort erstellen damit man mal sieht wie man für diese Daten am besten innerhalb von AutoIt einen Index erstellen kann und wie man das dann handhabt.

    aber wenn man sowieso TEXT hat (darunter fällt imho so gut wie jede Art von Datei ) , dann finde ich es ehrlich gesagt etwas seltsam (wie bspw hier gezeigt) , dass man csv-Dateien in Dictionarys verfrachtet, anstatt die enthaltenen Texte direkt zu bearbeiten/abzugleichen.

    Text enthält eine Information. Die Art wie man diese behandelt richtet sich danach wie diese Informationen strukturiert sind und nicht danach wie der Grunddatentyp der äußeren Struktur ist. Wie immer: Es kommt immer auf den Anwendungsfall an.
    Klar kannst du eine HTML-Datei als String betrachten und verwenden - wird ja auch in der Regel so gemacht. Aber eine Baumstruktur (wie z.B. beim DOM) wäre ebenfalls geeignet sinvoll mit diesen Daten zu arbeiten und bei vielen Fragestellungen die geeignetere Wahl als auf Stringebene zu arbeiten. Bearbeitest du Excel-Dateien etwa auch nur mit Stringfunktionen? Oder sqlite-db-Dateien?

    Sicher gibt es nicht DEN richtigen Weg, aber man sollte die vorhandenen Möglichkeiten nutzen.

    Dem schließe ich mich uneingeschränkt an. Dieser Gedanke war auch der Grund für diesen kleinen Vergleich hier.

    da liegen wir doch garnicht weit auseinander

    Liegen wir selten ;)

  • Zitat

    Bearbeitest du Excel-Dateien etwa auch nur mit Stringfunktionen? Oder sqlite-db-Dateien?

    hehe, erwischt!
    Ich bearbeite in VBA Excel-Dateien (ungeöffnet) per ADO/DOA Datenbankabfrage über ein Recordset in ein Array, das geschieht im Millisekundenbereich, während das reine Öffnen der Datei und anschließendes "geExcele" mehrere Sekunden benötigt. Natürlich benutze ich auch "normale" Excel-Funktionen, wenn es nicht zeitkritisch ist ;) (was aber zugegebenermaßen selten vorkommt... )

    Zitat

    Aber eine Baumstruktur (wie z.B. beim DOM) wäre ebenfalls geeignet sinvoll mit diesen Daten zu arbeiten und bei vielen Fragestellungen die geeignetere Wahl als auf Stringebene zu arbeiten.

    yepp, auch XML-Files als Steuerungsdateien für Maschinen werden in Millisekunden per Stringbefehl bearbeitet. Da interessiert mich dann auch nicht, wenn der Maschinenhersteller bzw. die Softwarefirma im Monatstakt die Struktur ändert/erweitert. Solange die "Marker" bzw. Bezeichner für meine Daten gleich bleiben und dahinter der gewünschte Maschinenparameter steht, ist alles paletti. Zudem kommt noch, dass ich keine Zeit und schon gar keine Lust habe, mich in (ggf externe) Software zum handling dieser Daten einzuarbeiten.

    Dazu kommt, dass ich, wie du aus den vorherigen Posts sicherlich längst festgestellt hast, größtenteils lesend auf die Daten zugreife. Schreibend gibt´s auch den einfachen Weg: Datei öffnen und den Text an der Position des zu schreibenden Strings teilen, den LINKEN Teil des "Textes" zusammen mit dem STRING und dem RECHTEN Teil zusammenfassen und speichern, fertig.

    Sicherlich werden jetzt einige von euch "Programmierern" lachen, und ehrlich gesagt habt ihr nicht mal unrecht. Ich bin ja selbst kein Programmierer...
    ABER ich verweise auf einen Vorfall vor ca. 2 Jahren, als ich mich von einigen "richtigen" Programmierern und deren Chefs der von uns verwendeten Branchensoftware auslachen lassen musste, weil ich die behäbige und nicht praktikable Umsetzung eines ihrer Softwaremodule in unserer Fertigung bemängelte. Das Modul läuft auf einem "Mini-PC" als VB-Anwendung im IE remote und kommuniziert mit einer Datenbank, ich glaube, da muss ich nicht mehr viel anderes dazu sagen.
    Auf meinen Lösungsvorschlag folgte schallendes Gelächter und ein anschließendes Angebot, das Modul für etliche tausend Euros zu bearbeiten und/oder auf "Updates" zu warten. Naja, auf die Updates wartet kein Mensch mehr, kein Wunder, zaubern können die nämlich nicht, in der verwendeten Umgebung ist halt "nicht mehr drin"!
    Ich habe daraufhin das Modul durch ein von mir entwickeltes TDM "Textdateienbasiertes Datenbankähnliches Modell" :D ersetzt mit dem Geschwindigkeitsfaktor 100. Das spart an ca. 10 Arbeitsplätzen täglich 1-2 Stunden und hat mich alles in allem 3 Tage "gehacke" gekostet. Als Frontend nutze ich gezwungenermaßen Excel :rofl:
    Beim nächsten Besuch der Programmierer waren die fassungslos, kein Wunder. Ihr Chef bat mich dann, meine Anwendung mit deren Logo zu versehen, damit sie damit auch bei anderen Kunden mit der "hervorragenden" Eignung ihrer Software im Hinblick auf Erweiterbarkeit und Anpassung auf kundeneigene Gegebenheiten werben können. Ich habe das wohlwollend zur Kenntnis genommen, provokativ sofort unsere eigenes Logo eingebaut, und habe seitdem einen guten Kontakt zu diesen Jungs...Wirklich ernst nehmen die mich noch nicht, aber mich auszulachen traut sich aber auch keiner mehr.

    Beim Kauf neuer Produktionsmaschinen im Millionenbereich kam dann auch das Gespräch auf ein "Übersetzungsprogramm" der von der Branchensoftware generierten Maschinendaten auf das Format der Maschinenhersteller. Im Raum standen für dieses "Übersetzungsprogramm" >50.000€. Die Datensatzbeschreibungen beider Formate als "Textdateien" lagen auf dem Tisch. In meiner herzhaften Art hab ich dann nur noch gefragt "Wollen sie mich verar***, so ein Programm habe ich in zwei Wochen selbst geschrieben! Textdatei öffnen, per lowlevel-Stringbefehlen die relevanten Daten auslesen und in einer anderen Anordnung wieder in eine Textdatei schreiben ist eine Aufgabe für einen Praktikanten!". Pikiert sind die Jungs abgehauen. Nach drei Tagen lag ein Angebot über 5.000€ auf dem Tisch, na also, geht doch.

    Was das jetzt mit dem Threadtitel zu tun hat?
    Lohnt die Einarbeitung in ein "neues" Konzept, welches zugegebenermaßen bei bestimmten Anwendungen Vorteile bietet, aber bei anderen Problemstellungen massiv (ich zitiere: ) "abstinkt"?
    Bin ich auf Gedeih und Verderb den Vorgaben von "Programmierern" und deren Skill ausgeliefert?
    Reichen einem Anfänger für eine einfache Textbearbeitung nicht auch die einfachen Stringfunktionen, wieso wird sofort RegEx empfohlen?
    Muss man CSV-ähnliche Dateien in Scripting Dictionarys verfrachten um darüber an Daten zu kommen?
    "Braucht" man Map´s wirklich?

    • Offizieller Beitrag

    Mal vorweg: Ich bin Beta-Verweigerer und habe somit noch keinen Blick auf diese Struktur geworfen.
    Ich denke, hier treffen verschiedene Vorstellungen aufeinander. Dem Einen ist es recht, wenn der Funktionsumfang der Sprache so groß wie möglich ist, dem Anderen reicht eine schmale Basis, die man gern durch UDF aufpeppen kann. Und hier gibt es vermutlich für beide Seiten ausreichend Für und Wider. Einen gemeinsamen Nenner können wir eh nicht stellen - den legt Jos fest. :D

  • BugFix

    Letztlich sind sowieso die Verwendungen der neuen Funktionen interessant, welche diese komplett zweckentfremden ^^

    Das hier klappt dann zukünftig deutlich besser, mit Sachen wie:

    [autoit]

    local $class[]

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

    Func somefunc($str)
    MsgBox(0,'',$str)
    EndFunc
    $class.afunc = somefunc

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

    somefunc(FuncName($class.afunc))
    $class.afunc("s")

    [/autoit]

    ^^

    • Offizieller Beitrag

    minx
    :D
    Ja, eine "Objektisierung" läßt sich damit durchaus erreichen. Aber mal ehrlich: Meinst du irgendjemand kann diesen Code dann noch entziffern? ;)
    Es ist super, wenn man durch Verbiegen der Syntax tolle Side-Effekte erreicht. Doch dann bitte jede Codezeile mit 3 Kommentarzeilen versehen, damit du später auch selbst durch dieses 'Gewirr' durchsteigst. :P

  • Zitat von BugFix

    Dem Einen ist es recht, wenn der Funktionsumfang der Sprache so groß wie möglich ist, dem Anderen reicht eine schmale Basis, die man gern durch UDF aufpeppen kann. Und hier gibt es vermutlich für beide Seiten ausreichend Für und Wider.

    was aber die Frage offenlässt, warum ein "Objekt" Maps aus den Fingern gesaugt wird, welches die Funktionalität von Arrays nachbildet, mit dem Unterschied, SCHNELL und DYNAMISCH erweitert werden zu können.
    Imho wäre 99% der User eher damit geholfen, das elend langsame ReDim(men) von Arrays anzupassen bzw. gleich wegzulassen (und wenn man schon dabei ist, eine dynamische Key/Value und Value/Key-Funktion einzubauen), als eine neue Funktion zu schnitzen, welche einigen wenigen Cracks bei Spezialanwendungen Vorteile bringt....

  • Imho wäre 99% der User eher damit geholfen, das elend langsame ReDim(men) von Arrays anzupassen bzw. gleich wegzulassen (und wenn man schon dabei ist, eine dynamische Key/Value und Value/Key-Funktion einzubauen),

    Gerade du solltest doch wissen wie Arrays intern im Speicher aussehen und warum ein ReDim eben so lange dauert. Da lässt sich schon prinzipbedingt nicht viel anpassen.
    Und wie willst du auf diese Struktur einfach mal so eine vernünftige Key/Value-Funktionalität draufsetzen ohne ihre Anordnung im Speicher zu verändern?
    Der Datentyp wurde nicht aus den "Fingern gesaugt" sondern war notwendig wenn man ein dynamisches Array haben will.
    So gut wie jede andere Programmiersprache bietet dies auch - und in der Regel sogar noch weit mehr Strukturtypen.

    Es bleibt dabei: Jede Datenstruktur hat Vor-und-Nachteile und deswegen ihren jeweiligen Anwendungsfall für den es geeigneter ist als andere.
    Das ist auch kein Spielzeug für Cracks sondern schlicht Grundlagen der Programmierung.
    Autoit soll Dinge möglichst schnell und einfach bewerkstelligbar (tolles Wort) machen.
    Eine Map hilft da ungemein.
    Ansonsten muss man sich halt eine Sprache suchen welche zum Ziel hat nur die minimalste Funktionalität zu bieten. AutoIt ist diese Sprache aber ganz sicher nicht.

  • Es fehlen noch 2 wirklich simple Sachen, damit man richtige "Objekte" damit erzeugen kann.
    1. Der Aufruf von minx mit $a.methode('s') muss funktionieren
    2. Im Aufruf muss eine $self (oä) Variable bestehen die ByRef auf die Map wirken kann.

    Spoiler anzeigen
    [autoit]


    Global $self

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

    Test()

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

    Func Test()

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

    Local $Ball1 = Ball(100, 100, 50)
    Local $Ball2 = Ball(200, 200, 100)

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

    $self = $Ball1 ; Das hier muss weg -> Der Funktionsaufruf braucht eine Referenz zur aufrufenden Map
    ConsoleWrite('Ball1: ' & Call($Ball1.GetPos)[0] & ',' & Call($Ball1.GetPos)[1] & @CRLF)
    $self = $Ball2
    ConsoleWrite('Ball2: ' & Call($Ball2.GetPos)[0] & ',' & Call($Ball2.GetPos)[1] & @CRLF)

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

    $self = $Ball1
    Call($Ball1.Move, 10, 10) ; Die dummen Calls sollte man eigentlich garnicht brauchen, IsFunc gibt 1 zurück !
    ConsoleWrite('Ball1: ' & Call($Ball1.GetPos)[0] & ',' & Call($Ball1.GetPos)[1] & @CRLF)

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

    $self = $Ball2
    Call($Ball2.Move, 1000, 1000, 200)
    ConsoleWrite('Ball2: ' & Call($Ball2.GetPos)[0] & ',' & Call($Ball2.GetPos)[1] & @CRLF)

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

    EndFunc

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

    Func Ball($mx, $my, $d)

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

    ; Konstruktor
    Local $Ball[]
    $Ball.X = $mx
    $Ball.Y = $my
    $Ball.D = $d

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

    ; Methoden
    $Ball.Move = __BallSetPos
    $Ball.GetPos = __BallGetPos

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

    Return $Ball

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

    EndFunc

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

    Func __BallGetPos()
    Local $a[] = [$self.X, $self.Y]
    Return $a
    EndFunc

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

    Func __BallSetPos($mx, $my, $d = 0)
    $self.X = $mx
    $self.Y = $my
    If $d Then $self.D = $d
    EndFunc

    [/autoit]


    Das $self Problem lässt sich leider nicht so einfach (auch nicht mit Workarounds) umgehen, da die Funktion keinerlei Information besitzt von wo sie aufgerufen wurde.

  • Gerade du solltest doch wissen wie Arrays intern im Speicher aussehen und warum ein ReDim eben so lange dauert. Da lässt sich schon prinzipbedingt nicht viel anpassen.
    Es bleibt dabei: Jede Datenstruktur hat Vor-und-Nachteile und deswegen ihren jeweiligen Anwendungsfall für den es geeigneter ist als andere.
    Autoit soll Dinge möglichst schnell und einfach bewerkstelligbar (tolles Wort) machen.
    Eine Map hilft da ungemein.


    Stimmt, aber wenn das wirklich das Ziel ist, wieso muss ich dann fast alle Datenstrukturen aus COM Objekten "importieren?" Mit einer einfach verketteten Liste könnte man zum Beispiel das ReDim Problem relativ einfach lösen, und wenn wir sowas nativ zur Verfügung hätten könnte sich auch jeder die passende Struktur für den jeweiligen Anwendungsfall aussuchen. Oder noch besser, wieso nicht gleich C- bzw. Haskell-artige Strukturen? Dann wäre man nicht mehr auf den Entwickler angewiesen und könnte sich so ziemlich jede Datenstruktur selbst zusammenbasteln.

  • Stimmt, aber wenn das wirklich das Ziel ist, wieso muss ich dann fast alle Datenstrukturen aus COM Objekten "importieren?" Mit einer einfach verketteten Liste könnte man zum Beispiel das ReDim Problem relativ einfach lösen, und wenn wir sowas nativ zur Verfügung hätten könnte sich auch jeder die passende Struktur für den jeweiligen Anwendungsfall aussuche

    Hast du den Thread gelesen?
    Mit Map steht (zumindest in der Beta) nun ein nativer AutoIt-Datentyp zur Verfügung welcher sowohl als Key-Value-Struktur als auch als dynamisches Array verwendet werden kann.
    Importieren musst du nichts mehr.

    Oder noch besser, wieso nicht gleich C- bzw. Haskell-artige Strukturen?

    Verkettete Listen kannst du auch jetzt schon mit DllStructCreate erzeugen. Einziges Manko ist, dass als Value nicht Variant funktioniert.

    @Bugfix/Mars
    Der "Python-Weg" wäre eine Alternative. Hierbei bekommt jede Klassenmethode als ersten Parameter eine Variable namens "self". Diese enthält einen Verweis auf das Objekt welche die Funktion aufruft. Erst damit werden Funktionen mit den jeweiligen Objekten verknüpft. Bei anderen Sprachen wird implizit das Selbe gemacht. Bei Python wird es hingegen lediglich explizit geschrieben.
    Beim Aufrufen müsste man dann also als ersten Parameter das Objekt mit übergeben.
    Wirklich objektorientiert wird das dann aber auch noch nicht. Vor allem die Möglichkeit Attribute vor Zugriff außerhalb des Objektes zu schützen klappt hier nicht.

    Einmal editiert, zuletzt von AspirinJunkie (10. August 2014 um 18:40)

  • Hast du den Thread gelesen? Mit Map steht (zumindest in der Beta) nun ein nativer AutoIt-Datentyp zur Verfügung welcher sowohl als Key-Value-Struktur als auch als dynamisches Array verwendet werden kann. Importieren musst du nichts mehr.

    Stimmt. Das ist aber auch nur ein Fall von vielen und wenn ich etwas anderes außer Arrays oder Maps haben will muss ich trotzdem auf Objekte zurückgreifen. Meine Aussage bezog sich mehr oder weniger auf die Aussage "Autoit soll Dinge möglichst schnell und einfach bewerkstelligbar machen;" nicht auf Maps speziell. Aber vielleicht kommt das ja alles noch, neue Features entstehen in letzter Zeit ja sowieso relativ häufig. :)

    Verkettete Listen kannst du auch jetzt schon mit DllStructCreate erzeugen. Einziges Manko ist, dass als Value nicht Variant funktioniert.

    Außerdem muss der Rückgabewert von DllStructCreate irgendwo (z.B. in einem Array) zwischengespeichert werden, auch wenn er gar nicht verwendet wird. Zum Erstellen eigener Datenstrukturen ist das also nicht optimal.

  • Außerdem muss der Rückgabewert von DllStructCreate irgendwo (z.B. in einem Array) zwischengespeichert werden, auch wenn er gar nicht verwendet wird. Zum Erstellen eigener Datenstrukturen ist das also nicht optimal.

    Doch das geht schon. Man nimmt die Speicherallokation von Dllstructcreate weg z.B. mit _MemGlobalAlloc(). Hier mal ein kleines unausgereiftes Beispiel wie man in AutoIt eine einfach verkettete Liste erstellen kann:

    Grundprinzip einer einfach verketteten ArrayList in AutoIt
    [autoit]

    #include <Memory.au3>
    Global Const $tag_LinkedList = "wchar value[255];ptr next"

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

    ; leere neue Arraylist erstellen:
    Global $a_List = ArrayList()

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

    ; Elemente hinzufügen
    $a_Act = $a_List
    For $i = 1 To 100
    $a_Act = ArrayListAppend($a_Act, "Test" & $i)
    Next

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

    ; Liste iterieren und Elemente wieder ausgeben
    $a_Act = $a_List ; Anfangselement
    Do
    ConsoleWrite($a_Act.value & @CRLF)
    $a_Act = ArrayListGetNext($a_Act)
    Until @error

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

    #region ArrayList-Funktionen
    ; eine neue Arraylist erzeugen ($value: Wert ansonsten NULL)
    Func ArrayList($value = Default, $pointer = Default)
    Local $t_List = ($pointer = Default) ? DllStructCreate($tag_LinkedList) : DllStructCreate($tag_LinkedList, $pointer)
    $t_List.value = ($value = Default) ? Null : $value
    $t_List.Next = Null
    Return $t_List
    EndFunc ;==>ArrayList

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

    ; gibt das nächste ArrayList-Element des übergebenen Elementes (@error falls letztes Element erreicht)
    Func ArrayListGetNext(ByRef $aArr)
    If $aArr.Next = 0 Then Return SetError(1, 0, $aArr)
    Return DllStructCreate($tag_LinkedList, $aArr.Next)
    EndFunc

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

    ; Fügt ein Element ans Ende der Liste
    Func ArrayListAppend(ByRef $aList, $value)
    If $aList.value = "" Then ; falls erstes Element beschrieben wird
    $aList.value = $value
    Return $aList
    Else
    $aLast = $aList
    Do
    $aLast = ArrayListGetNext($aLast)
    Until @error

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

    Local $hAdress = _MemGlobalLock(_MemGlobalAlloc(DllStructGetSize($aLast)))
    Local $aElement = ArrayList($value, $hAdress) ; Neues Element erzeugen
    $aLast.Next = $hAdress
    EndIf
    Return $aElement
    EndFunc ;==>ArrayListAppend
    #EndRegion

    [/autoit]


    Bei mir ist schon diese unoptimierte Variante beim dynamischen Erstellen einer Liste mit 1000 Elemente mehr als dreimal so schnell wie Array mit ReDim!
    Dafür sind die zufälligen Zugriffe im Vergleich zum Array unterirdisch langsam da die Liste jedesmal von Anfang an durchlaufen werden muss. Komplettes durchiterieren ist daher hingegen etwa gleich schnell (wenn beides nativ vorliegen würde).

    2 Mal editiert, zuletzt von AspirinJunkie (12. August 2014 um 10:16) aus folgendem Grund: GetNext als Funktion implementiert - damit es bisschen übersichtlicher und klarer wird.

  • Gerade du solltest doch wissen wie Arrays intern im Speicher aussehen und warum ein ReDim eben so lange dauert. Da lässt sich schon prinzipbedingt nicht viel anpassen.

    Doch es geht. Wenn man den Speicher nicht linear auf die benoetige Groesse erhoeht, sonder z.B. immer auf die naechste Zweierpotenz aufrundet hat man zwar minimalen Speicherverlust, reduziert aber die amortisierte Laufzeit beim Wachsen von quadratisch auf linear. Ausserdem gibt es ja noch andere Datenstrukturen, die wie ein Array verwendet werden. In C++ gibt es eine, die sich deque nennt und reserviert den Speicher in mehreren grossen Bloecken (meistens 4 kb). Die ganze Datenstruktur funktioniert wie ein Array, laesst sich aber wesentlich schneller fuellen.

    Zudem gibt es bei 64 bit Systemen noch eine weitere und sehr maechtige, wenn auch sehr selten eingesetzte Alternative: Man reserviert fuer ein Array einen grossen Bereich virtuellen Speicher (z.B. 2 GB). Man verbraucht hierbei nur Addressbereich und den hat man genug. Das Betriebssystem mappt die Addressen erst dann auf physichen Speicher, wenn man darauf zugreift.