Zu : Funktion _Number() von BugFix

  • Im Thread :

    IsNumber mit String, der nur Zahlen enthält

    hat BugFix auf folgendes Problem hingewiesen :

    Die Standardfunktion Number() zeigt in einem Fall ein unerwartetes Verhalten. Strings werden zu 0 konvertiert, aber "0" auch (Details siehe stackoverflow) !

    Der Link in seinem Beitrag ist aber einem Forumsupdate zum Opfer gefallen.

    (Der Thread selbst wurde geschlossen - daher konnte ich dort nicht 'reinschreiben)


    Da ich die Funktion gerade selbst brauche, hier, für Interessierte, der entsprechende Code

    Quelle : https://stackoverflow.com/ques…vert-a-string-to-a-number


    Gruß Musashi



    EDIT BugFix:


    Ich habe die Positionen aus dem Diskussionsverlauf mal aufgenommen und die Funktion entsprechend angepasst.

    - Es werden jetzt auch negative Werte erkannt

    - Im Fehlerfall wird ein (frei festlegbarer) numerischer Wert zurückgegeben. Standard ist 0xDEADBEEF ;)

    Es ist schon sinnvoller bei einer Funktion, die numerische Werte zurückgeben soll, dies auch im Fehlerfall zu tun.

    Bei Übergabe von Werten mit Vorzeichen, wird auch ein Leerzeichen zwischen Vorzeichen und Digit akzeptiert (sowohl: -1.25 als auch - 1.25).


    EDIT2 BugFix: ---> Hier hatte ich zwar die Erkennung des Leerzeichens zwischen Vorzeichen und Ziffer berücksichtigt, aber nicht entsprechend behandelt. :Face:

    Nun sollte es aber fehlerfrei sein:


    EDIT3 BugFix: Signed Numbers könnten ja auch positive Vorzeichen haben...

  • Abgesehen davon, daß ich den Zweck der Funktion _number nicht ganz verstehe, würde ich wenn ich solch

    eine Funktion brauche es folgendermassen machen



    MsgBox(4096, "Titel", _Number("tmpm-567.2ory"))

    Func _Number($_Expression, $_Flag=0)

    $Val = StringRegExp($_Expression, '\D*?(-?[0-9.]+)',2) ;\D+(-*[0-9.]+) \D+(\d+) \D*?(-?[0-9.]+)

    If Not @error Then

    Return Number($Val[1], $_Flag)

    Else

    Return "Fehler"

    EndIf

    EndFunc ;==>_Number



    Hier kann ich dann abfragen ob die Funktionsrückgabe "Fehler" lautet und wenn nicht dann rechne mit dem Rückgabewert.

    Hier kommt immer entweder eine Zahl inklusive 0 Komma und -Vorzeichen oder eben Fehler(Falls keine Zahlen enthalten sind) heraus.


    Sorry wegen der Formatierung(habe leider keinen Einfluß darauf)

  • Hier kann ich dann abfragen ob die Funktionsrückgabe "Fehler" lautet und wenn nicht dann rechne mit dem Rückgabewert.

    Hier kommt immer entweder eine Zahl inklusive 0 Komma und -Vorzeichen oder eben Fehler(Falls keine Zahlen enthalten sind) heraus.

    Rückgabewerte zu mischen ist kein guter Code da du ambivalente Typen hast. Return lieber eine 0 und setz @error.

  • Abgesehen davon, daß ich den Zweck der Funktion _number nicht ganz verstehe, ....

    Hi Tuxedo !

    Das hat BugFix im stackoverflow-Beitrag doch erklärt, Zitat :

    Be aware, that Number() works in one case unexpected. Strings will converted to 0, but "0" also. You have no chance, to see the difference. It's written in the help file (A string beginning with letters has a numeric value of zero.), but it is not satisfactory.

    I've made my own function, that exactly works, like Number(). But if you pass an expression, that is a string or starts with a string, this returns an empty string and sets @error to 1.


    Gruß Musashi

  • Nein Alpines diesmal eben nicht. Null als Rückgabe ist Scheisse weil eben auch der String gewollt eine Null als Nummer beinhalten könnte.

    Und wenn ich bei einem Fehler eine Null zurückgebe, wie willst du dann sehen ob die Null eine Nummer oder eben der Fehler ist.


    Achja du setzt ja noch den Fehler zusätzllich, nagut dann must du eben auch den Wert abfragen und bei Null noch nachsehen ob Fehler ja oder nein.

    Dafür muss ich bei meiner Funktion nur den Wert abfragen und weiss gleich ob ich damit rechnen kann oder nicht.


    Und zum schlechten Stil, es gibt doch in Autoit genug Funktionen die gemischte Rückgaben(Strings oder Zahlen meist -1 bis etwa 5) haben.

    Das ist genauso wie die ewigen Diskussionen ob Goto oder Gosub schlechte Befehle sind,

    wenn man damit nicht umgehen kann sind es schlechte Befehle.

  • Nein Alpines diesmal eben nicht. Null als Rückgabe ist Scheisse weil eben auch der String gewollt eine Null als Nummer beinhalten könnte.

    Und wenn ich bei einem Fehler eine Null zurückgebe, wie willst du dann sehen ob die Null eine Nummer oder eben der Fehler ist.

    Du solltest das nächste Mal vielleicht gründlicher lesen ;)

    Rückgabewerte zu mischen ist kein guter Code da du ambivalente Typen hast. Return lieber eine 0 und setz @error.

    Aber das hast du ja schon selbst bemerkt.


    Bei dir muss man zwar nur einen Wert abfragen aber das ist vom Stil her keine feine Sache, du mischt deine Rückgabetypen und das sollte man für gewöhnlich nicht machen.

    Das mag in AutoIt keinen großen Unterschied machen aber Vergleiche mit Strings sind teuer und kosten mehr Zeit als wenn ich zwei Integer miteinander vergleiche.


    @error ist in AutoIt ja bereits fest verankert, da sollte man nicht extra noch eine neue Methode der Fehlererkennung raushauen wenn sie keinen nennenswerten Mehrwert bietet,

    das resultiert einfach nur darin, dass man mehr im Kopf haben muss und solche Selbstverständlichkeiten rausdenken muss.


    Und zum schlechten Stil, es gibt doch in Autoit genug Funktionen die gemischte Rückgaben(Strings oder Zahlen meist -1 bis etwa 5) haben.

    Richtig, und das befürworte ich ja auch nicht, das sollte dir aber keinen Freischein dafür geben selber schlechten Code zu schreiben.

    Das ist aber der Tatsache geschuldet das AutoIt schon etliche Jahre alt ist und sämtliche Skripte kaputtgehen würden wenn man alles neu-konsistent schreiben würde.

    Man hat anfänglich die falsche Entscheidung getroffen (meine persönlich Meinung!!) und muss jetzt die Linie weiterziehen, statt alles so gründlich wie möglich zu halten. (Sowohl Kernfunktionen als auch die UDFs)


    Das ist genauso wie die ewigen Diskussionen ob Goto oder Gosub schlechte Befehle sind,

    Sind sie nicht, wenn der Großteil sie nicht korrekt einsetzen kann dann ändert es an der Funktionalität nichts.

  • Tuxedo !


    In der Console sieht man dann den Unterschied :

    Code
    1. + VARIANTE mit Expression = <0>
    2. > @@BugFix ==> _Number : Expression= <0> Return= <0> Error = 0
    3. > @@Standard ==> Number : Expression= <0> Return= <0>
    4. + VARIANTE mit Expression = <Tuxedo 7>
    5. > @@BugFix ==> _Number : Expression= <Tuxedo 7> Return= <> Error = 1
    6. ! @@Standard ==> Number : Expression= <Tuxedo 7> Return= <0>


    Richtig, und das befürworte ich ja auch nicht, das sollte einem aber keinen Freischein dafür geben selber schlechten Code zu schreiben.

    :rofl: (Das gilt dem Statement i.A., geht nicht gegen Dich, Tuxedo  ;))


    Gruß Musashi

  • Funktioniert so aber leider nicht mit negativen Werten...

    Code
    1. Local $sNum = '-0.3'
    2. ConsoleWrite(StringFormat('> Number : %12s \t #Error: %i\r\n', Number($sNum), @error))
    3. ConsoleWrite(StringFormat('! _Number : %12s \t #Error: %i\r\n', _Number($sNum), @error))
    4. > Number : -0.3 #Error: 0
    5. ! _Number : #Error: 1
  • Funktioniert so aber leider nicht mit negativen Werten...

    Hi Bitnugger !

    Da hast Du recht !

    Mir ging es ursprünglich nur darum, den Zugang zu dieser Funktion wieder 'frisch' zu machen;) .

    Ob BugFix das übersehen - oder absichtlich nicht berücksichtigt - hat, kann ich nicht beurteilen (seine Funktion -> seine Sache). Die Angabe "... funktioniert genau wie Number()..." ist dann aber missverständlich.

    _Number() hat den Vorteil, dass eine Textkette mit nicht-nummerischem Anfang @error und leer zurückgibt, nicht 0 (das war ja auch das Ziel). In den anderen Fällen erscheint mir Number() geeigneter. Wer möchte kann sich das selbst erstellen, die Logik dafür ist einfach :

    -> Zeichen1 = 0..9 oder - oder, wenn man möchte, auch +

    -> Falls Zeichen1 - oder +, dann muss Zeichen2 in 0..9 liegen

    ==> Number() verwenden, ansonsten @error=1 setzen


    Gruß Musashi

  • Kurz mal als Blick über den Tellerrand: In diversen Programmiersprachen gibt es ganz andere Konzepte, um Fehler zu erkennen und abzufangen.


    Das, was wir hier haben, ist die althergebrachte Methode, wie sie auch in purem C zum Einsatz kommt. Eine Funktion gibt "Null" (das ist ein englisches Wort) bzw. "0" zurück, was beides im Endeffekt auf den Zahlenwert 0 hinausläuft. Eine typische Fehlerüberprüfung in C sieht dann also so aus: if(myVal == NULL) { ....

    Da hier zurecht Verwechslungsgefahr besteht, existiert in der Standardbibliothek noch die globale Variable errno. Eine Funktion setzt diese bei einem Fehler auf einen situationsabhängigen Zahlenwert (ungleich 0). Damit wären wir dann bei einer Fehlerabfrage a la if(errno) { .... Das kommt einem AutoIt-Programmierer jetzt vermutlich bekannt vor.


    Nachtrag: Es gibt tatsächlich auch Sprachen, die schlicht NULL und 0 als unterschiedliche Werte betrachten. "0" ist bspw. eine Instanz der Klasse Integer, "NULL" ist tatsächlich 0.


    Das Problem hierbei ist, dass Fehlerbehandlung optional wird. Der Programmierer kann Fehler abfangen - muss es aber nicht. Das kann aus Faulheit, aus Unwissenheit oder einfach durch Vergessen geschehen, ist aber eine sehr große Fehlerquelle. Darüber hinaus ist die Fehlerbehandlung syntaktisch auch nicht vom wirklichen Anwendungscode unterscheidbar.


    In modernen Hochsprachen wird daher meist ein eigenes Sprachelement für diese Fehlerbehandlung genutzt, oftmals Exception (dtsch. Ausnahme) genannt. Dabei unterscheidet man bspw. bei Java zwischen einer checked und einer unchecked Exception. Beide haben gemein, dass sie mittels eines try-catch-Blockes abgefangen werden. Eine unchecked Exception kann ich abfangen, eine checked Exception muss ich abfangen. Im try-Bereich steht der Code, der eventuell einen Fehler auslösen könnte. Im catch-Bereich steht der Code, der den Fehler beim Auftreten behandelt. In Java sähe unser Beispiel mit Number() irgendwie so aus:

    Code
    1. try {
    2. myVal = Integer.parseInt(myString);
    3. } catch(NumberFormatException nfe) {
    4. System.out.println("Invalid Number");
    5. }


    Damit ist der normale Ablauf (try) strikt vom Fehlerfall (catch) getrennt. Außerdem können Ausnahmen bei verschachtelten Aufrufen auch nach oben "durchgereicht" werden.


    Sowas wäre der moderne Weg, Fehler abzufangen. Aber leeeider bietet AutoIt uns das nicht.

  • Hi chesstiger !

    (da haben sich unsere Beiträge wohl überschnitten)

    Damit ist der normale Ablauf (try) strikt vom Fehlerfall (catch) getrennt. Außerdem können Ausnahmen bei verschachtelten Aufrufen auch nach oben "durchgereicht" werden.

    Sowas wäre der moderne Weg, Fehler abzufangen. Aber leeeider bietet AutoIt uns das nicht.

    Volle Zustimmung !


    AutoIt war zu Beginn sicher nicht für das konzeptioniert, was mittlerweile damit gemacht werden kann. Die Inkonsistenzen in den Funktionen diverser UDF's sind der Entstehung über Jahre und der Vielzahl an (freiwilligen) Autoren geschuldet. Ein krebsartiges Wachstum erscheint mir dabei unvermeidlich.

    Von daher, und dem hast Du ja auch nicht widersprochen, können wir uns trotz gewisser Mängel eigentlich noch ganz glücklich schätzen.

    "Null" (das ist ein englisches Wort)

    Null leitet sich meines Wissens vom lat. nihilum ('Nichts') ab ;).


    Gruß Musashi

  • Bei Übergabe von Werten mit Vorzeichen, wird auch ein Leerzeichen zwischen Vorzeichen und Digit akzeptiert (sowohl: -1.25 als auch - 1.25).

    Wenn $_Expression ein String ist, liefert Number() und auch deine Funktion _Number() im zweiten Fall (mit Leerzeichen) allerdings 0 als Ergebnis...


    So würde es funktionieren...

    Code
    1. Func _Number($_Expression, $_iErrReturn=0xDEADBEEF, $_Flag=0)
    2. ;~ If VarGetType($_Expression) = 'String' Then $_Expression = StringStripWS($_Expression, 8)
    3. $_Expression = StringStripWS($_Expression, 8)
    4. If $_iErrReturn = Default Or $_iErrReturn = '' Then $_iErrReturn = 0xDEADBEEF
    5. If StringRegExp($_Expression, '^(\d|-\d)') Then
    6. Return Number($_Expression, $_Flag)
    7. Else
    8. Return SetError(1, 0, $_iErrReturn)
    9. EndIf
    10. EndFunc ;==>_Number
  • Nur der Ordnung halber;) :

    Bei Übergabe von Werten mit Vorzeichen, wird auch ein Leerzeichen zwischen Vorzeichen und Digit akzeptiert (sowohl: -1.25 als auch - 1.25).

    Dieser Text, und auch die neue Version von _Number() , stammt nicht von mir, sondern von BugFix. Er hat ihn freundlicherweise in meinen Startbeitrag 'hineineditiert'. An Bitnugger 's Hinweis in Post #14 ändert das natürlich nichts.

    Gruß Musashi

  • Also ich sehe keinen signifikanten Vorteil von Try Catch gegenüber @error. Wenn ich @error setze und nach meiner Funktion If @error Then ... (fehlerbehebung/return falls es mit dem Fehler unmöglich ist weiterzumachen) einfüge kommt das doch genau auf das gleiche heraus wie wenn ich im Fehlerfall eine exception werfe und diese anschließend Abfrage. Eine Art @error"Stack" wäre manchmal ganz praktisch, sodass das "durchreichen" besser funktioniert.

  • Falls es eine Rolle spielt: Number() gibt das Ergebnis immer als Double zurück, wenn in expression ein Punkt enthalten ist.


    Code
    1. Number(0<2) --> 1 Int32
    2. Number(2>0) --> 1 Int32
    3. Number(2^0) --> 1 Double
    4. Number(2.0) --> 2 Double
    5. Number(20) --> 20 Int32
    6. Number(20/4) --> 5 Double
    7. Number(20*4) --> 80 Int32
    8. Number(20-4) --> 16 Int32
    9. Number(20+4) --> 24 Int32