Strings berechnen (Auch "Eval" genannt)

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
marco2007
Beiträge: 906
Registriert: 26.10.2006 13:19
Kontaktdaten:

Beitrag von marco2007 »

@Josef Sniatecki:

Wieso machst nicht so wie Stargate empfohlen hat?
Dann funkt es nämlich.

Code: Alles auswählen

Case '+' 
   If Prio<=#CalcStr_AddASubPrio 
      *CalcStr_CChar+1 
      neu.d=CalcStr("",#CalcStr_AddASubPrio)
      Num+ neu
      Prio=0 
      Continue 
  Else 
     ProcedureReturn Num 
 EndIf


Ansonsten, echt guter Code :allright:
Windows 11 - PB 6.03 x64
_________________________________
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7028
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Beitrag von STARGÅTE »

jup stimmt,

hab "meine Stelle" bei Josef Sniatecki nicht gleich wieder gefunden.

Es wäre aber mal interessant zu wissen warum dieser "Vielleicht-BUG" ab einer 9-Stufigen Rekursion auftritt und nur im zusammenhang mit Fließkommazahlen?

Beispiel:

Code: Alles auswählen

Procedure.l TestL(Zahl.l)
 If Zahl > 1
  ProcedureReturn Zahl*TestL(Zahl-1)
 Else
  ProcedureReturn Zahl
 EndIf
EndProcedure
Procedure.f TestF(Zahl.f)
 If Zahl > 1
  ProcedureReturn Zahl*TestF(Zahl-1)
 Else
  ProcedureReturn Zahl
 EndIf
EndProcedure

Debug TestL(8)
Debug TestF(8)

Debug TestL(9)
Debug TestF(9)
Denn diesen "Zwischenspeichern" des ProcedurRückgabeWertes kann keine Notlösung auf dauer sein ...
denn das geht ja wieder gut:

Code: Alles auswählen

Procedure.f TestF(Zahl.f)
 If Zahl > 1
  k.f = TestF(Zahl-1)
  ProcedureReturn Zahl*k
 Else
  ProcedureReturn Zahl
 EndIf
EndProcedure
Bug-Forum ?
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
marco2007
Beiträge: 906
Registriert: 26.10.2006 13:19
Kontaktdaten:

Beitrag von marco2007 »

STARGÅTE hat geschrieben:Bug-Forum ?

..würd`ich schon schon sagen:

Code: Alles auswählen

Procedure.l TestF1(Zahl.l) 
 If Zahl > 1 
  ProcedureReturn Zahl+TestF1(Zahl-1)
 Else 
  ProcedureReturn Zahl 
 EndIf 
EndProcedure 

Procedure.f TestF2(Zahl.f) 
 If Zahl > 1 
  ProcedureReturn Zahl+TestF2(Zahl-1)
 Else 
  ProcedureReturn Zahl 
 EndIf 
EndProcedure 

Procedure.q TestF3(Zahl.q) 
 If Zahl > 1 
  ProcedureReturn Zahl+TestF3(Zahl-1)
 Else 
  ProcedureReturn Zahl 
 EndIf 
EndProcedure 

Procedure.d TestF4(Zahl.d) 
 If Zahl > 1 
  ProcedureReturn Zahl+TestF4(Zahl-1)
 Else 
  ProcedureReturn Zahl 
 EndIf 
EndProcedure 

Procedure.w TestF5(Zahl.w) 
 If Zahl > 1 
  ProcedureReturn Zahl+TestF5(Zahl-1)
 Else 
  ProcedureReturn Zahl 
 EndIf 
EndProcedure 

Procedure.c TestF6(Zahl.c) 
 If Zahl > 1 
  ProcedureReturn Zahl+TestF6(Zahl-1)
 Else 
  ProcedureReturn Zahl 
 EndIf 
EndProcedure

Procedure.b TestF7(Zahl.b) 
 If Zahl > 1 
  ProcedureReturn Zahl+TestF7(Zahl-1)
 Else 
  ProcedureReturn Zahl 
 EndIf 
EndProcedure

Debug testf1(9)
Debug testf2(9)
Debug testf3(9)
Debug testf4(9)
Debug testf5(9)
Debug testf6(9)
Debug testf7(9)


Ich hab` mal alle Typen probiert. Double und Float schlagen hier fehl.

lg
Marco
Windows 11 - PB 6.03 x64
_________________________________
Benutzeravatar
Danilo
-= Anfänger =-
Beiträge: 2284
Registriert: 29.08.2004 03:07

Beitrag von Danilo »

STARGÅTE hat geschrieben:Es wäre aber mal interessant zu wissen warum dieser "Vielleicht-BUG" ab einer 9-Stufigen Rekursion auftritt und nur im zusammenhang mit Fließkommazahlen?
Weil der mathematische Coprozessor nur 8 Register hat, ST(0) bis ST(7).
Bei Rekursion liegen die anderen 8 Ergebnisse noch auf dem FPU-Stack,
und wenn der 9. Wert auf diesen Stack kommt, dann verschwindet der
unterste Wert ins Nirvana.

Man kann das umgehen indem man die Floats/Doubles als Long/Quad
zurückgibt, zum Beispiel mit ProcedureReturn PeekL(@float), und dann
wieder zurückwandeln mit x.l = procedure() : float.f = PeekF(@x).

Oder man spart sich die ganzen Umwandlungen und macht es über
Strukturen mit Unions, so dass man Longs und Floats und Quads und Doubles
gleichermaßen zurückgeben kann.
cya,
...Danilo
"Ein Genie besteht zu 10% aus Inspiration und zu 90% aus Transpiration" - Max Planck
Kaeru Gaman
Beiträge: 17389
Registriert: 10.11.2004 03:22

Beitrag von Kaeru Gaman »

...oder man baut sich einen eigenen stack für höhere rekursionstiefe.
das ist dann aber was ähnliches wie die Vars zwischen zu speichern.
Der Narr denkt er sei ein weiser Mann.
Der Weise weiß, dass er ein Narr ist.
Little John

Beitrag von Little John »

Hier ist zur Veranschaulichung ein rekursiv absteigender Parser. Wie man sieht wird die Rekursion nur eingesetzt, um den Prioritäten der verschiedenen Operatoren gerecht zu werden. Dazu muss man nicht wie hier für jede Prioritätsebene eine eigene Prozedur schreiben, aber dadurch wird IMHO das Prinzip schön verdeutlicht.

Auch eine stark verschachtelte Berechnung (= große Rekursionstiefe) wird korrekt ausgeführt.

Gruß, Little John

edit 27.7.2008
Der stark erweiterte Code ist jetzt in einem eigenen Thread.
Zuletzt geändert von Little John am 27.07.2008 17:03, insgesamt 2-mal geändert.
Benutzeravatar
Josef Sniatecki
Beiträge: 657
Registriert: 02.06.2008 21:29
Kontaktdaten:

Beitrag von Josef Sniatecki »

Ich habe nocheinmal meinen Code bearbeitet und einige
Kommentare hinzugefügt. Nun wird nur eine Ebene für diesen
Term genutzt:
CalcStr("1+1+1+1+1+1+1+1+1+1+1+1+1")

Der Code funktioniert perfekt. Es passieren keine Fehler mehr,
wenn zu viele Operatoren vorkommen.

Hier ist der neue Code:

Code: Alles auswählen

;/-----------------------------------------------------------------\
;|  *** Calculate string - template ***                            |
;|    Created by Josef Sniatecki 2008.                             |
;\-----------------------------------------------------------------/

;  Durch "CalculateString" kann man einfache Stringterme
;  berechnen lassen. Die Werte werden in Doubles berechnet.
;  Dies erlaubt das Ausrechnen von komplexen Eingaben
;  wärend dem Programmablauf.
;  
;  FEATURES:
;    > Die Priorität kann durch Klammern erhöht werden.
;    > Die Priorität der Operatoren wird beachtet. (Punkt vor Strich usw.)
;    > Funktionen und Konstanten werden unterstützt. Sie können
;      auch selbst welche hinzufügen.
;    > Zahlen können auch im Binär- und Hexadezimalformat geschrieben werden.
;    > Logische Operatoren (wie &,|,! usw.) können genutzt werden.
;    > Variable Werte können leicht inerhalb eines Terms genutzt werden.
;      Mehr dazu unter "SYNTAX".
;    > Der Potenz-Operator ("^") kann genutzt werden.
;  
;  SYNTAX:
;    KOMMA:
;      Das Komma für Dezimalzahlen wird immer mit
;      einem "." gekennzeichnet.
;    FUNKTIONEN:
;      Funktionen können klein oder groß geschrieben werden.
;      Parameter werden in einer nachfolgenden Klammer gesetzt
;      und durch ein "," voneinander getrennt. Die Klammern
;      müssen auch bei keinen Parametern vorkommen.
;    KONSTANTEN:
;      Konstanten werden wie in PureBasic mit einem "#"
;      angefangen und können klein oder groß geschrieben werden.
;    VARIABLEN:
;      Variablen müssen immer ein einzelner Buchstabe sein.
;      Dieser kann klein oder groß sein; jedoch macht
;      die größe keinen Unterschied. Um einer Variable
;      einen Wert zu setzen, kann man folgende Wege nutzen:
;      > CalcStr_Var('GROßBUCHSTABE'-65)=WERT
;      > CalcStr_Var('KLEINBUCHSTABE'-97)=WERT
;    BINÄR:
;      Binärzahlen werden mit einem "%" gestartet. Dabei können
;      die nachfolgenden Binärzeichen folgende sein:
;      > 1 = 1,i,I
;      > 0 = 0,o,O
;    HEXADEZIMAL:
;      Hexadezimalzahlen werden mit einem "$" gestartet.
;      Die nachfolgenden Buchstaben können klein oder groß
;      geschrieben werden.
;    BOGENMAß:
;      Winkel können durch ein folgendes "°" in Bogenmaß
;      umgewandelt werden. Manche Funktionen nutzen
;      Parameter im Bogenmaß (zB. Sin,Cos,...).
;    POTENZEN:
;      Die Zeichen "²" und "³" können als Potenzen genutzt werden.
;      Auch bei Consolen-Anwendungen (Ascii-Codes variiren).
;    DURCHSCHNITT:
;      Der Durchschnitt aus einer beliebigen Menge von Zahlen
;      wird berechnet, indem man die Zahlen in einer
;      geschweiften Klammer angibt und mit Kommas voneinander
;      trennt.
;      Beispiel: "{10,20,30}" = 20
;    FREIHEIT:
;      Leerzeichen können ruhig zur eigenen Übersichtlichkeit des
;      Terms genutzt werden.



CompilerIf Defined(Templates_Math_CalculateString_PBI,#PB_Constant)=#False
#Templates_Math_CalculateString_PBI=#True



;/------------------------------------------\
;| Intergrations                            |
;\------------------------------------------/

Procedure.l DBin(Bin.s)
  ;DecimalBinary(Binary)
  
  Protected *Char.Character ;Character
  Protected I.l             ;Index
  Protected Dec.l           ;Decimal
  
  *Char=@Bin
  While *Char\C<>0
    Dec<<1
    If *Char\C='1' : Dec+1 : EndIf
    *Char+1
  Wend
  ProcedureReturn Dec
EndProcedure
Procedure.l DHex(Hex.s)
  ;DecimalHexadecimal(Hex)
  
  Protected *Char.Character ;Character
  Protected I.l             ;Index
  Protected Dec.l           ;Decimal
  
  *Char=@Hex
  While *Char\C<>0
    Dec<<4
    If *Char\C<='9' : Dec+*Char\C-'0' : Else : Dec+*Char\C-'A'+10 : EndIf
    *Char+1
  Wend
  ProcedureReturn Dec
EndProcedure



;/------------------------------------------\
;| Constants                                |
;\------------------------------------------/

Enumeration 1  ;CalculateString_Priorities
  #CalcStr_AddASubPrio ;AddAndSubtract~
  #CalcStr_MulADivPrio ;MultiplicateAndDivide~
  #CalcStr_LogicalPrio
  #CalcStr_PowPrio     ;Power~
  #CalcStr_NotPrio
EndEnumeration



;/------------------------------------------\
;| Variables                                |
;\------------------------------------------/

Global *CalcStr_CChar.Character=0 ;{ CalculateString_CurrentCharacter
;  Dies ist der Lesezeiger des zu berechnenden
;  Terms.
;}
Global CalcStr_Prio.b             ;~Priority
Global Dim CalcStr_Var.d(25)      ;~Variable
Global *CalcStr_FuncA             ;~FunctionArray
Global *CalcStr_FuncA_End.l       ;~FunctionArray_End
Global *CalcStr_ConstA            ;~ConstantArray
Global *CalcStr_ConstA_End.l      ;~ConstantArray_End



;/------------------------------------------\
;| Declarings                               |
;\------------------------------------------/

Declare.d CalcStr(Term.s="",Prio.b=0)
Declare.d CalcStr_Num()
Declare.d CalcStr_Bin()
Declare.d CalcStr_Hex()
Declare.d CalcStr_Func()
Declare.l CalcStr_Args(ArgAm.l)
Declare.d CalcStr_Const()



;/------------------------------------------\
;| Functions                                |
;\------------------------------------------/

Procedure.d CalcStr(Term.s="",Prio.b=0)
  ;CalculateString(Term="",Priority=0)
  ;{
  ;  Diese Funktion liest ein Term, Zeichen
  ;  für Zeichen und berechnet diesen.
  ;  Falls kein Term angegeben wird, wird
  ;  der Lesezeiger nicht erneut an den
  ;  Anfang des Terms gesetzt.
  ;  
  ;  Der Lesezeiger ("*CalcStr_CChar") ist
  ;  global und wird in jeder Ebene benutzt.
  ;
  ;  WARNUNG:
  ;    Bitte diese Prozedur nicht mehrmals
  ;    gleichzeitig nutzen (In Threads usw.),
  ;    Da diese Funktion und viele andere
  ;    dazugehörigen Funktionen, globale
  ;    Variablen nutzen und modifizieren.
  ;}
  
  #CalcStr_InvChar=0 ;~InvaildCharacter
  
  Protected Num.d ;Number
  Protected L1.l  ;Long1
  Protected L2.l  ;Long2
  
  If Term ;Neuer Term.
    *CalcStr_CChar=@Term ;Lesezeiger auf den Anfang des Terms setzen.
  EndIf
  
  While *CalcStr_CChar\C<>0
    Select *CalcStr_CChar\C
      
      ;[ Werte auslesen ]
      Case '0' To '9'
        Num=CalcStr_Num()
      Case '%' ;Binär
        *CalcStr_CChar+1
        Num=CalcStr_Bin()
      Case '$' ;Hexadezimal
        *CalcStr_CChar+1
        Num=CalcStr_Hex()
      Case 'A' To 'Z','a' To 'z'
        Select PeekC(*CalcStr_CChar+1) ;Nächstes Zeichen.
          Case 'A' To 'Z','a' To 'z' ;Funktion.
            Num=CalcStr_Func()
            *CalcStr_CChar+1
            Continue
          Default ;Variable (Nur ein Zeichen).
            If *CalcStr_CChar<=90
              Num=CalcStr_Var(*CalcStr_CChar\C-65) ;Großbuchstabe.
            Else
              Num=CalcStr_Var(*CalcStr_CChar\C-97) ;Kleinbuchstabe.
            EndIf
        EndSelect
      Case '#' ;Konstante auslesen.
        *CalcStr_CChar+1
        Num=CalcStr_Const()
      
      ;[ Operatoren ]
      ;{ Schema:
      ;    Case 'ZEICHEN FÜR OPERATOR'
      ;      If Prio<#KONSTANTE_FÜR_PRIORITÄT_DES_OPERATORS
      ;        *CalcStr_CChar+1 ;Über das Zeichen springen.
      ;        BERECHNUNG.
      ;        ;Bei der Berechnung muss eine neue Ebene
      ;        ;zum zurückgeben eines zweiten Faktors
      ;        ;aufgerufen werden. Dabei muss der erste Parameter
      ;        ;ein "" sein und der zweite die Konstante
      ;        ;mit der Priorität des Operators.
      ;      Else
      ;        ProcedureReturn Num
      ;      EndIf
      ;}
      Case '+'
        If Prio<#CalcStr_AddASubPrio
          *CalcStr_CChar+1
          Num+CalcStr("",#CalcStr_AddASubPrio)
          Continue
        Else
          ProcedureReturn Num
        EndIf
      Case '-'
        If Prio<#CalcStr_AddASubPrio
          *CalcStr_CChar+1
          Num-CalcStr("",#CalcStr_AddASubPrio)
          Continue
        Else
          ProcedureReturn Num
        EndIf
      Case '*'
        If Prio<#CalcStr_MulADivPrio
          *CalcStr_CChar+1
          Num*CalcStr("",#CalcStr_MulADivPrio)
          Continue
        Else
          ProcedureReturn Num
        EndIf
      Case '/'
        If Prio<#CalcStr_MulADivPrio
          *CalcStr_CChar+1
          Num/CalcStr("",#CalcStr_MulADivPrio)
          Continue
        Else
          ProcedureReturn Num
        EndIf
      Case '&'
        If Prio<#CalcStr_LogicalPrio
          *CalcStr_CChar+1
          L1=Num
          L2=CalcStr("",#CalcStr_LogicalPrio)
          L1&L2
          Num=L1
          Continue
        Else
          ProcedureReturn Num
        EndIf
      Case '|'
        If Prio<#CalcStr_LogicalPrio
          *CalcStr_CChar+1
          L1=Num
          L2=CalcStr("",#CalcStr_LogicalPrio)
          L1|L2
          Num=L1
          Continue
        Else
          ProcedureReturn Num
        EndIf
      Case '!'
        If Prio<#CalcStr_LogicalPrio
          *CalcStr_CChar+1
          L1=Num
          L2=CalcStr("",#CalcStr_LogicalPrio)
          L1!L2
          Num=L1
          Continue
        Else
          ProcedureReturn Num
        EndIf
      Case '^'
        If Prio<#CalcStr_PowPrio
          *CalcStr_CChar+1
          Num=Pow(Num,CalcStr("",#CalcStr_PowPrio))
          Continue
        Else
          ProcedureReturn Num
        EndIf
      Case '~'
        If Prio<#CalcStr_NotPrio
          *CalcStr_CChar+1
          L1=CalcStr("",#CalcStr_NotPrio)
          L1=~L1
          Num=L1
          Continue
        Else
          ProcedureReturn Num
        EndIf
        
      ;[ Umwandlungen ]
      Case '°',248 ;Winkel in Bogenmaß umwandeln.
        Num=Num*2*#PI/360
      Case '²',253 ;²
        Num=Num*Num
      Case '³',252 ;³
        Num=Num*Num*Num
        
      ;[ Extras ]
      Case '{' ;Durchschnitt.
        *CalcStr_CChar+1
        L1=0
        Repeat
          Num+CalcStr()
          L1+1
        Until *CalcStr_CChar\C='}' Or PeekC(*CalcStr_CChar-1)<>','
        Num/L1
      Case '}' ;Ende des Durchschnitts.
        ProcedureReturn Num
        
      ;[ Verwaltung der Ebenen ]
      Case ','           ;Parameter zurückgeben.
        *CalcStr_CChar+1 ;Über das Komma springen.
        ProcedureReturn Num
      Case '('           ;Neue Ebene hinzufügen.
        *CalcStr_CChar+1 ;Über "Klammer auf" springen.
        Num=CalcStr()    ;Nummer aus einer neuen Prozedur auslesen und berechnen.
        *CalcStr_CChar+1 ;Über "Klammer zu" springen.
        Continue
      Case ')'           ;Eine Ebene zurückspringen.
        ProcedureReturn Num
      Case ' '           ;Unnützliches Zeichen.
        *CalcStr_CChar+1 ;Ignorieren.
        Continue
        
      Default
        CompilerIf #PB_Compiler_Debugger
          Debug "CalcStr() error: "+Chr(32)+Chr(*CalcStr_CChar\C)+Chr(32)+" is an invaild character!"
          Beep_(250,250)
          End
        CompilerEndIf
    EndSelect
    *CalcStr_CChar+1
  Wend
  ProcedureReturn Num
EndProcedure
Procedure.d CalcStr_Num()
  ;CalculateString_Number()
  
  Protected NumAStr.s ;NumberAsString
  
  Repeat
    Select *CalcStr_CChar\C
      Case '0' To '9','.'
        NumAStr+Chr(*CalcStr_CChar\C)
      Default                         ;Unbekanntes Zeichen.
        *CalcStr_CChar-1              ;Lesezeicher vor dem unbekannten Zeiger setzen.
        ProcedureReturn ValD(NumAStr) ;Gelesener String als Zahl zurückgeben.
    EndSelect
    *CalcStr_CChar+1
  ForEver
  ProcedureReturn 0
EndProcedure
Procedure.d CalcStr_Bin()
  ;CalculateString_Binary()
  
  Protected NumAStr.s ;NumerAsString
  
  Repeat
    Select *CalcStr_CChar\C
      Case '0','1'
        NumAStr+Chr(*CalcStr_CChar\C)
      Case 'o','O'
        NumAStr+"0"
      Case 'i','I'
        NumAStr+"1"
      Default                         ;Unbekanntes Zeichen.
        *CalcStr_CChar-1              ;Lesezeiger vor dem unbekannten Zeichen setzen.
        ProcedureReturn DBin(NumAStr) ;Gelesener String als Binärzahl zurückgeben.
    EndSelect
    *CalcStr_CChar+1
  ForEver
  ProcedureReturn 0
EndProcedure
Procedure.d CalcStr_Hex()
  ;CalculateString_Hexadecimal()
  
  Protected NumAStr.s ;NumerAsString
  
  Repeat
    Select *CalcStr_CChar\C
      Case '0' To '9','A' To 'F'
        NumAStr+Chr(*CalcStr_CChar\C)
      Case 'a' To 'f'
        NumAStr+UCase(Chr(*CalcStr_CChar\C))
      Default                         ;Unbekanntes Zeichen.
        *CalcStr_CChar-1              ;Lesezeiger vor dem unbekannten Zeichen setzen.
        ProcedureReturn DHex(NumAStr) ;Den gelesenen String als Hexadezimalzahl zurückgeben.
    EndSelect
    *CalcStr_CChar+1
  ForEver
  ProcedureReturn 0
EndProcedure
Procedure.d CalcStr_Func()
  ;CalculateString_Function()
  
  #CalcStr_Func_InvFunc=0 ;~InvaildFunction
  #CalcStr_Func_InvChar=1 ;~InvaildCharacter
  
  Protected Func.s ;Function
  Protected Num.d  ;Number
  Protected *Args  ;Arguments
  Protected *Ptr   ;Pointer
  Protected F.b    ;Found
  
  Repeat
    Select *CalcStr_CChar\C
      Case 'A' To 'Z','a' To 'z'   ;Buchstabe wird in den Name eingelesen.
        Func+Chr(*CalcStr_CChar\C) ;Name der Funktion verlängern.
      Case '('                     ;Klammer wurde nach dem Name gefunden.
        Func=LCase(Func)           ;Name der Funktion in Kleinbuchstaben umwandeln.
        Select Func
          Case "abs"
            *Args=CalcStr_Args(1)
            Num=Abs(PeekD(*Args))
          Case "pow"
            *Args=CalcStr_Args(2)
            Num=Pow(PeekD(*Args),PeekD(*Args+SizeOf(Double)))
          Case "sqr"
            *Args=CalcStr_Args(2)
            Num=Pow(PeekD(*Args),1/PeekD(*Args+SizeOf(Double)))
          Case "arc","a"
            *Args=CalcStr_Args(1)
            Num=PeekD(*Args)*360/(2*#PI)
          Case "rad","r"
            *Args=CalcStr_Args(1)
            Num=PeekD(*Args)*2*#PI/360
          Case "sin"
            *Args=CalcStr_Args(1)
            Num=Sin(PeekD(*Args))
          Case "cos"
            *Args=CalcStr_Args(1)
            Num=Cos(PeekD(*Args))
          Case "tan"
            *Args=CalcStr_Args(1)
            Num=Tan(PeekD(*Args))
          Case "asin"
            *Args=CalcStr_Args(1)
            Num=ASin(PeekD(*Args))
          Case "acos"
            *Args=CalcStr_Args(1)
            Num=ACos(PeekD(*Args))
          Case "atan"
            *Args=CalcStr_Args(1)
            Num=ATan(PeekD(*Args))
            
          Default                                   ;Eigene oder ungültige Funktion.
            If *CalcStr_FuncA                       ;Startzeiger für Funktionen wurde vom Anwender gesetzt...
              *Ptr=*CalcStr_FuncA                   ;Lesezeiger auf den Start setzen.
              While *Ptr<*CalcStr_FuncA_End         ;Solange der Lesezeiger != dem Endzeiger ist...
                If Func=PeekS(*Ptr)                 ;Funktionsnamen auslesen und Checken...
                  *Ptr+Len(PeekS(*Ptr))+1           ;Mit dem Lesezeiger über den Namen springen.
                  If PeekL(*Ptr)                    ;Falls Parameter gebraucht werden... (!=0)
                    *Args=CalcStr_Args(PeekL(*Ptr)) ;Zeiger auf Array der Parameter in "*Args" abspeichern.
                  EndIf
                  *Ptr+SizeOf(Long)                    ;Lesezeiger über die Anzahl der Parameter springen lassen.
                  CallFunctionFast(PeekL(*Ptr),*Args,@Num)     ;Funktion aufrufen lassen.
                  F=#True                           ;Gefunden.
                  Break
                Else
                  *Ptr+Len(PeekS(*Ptr))+1
                  *Ptr+SizeOf(Long)*2
                EndIf
              Wend
            EndIf
            
            If F=#False ;Funktion nicht gefunden.
              CompilerIf #PB_Compiler_Debugger
                Debug "CalcStr_Func() error: "+Func+" is an invaild function!"
                Beep_(250,250)
                End
              CompilerEndIf
            EndIf
        EndSelect
        If *Args : FreeMemory(*Args) : EndIf
        ProcedureReturn Num
      Default
        CompilerIf #PB_Compiler_Debugger
          Debug "CalcStr_Func() error: "+Str(*CalcStr_CChar\C)+" code is an invaild function character."
          Beep_(250,250)
          End
        CompilerEndIf
    EndSelect
    *CalcStr_CChar+1
  ForEver
  ProcedureReturn 0
EndProcedure
Procedure.l CalcStr_Args(ArgAm.l)
  ;CalculateString_Arguments(ArgumentAmount)
  
  Protected *Args ;Arguments
  Protected I.l   ;Index
  
  *Args=AllocateMemory(SizeOf(Double)*ArgAm) ;Array für Argumente erstellen.
  *CalcStr_CChar+1                        ;Mit dem Lesezeiger über "Klammer auf" springen.
  
  For I=1 To ArgAm                           ;Für jedes Argument.
    PokeD(*Args+SizeOf(Double)*(I-1),CalcStr()) ;Wert des Arguments berechnen und in die Array speichern.
  Next
  ProcedureReturn *Args
EndProcedure
Procedure.d CalcStr_Const()
  ;CalculateString_Constant()
  
  #CalcStr_InvConst=0 ;~InvaildConstant
  
  Protected Const.s ;Constant
  Protected Num.d   ;Number
  Protected *Ptr    ;Pointer
  Protected F.b     ;Found
  
  Repeat
    Select *CalcStr_CChar\C
      Case 'A' To 'Z','a' To 'z'    ;Buchstabe wird in den Name eingelesen.
        Const+Chr(*CalcStr_CChar\C) ;Name der Konstante verlängern.
      Default                       ;Ein unbekanntes Zeichen.
        *CalcStr_CChar-1            ;Vor dem Unbekannten Zeichen setzen.
        Const=LCase(Const)          ;Name der Konstante in Kleinbuchstaben umwandeln.
        Select Const
          Case "pi"
            Num=3.1415926535897931 ;Entspricht dem Double-Format.
          Case "eul","e"
            Num=2.7182818284590455 ;Entspricht dem Double-Format.
          Case "ang","a"           ;"Ang" entspricht "Angle"
            Num=57.2957795131
          Case "rad","r"
            Num=0.0174532925
          Default                             ;Eigene oder ungültige Konstante.
            If *CalcStr_ConstA                ;Ist dieser Zeiger vom Anwender gesetzt worden...
              *Ptr=*CalcStr_ConstA            ;Lesezeiger auf den Start aller Konstanten setzen.
              While *Ptr<>*CalcStr_ConstA_End ;Lesezeiger ist != dem Endzeiger.
                If Const=PeekS(*Ptr)          ;Name der Konstante auslesen und Checken...
                  *Ptr+Len(PeekS(*Ptr))+1     ;Lesezeiger über den Namen springen lassen.
                  Num=PeekD(*Ptr)             ;Konstante Nummer auslesen.
                  F=#True                     ;Gefunden.
                  Break                       ;Suchschleife beenden.
                Else
                  *Ptr+Len(PeekS(*Ptr))+1     ;Lesezeiger über den falschen Name springen lassen.
                  *Ptr+SizeOf(Double)            ;Lesezeiger über die unbrauchbare Zahl springen lassen.
                Endif
              Wend
            EndIf
            
            If F=#False ;Nicht gefunden.
              CompilerIf #PB_Compiler_Debugger
                Debug "CalcStr_Const() error: "+Const+" is an invaild constant!"
                Beep_(250,250)
                End
              CompilerEndIf
            EndIf
        EndSelect
        ProcedureReturn Num
    EndSelect
    *CalcStr_CChar+1
  ForEver
  ProcedureReturn 0
EndProcedure



;/------------------------------------------\
;| Examples                                 |
;\------------------------------------------/

;{ View
;   ;Dieses Beispiel zeigt einige möglichen Terme und erlaubt
;   ;selbst welche zu schreiben und ausrechnen zu lassen.
; 
;   OpenConsole()
;   ConsoleTitle("Term calculator")
;   
;   ConsoleColor(15,0) : PrintN("Example terms with their results:") : ConsoleColor(7,0)
;   PrintN("12.5 + 20.5      = "+StrD(CalcStr("12.5+20.5")))           ;Normale Dezimalrechnung.
;   PrintN("60 + 2 * 5       = "+StrD(CalcStr("60+2*5")))              ;Punkt vor Strich wird beachtet.
;   PrintN("sin(45"+Chr(248)+")         = "+StrD(CalcStr("Sin(45°)"))) ;Unterstützung von Funktionen.
;   PrintN("%10 | %01        = "+StrD(CalcStr("%10|%01")))             ;Binäre Berechnung.
;   PrintN("{20,10}          = "+StrD(CalcStr("{20,10}")))             ;Berechnung des Durchschnitts.
;   PrintN("#PI*2            = "+StrD(CalcStr("#PI*2")))               ;Ersetzt Konstanten.
;   
;   PrintN("")
;   ConsoleColor(15,0) : PrintN("You can also use functions:") : ConsoleColor(7,0)
;   PrintN("2 * x + 3 ^ (x / 2) + 5")
;   For X=1 To 5
;     CalcStr_Var('X'-65)=X
;     PrintN("x = "+Str(X)+"     : "+StrD(CalcStr("2*x+3^(x/2)+5")))
;   Next
;   PrintN("the variables can be the letters from a to z.")
;   
;   PrintN("")
;   ConsoleColor(15,0) : PrintN("Your own terms:") : ConsoleColor(7,0)
;   PrintN("1 - default")
;   PrintN("2 - function")
;   PrintN("Enter nothing to return.")
;   PrintN("")
;   
;   Repeat
;     ConsoleColor(12,0) : Print("Mode: ") : ConsoleColor(7,0)
;     Select Input()
;       Case "1"
;         Repeat
;           ConsoleColor(12,0) : Print("Term: ") : ConsoleColor(7,0)
;           Term$=Input()
;           If Term$
;             ConsoleColor(14,0) : Print("Result: ") : ConsoleColor(7,0)
;             PrintN(StrD(CalcStr(Term$)))
;           EndIf
;         Until Term$=""
;       Case "2"
;         ConsoleColor(12,0) : Print("Function: ") : ConsoleColor(7,0)
;         Function$=Input()
;         Repeat
;           ConsoleColor(12,0) : Print("x: ") : ConsoleColor(7,0)
;           X$=Input()
;           If X$
;             CalcStr_Var('X'-65)=CalcStr(X$) ;Setzt die Variable x auf den eingegebenen Wert.
;             ConsoleColor(14,0) : Print("Result: ") : ConsoleColor(7,0)
;             PrintN(StrD(CalcStr(Function$)))
;           EndIf
;         Until X$=""
;       Default
;         Break
;     EndSelect
;   ForEver
;   
;   CloseConsole()
;   End
;}
;{ Extended
;   ;In diesem Beispiel werden eigene Funktionen angewendet.
;   ;Alle Argumente werden in den Zeiger 'Args' abgespeichert.
;   ;Diese können dann mit 'PokeD' ausgelesen werden.
;   
;   Global Mem.d
;   
;   Procedure.l CalcStr_Func_Rand(*Args,*Num)
;     ;CalculateString_Function_Random(Arguments,Number)
;     
;     PokeD(*Num,Random(PeekD(*Args)))
;   EndProcedure
;   Procedure.l CalcStr_Func_Mem(*Args,*Num)
;     ;CalculateString_Function_Memory(Arguments,Number)
;     
;     PokeD(*Num,Mem)
;   EndProcedure
;   
;   DataSection
;     Funcs: ;Functions
;       Data.s "rand"               ;Name einer Funktion. Immer klein schreiben!
;       Data.l 1                    ;Anzahl der Argumente.
;       Data.l @CalcStr_Func_Rand() ;Zeiger auf die wahre Funktion.
;       
;       Data.s "mem"
;       Data.l 0
;       Data.l @CalcStr_Func_Mem()
;     EndFuncs: ;EndOfFunctions
;   EndDataSection
;   
;   *CalcStr_FuncA=?Funcs        ;Setzt den Anfangslabel für die eigenen Funktionen.
;   *CalcStr_FuncA_End=?EndFuncs ;Setzt das Endlabel für die eigenen Funktionen.
;   
;   
;   
;   OpenConsole()
;   ConsoleTitle("Term calculator")
;   
;   PrintN("1 - default")
;   PrintN("2 - function")
;   PrintN("Enter nothing to return.")
;   PrintN("")
;   
;   Repeat
;     ConsoleColor(12,0) : Print("Mode: ") : ConsoleColor(7,0)
;     Select Input()
;       Case "1"
;         Repeat
;           ConsoleColor(12,0) : Print("Term: ") : ConsoleColor(7,0)
;           Term$=Input()
;           If Term$
;             ConsoleColor(14,0) : Print("Result: ") : ConsoleColor(7,0)
;             Mem=CalcStr(Term$) ;Das Ergebnis wird in 'Mem' abgespeichert und kann mit "MEM()" wieder erhalten werden.
;             PrintN(StrD(Mem))
;           EndIf
;         Until Term$=""
;       Case "2"
;         ConsoleColor(12,0) : Print("Function: ") : ConsoleColor(7,0)
;         Function$=Input()
;         Repeat
;           ConsoleColor(12,0) : Print("x: ") : ConsoleColor(7,0)
;           X$=Input()
;           If X$
;             CalcStr_Var('X'-65)=CalcStr(X$) ;Setzt die Variable x auf den eingegebenen Wert.
;             ConsoleColor(14,0) : Print("Result: ") : ConsoleColor(7,0)
;             PrintN(StrD(CalcStr(Function$)))
;           EndIf
;         Until X$=""
;       Default
;         Break
;     EndSelect
;   ForEver
;   
;   CloseConsole()
;   End
;}



CompilerEndIf
PB 4.61 | Windows Vista - 32Bit
Homepage

"Wahrlich es ist nicht das Wissen, sondern das Lernen, nicht das Besitzen sondern das Erwerben, nicht das Dasein, sondern das Hinkommen, was den grössten Genuss gewährt." - Carl Friedrich Gauß
Little John

Beitrag von Little John »

Josef Sniatecki hat geschrieben:Ich habe nocheinmal meinen Code bearbeitet und einige
Kommentare hinzugefügt. Nun wird nur eine Ebene für diesen
Term genutzt:
CalcStr("1+1+1+1+1+1+1+1+1+1+1+1+1")

Der Code funktioniert perfekt. Es passieren keine Fehler mehr,
wenn zu viele Operatoren vorkommen.

Code: Alles auswählen

Debug CalcStr("7/0")
liefert bei mir

Code: Alles auswählen

1.#INF
Das ist kein Fehler in Deinem Code, aber ich würde die Division durch 0 abfangen und selbst behandeln.

Code: Alles auswählen

Debug CalcStr("1+(2+(3+(4+(5+(6+(7+(8+(9-(10+(11+(7)))))))))))")
liefert bei mir

Code: Alles auswählen

-1.#IND
Sollte 17 ergeben.

Gruß, Little John
Benutzeravatar
Josef Sniatecki
Beiträge: 657
Registriert: 02.06.2008 21:29
Kontaktdaten:

Beitrag von Josef Sniatecki »

OK. Das mit der Division durch 0 ist leicht zu beheben. Doch das mit den
vielen Klammern nicht. "CalcStr" muss einfach eine neue Ebene bei jeder
Klammer öffnen, sonst wird die Priorität nicht beachtet. Ich weiß jetzt nicht,
wie ich dieses Problem lösen könnte. Natürlich wäre es möglich einen eigenen Stack zu machen, in dem alle Daten übereinander gestapelt werden, und am Ende einer Klammer wieder "gepoppt" :wink: (PUSH,POP) werden.
Doch irgendwie finde ich das ein bisschen pnprofessionell. Oder bin ich der
einzige der das so sieht?
PB 4.61 | Windows Vista - 32Bit
Homepage

"Wahrlich es ist nicht das Wissen, sondern das Lernen, nicht das Besitzen sondern das Erwerben, nicht das Dasein, sondern das Hinkommen, was den grössten Genuss gewährt." - Carl Friedrich Gauß
Benutzeravatar
AND51
Beiträge: 5220
Registriert: 01.10.2005 13:15

Beitrag von AND51 »

Wollte auch schon immer mal sowas coden.

Ich finde die Idee mit der Rekusrion sehr elegant. Es ist naheliegend, für jkeden neuen Teilterm die Prozedur erneut aufzurufen.

Was haltet ihr denn von der Idee, alle Teilterme z. B. per RegExp zu separieren. Diese lägen dann in einem Array vor, welches man durchlaufen könnte.
PB 4.30

Code: Alles auswählen

Macro Happy
 ;-)
EndMacro

Happy End
Antworten