Umgekehrte Polnische Notation

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.
Benutzeravatar
remi_meier
Beiträge: 1078
Registriert: 29.08.2004 20:11
Wohnort: Schweiz

Umgekehrte Polnische Notation

Beitrag von remi_meier »

oder Infix -> Postfix
http://de.wikipedia.org/wiki/Umgekehrte ... e_Notation

Irgendwo in einem anderen Thread wurde das mal angesprochen.
Da ich es schon in meinem Compiler eingebaut hatte, hab ich es jetzt für
euch wieder rausgelesen und aufbereitet (v.a. vereinfacht).
Ich garantiere nicht für Fehlerlosigkeit, aber die erweiterte Version tuts für
mich bis jetzt fehlerlos :)

Der Hauptteil des Codes (bis Zeile 150) ist eigentlich irrelevant, er dient
nur zum aufteilen des Strings in Tokens.
Dann kommen etwa 100 Zeilen, die den Hauptalgorithmus ausmachen!

Code: Alles auswählen


Structure TOKENS
  s.s     ; Inhalt
  Typ.s   ; "op", "int", "par"
EndStructure

NewList Tokens.TOKENS()

Procedure SCIsOperator(s.s)
  Protected ReturnValue.l
  
  ;/ überprüft ob es ein bekannter Operator ist
  
  ReturnValue = #False
  
  If Len(s) = 1
    Select PeekB(@s)
      Case '+'
        ReturnValue = #True
      Case '-'
        ReturnValue = #True
      Case '*'
        ReturnValue = #True
      Case '/'
        ReturnValue = #True
    EndSelect
  EndIf
  
  ProcedureReturn ReturnValue
EndProcedure

Procedure SCIsKlammer(s.s)
  Protected *p.BYTE, ReturnValue.l
  
  ;/ Ist es eine Klammer?
  
  If Len(s) = 1
    *p = @s
    
    ReturnValue = #False
    
    If *p\b = '(' Or *p\b = ')'
      ReturnValue = #True
    EndIf
  EndIf
  
  ProcedureReturn ReturnValue
EndProcedure

Procedure SCIsInteger(s.s)
  Protected *p.BYTE, ReturnValue.l
  *p = @s
  
  ;/ Ist es eine Zahl
  
  ReturnValue = #False
  
  If *p\b <> 0
    While *p\b <> 0
      If (*p\b < 48 Or *p\b > 57)
        Break
      EndIf
      
      *p + 1
    Wend
    
    If *p\b = 0 And *p\b <> @s + 1
      ReturnValue = #True
    EndIf
  EndIf
  
  ProcedureReturn ReturnValue 
EndProcedure

Procedure CleanSCOutput()
  ;/ Fügt mehrziffrige Zahlen zusammen
  ;/ Die Zahl 23 ist im Moment noch in 2 Tokens unterteilt
  
  Protected WasInt.l, LastInt.s
  
  ForEach Tokens()
    If Tokens()\Typ = "int"
      If WasInt = #True
        LastInt = Tokens()\s
        DeleteElement(Tokens())
        Tokens()\s + LastInt
      EndIf
      WasInt = #True
    Else
      WasInt = #False
    EndIf
  Next
EndProcedure

Procedure Scanner(String.s)
  Protected Len.l, Start.l, z.l, s.s
  
  ;/ Zerlegt den String in die einzelnen Bestandtteile und
  ;/ schreibt sie in eine Liste
  
  Len = Len(String)
  Start = 1
  ; Den ganzen Ausdruck einklammern (für umgek. Polnische Notation nötig)
  AddElement(Tokens())
  Tokens()\s   = "("
  Tokens()\Typ = "par"
  For z = 1 To Len + 1
    s = ReplaceString( Mid(String, Start, z - Start), Chr(9), " ")
    
    If s = " "
      s = ""
      Start + 1
      
    ElseIf s = ""
      Continue
      
    ElseIf SCIsOperator(s)
      AddElement(Tokens())
      Tokens()\Typ    = "op"
      Tokens()\s      = s
      Start + 1
      s = ""
      
    ElseIf SCIsInteger(s)
      AddElement(Tokens())
      Tokens()\Typ    = "int"
      Tokens()\s      = s
      Start + Len(s)
      s = ""
      
    ElseIf SCIsKlammer(s)
      AddElement(Tokens())
      Tokens()\Typ    = "par"
      Tokens()\s      = s
      Start + 1
      s = ""
      
    EndIf
  Next
  
  AddElement(Tokens())
  Tokens()\s   = ")"
  Tokens()\Typ = "par"
  
  CleanSCOutput()
EndProcedure



;- Hauptteil zur umgekehrten Polnischen Notation
NewList Symbols.TOKENS()
NewList PStack.TOKENS()

Procedure GetPrecedence(*Token.TOKENS)
  Protected ReturnValue.l
  
  ;/ Gibt die Priorität der Operatoren und Klammern zurück
  
  Select *Token\Typ
    Case "op"
      Select *Token\s
        Case "+"
          ReturnValue = 3
        Case "-"
          ReturnValue = 3
        Case "*"
          ReturnValue = 4
        Case "/"
          ReturnValue = 4
      EndSelect
    Case "par"
      ReturnValue = 5
  EndSelect
  
  ProcedureReturn ReturnValue
EndProcedure

Procedure PParseExpression()
  
  ;/ Hauptteil: Kehrt die Tokens gemäss Punkt-vor-Strich-Regel um.
  ;/ anstatt 3 + 4 steht dann 3 4 +
  ;/ oder 3 * (4 + 5) -> 3 4 5 + *
  
  ClearList(Symbols())
  ClearList(PStack())
  
  ForEach Tokens()
    
    If Tokens()\s = "("
      ; Lege auf Stack
      AddElement(PStack())
      PStack()\s      = Tokens()\s
      PStack()\Typ    = Tokens()\Typ
      
    ElseIf Tokens()\s = ")"
      ; Nehme so lange vom Stack bis wir bem Anfang
      ; des Klammernblockes sind
      While PStack()\s <> "("
        AddElement(Symbols())
        Symbols()\s      = PStack()\s
        Symbols()\Typ    = PStack()\Typ
        DeleteElement(PStack())
      Wend
      
      If PStack()\s = "("
        DeleteElement(PStack())
      EndIf
      
    ElseIf Tokens()\Typ = "op"
      ; Wenn es ein Operator ist
      Repeat
        
        ; Wenn entweder Stack leer, auf Stack ne Klammer-Auf '('
        ; oder wenn das Zeichen auf dem Stack eine niedrigere 
        ; Priorität besitzt als das aktuelle
        If CountList(PStack()) = 0 Or PStack()\s = "(" Or GetPrecedence(@PStack()) < GetPrecedence(@Tokens())
          ; Lege aktuelles Element auf Stack und breche Schleife ab
          AddElement(PStack())
          PStack()\s      = Tokens()\s
          PStack()\Typ    = Tokens()\Typ
          Break
          
        Else
          ; Schreibe es in Symbols rein
          AddElement(Symbols())
          Symbols()\s      = PStack()\s
          Symbols()\Typ    = PStack()\Typ
          DeleteElement(PStack())
        EndIf
      ForEver
      
    Else
      ; Wenns ein Integer ist gleich in Symbols reinschreiben
      AddElement(Symbols())
      Symbols()\s      = Tokens()\s
      Symbols()\Typ    = Tokens()\Typ
      
    EndIf
    
  Next
  
EndProcedure



Procedure OutputTokens()
  ForEach Tokens()
    Debug Tokens()\s
  Next
EndProcedure

Procedure OutputSymbols()
  ForEach Symbols()
    Debug Symbols()\s
  Next
EndProcedure



String.s = "3 + 4 * (5 + 23)"
Scanner(String)
Debug "##### Scanned #####"
OutputTokens()
PParseExpression()
Debug "##### Reversed #####"
OutputSymbols()
Wer Fragen hat, stelle sie einfach!

greetz
Remi :)

Übrigens zum etwa 5. Male will ich wieder ein Theorie&Konzeptions-Forum!
traumatic
Beiträge: 478
Registriert: 27.11.2004 15:42

Re: Umgekehrte Polnische Notation

Beitrag von traumatic »

:allright:

...jetzt noch floats ;)
Benutzeravatar
remi_meier
Beiträge: 1078
Registriert: 29.08.2004 20:11
Wohnort: Schweiz

Beitrag von remi_meier »

aaaaaber sicher :wink:

Code: Alles auswählen

Structure TOKENS 
  s.s     ; Inhalt 
  Typ.s   ; "op", "int", "par" 
EndStructure 

NewList Tokens.TOKENS() 

Procedure SCIsOperator(s.s) 
  Protected ReturnValue.l 
  
  ;/ überprüft ob es ein bekannter Operator ist 
  
  ReturnValue = #False 
  
  If Len(s) = 1 
    Select PeekB(@s) 
      Case '+' 
        ReturnValue = #True 
      Case '-' 
        ReturnValue = #True 
      Case '*' 
        ReturnValue = #True 
      Case '/' 
        ReturnValue = #True 
      Case '.' 
        ReturnValue = #True 
    EndSelect 
  EndIf 
  
  ProcedureReturn ReturnValue 
EndProcedure 

Procedure SCIsKlammer(s.s) 
  Protected *p.BYTE, ReturnValue.l 
  
  ;/ Ist es eine Klammer? 
  
  If Len(s) = 1 
    *p = @s 
    
    ReturnValue = #False 
    
    If *p\b = '(' Or *p\b = ')' 
      ReturnValue = #True 
    EndIf 
  EndIf 
  
  ProcedureReturn ReturnValue 
EndProcedure 

Procedure SCIsInteger(s.s) 
  Protected *p.BYTE, ReturnValue.l 
  *p = @s 
  
  ;/ Ist es eine Zahl 
  
  ReturnValue = #False 
  
  If *p\b <> 0 
    While *p\b <> 0 
      If (*p\b < 48 Or *p\b > 57) 
        Break 
      EndIf 
      
      *p + 1 
    Wend 
    
    If *p\b = 0 And *p <> @s
      ReturnValue = #True 
    EndIf 
  EndIf 
  
  ProcedureReturn ReturnValue 
EndProcedure 

Procedure CleanSCOutput() 
  ;/ Fügt mehrziffrige Zahlen zusammen 
  ;/ Die Zahl 23 ist im Moment noch in 2 Tokens unterteilt 
  
  Protected WasInt.l, LastInt.s 
  
  ForEach Tokens() 
    If Tokens()\Typ = "int" Or Tokens()\s = "." 
      If WasInt = #True 
        LastInt = Tokens()\s 
        DeleteElement(Tokens()) 
        Tokens()\s + LastInt 
      EndIf 
      WasInt = #True 
    Else 
      WasInt = #False 
    EndIf 
  Next 
EndProcedure 

Procedure Scanner(String.s) 
  Protected Len.l, Start.l, z.l, s.s 
  
  ;/ Zerlegt den String in die einzelnen Bestandtteile und 
  ;/ schreibt sie in eine Liste 
  
  Len = Len(String) 
  Start = 1 
  ; Den ganzen Ausdruck einklammern (für umgek. Polnische Notation nötig) 
  AddElement(Tokens()) 
  Tokens()\s   = "(" 
  Tokens()\Typ = "par" 
  For z = 1 To Len + 1 
    s = ReplaceString( Mid(String, Start, z - Start), Chr(9), " ") 
    
    Debug s
    If s = " " 
      s = "" 
      Start + 1 
      
    ElseIf s = "" 
      Continue 
      
    ElseIf SCIsOperator(s) 
      AddElement(Tokens()) 
      Tokens()\Typ    = "op" 
      Tokens()\s      = s 
      Start + 1 
      s = "" 
      
    ElseIf SCIsInteger(s) 
      AddElement(Tokens()) 
      Tokens()\Typ    = "int" 
      Tokens()\s      = s 
      Start + Len(s) 
      s = "" 
      
    ElseIf SCIsKlammer(s) 
      AddElement(Tokens()) 
      Tokens()\Typ    = "par" 
      Tokens()\s      = s 
      Start + 1 
      s = "" 
      
    EndIf 
  Next 
  
  AddElement(Tokens()) 
  Tokens()\s   = ")" 
  Tokens()\Typ = "par" 
  
  CleanSCOutput() 
EndProcedure 



;- Hauptteil zur umgekehrten Polnischen Notation 
NewList Symbols.TOKENS() 
NewList PStack.TOKENS() 

Procedure GetPrecedence(*Token.TOKENS) 
  Protected ReturnValue.l 
  
  ;/ Gibt die Priorität der Operatoren und Klammern zurück 
  
  Select *Token\Typ 
    Case "op" 
      Select *Token\s 
        Case "+" 
          ReturnValue = 3 
        Case "-" 
          ReturnValue = 3 
        Case "*" 
          ReturnValue = 4 
        Case "/" 
          ReturnValue = 4 
      EndSelect 
    Case "par" 
      ReturnValue = 5 
  EndSelect 
  
  ProcedureReturn ReturnValue 
EndProcedure 

Procedure PParseExpression() 
  
  ;/ Hauptteil: Kehrt die Tokens gemäss Punkt-vor-Strich-Regel um. 
  ;/ anstatt 3 + 4 steht dann 3 4 + 
  ;/ oder 3 * (4 + 5) -> 3 4 5 + * 
  
  ClearList(Symbols()) 
  ClearList(PStack()) 
  
  ForEach Tokens() 
    
    If Tokens()\s = "(" 
      ; Lege auf Stack 
      AddElement(PStack()) 
      PStack()\s      = Tokens()\s 
      PStack()\Typ    = Tokens()\Typ 
      
    ElseIf Tokens()\s = ")" 
      ; Nehme so lange vom Stack bis wir bem Anfang 
      ; des Klammernblockes sind 
      While PStack()\s <> "(" 
        AddElement(Symbols()) 
        Symbols()\s      = PStack()\s 
        Symbols()\Typ    = PStack()\Typ 
        DeleteElement(PStack()) 
      Wend 
      
      If PStack()\s = "(" 
        DeleteElement(PStack()) 
      EndIf 
      
    ElseIf Tokens()\Typ = "op" 
      ; Wenn es ein Operator ist 
      Repeat 
        
        ; Wenn entweder Stack leer, auf Stack ne Klammer-Auf '(' 
        ; oder wenn das Zeichen auf dem Stack eine niedrigere 
        ; Priorität besitzt als das aktuelle 
        If CountList(PStack()) = 0 Or PStack()\s = "(" Or GetPrecedence(@PStack()) < GetPrecedence(@Tokens()) 
          ; Lege aktuelles Element auf Stack und breche Schleife ab 
          AddElement(PStack()) 
          PStack()\s      = Tokens()\s 
          PStack()\Typ    = Tokens()\Typ 
          Break 
          
        Else 
          ; Schreibe es in Symbols rein 
          AddElement(Symbols()) 
          Symbols()\s      = PStack()\s 
          Symbols()\Typ    = PStack()\Typ 
          DeleteElement(PStack()) 
        EndIf 
      ForEver 
      
    Else 
      ; Wenns ein Integer ist gleich in Symbols reinschreiben 
      AddElement(Symbols()) 
      Symbols()\s      = Tokens()\s 
      Symbols()\Typ    = Tokens()\Typ 
      
    EndIf 
    
  Next 
  
EndProcedure 



Procedure OutputTokens() 
  ForEach Tokens() 
    Debug Tokens()\s 
  Next 
EndProcedure 

Procedure OutputSymbols() 
  ForEach Symbols() 
    Debug Symbols()\s 
  Next 
EndProcedure 



String.s = "3.5+52 * 7";"3.0 + 4.0 * (5 + 23.0)" 
Scanner(String) 
Debug "##### Scanned #####" 
OutputTokens() 
PParseExpression() 
Debug "##### Reversed #####" 
OutputSymbols()
Zuletzt geändert von remi_meier am 04.08.2005 11:09, insgesamt 2-mal geändert.
Benutzeravatar
remi_meier
Beiträge: 1078
Registriert: 29.08.2004 20:11
Wohnort: Schweiz

Beitrag von remi_meier »

Ach, und den Rechner zu beiden Versionen dazu:

Code: Alles auswählen

;/ Und der Rechner dazu!

NewList CStack.f()

Procedure.f CalcExpr()
  
  ClearList(CStack())
  ForEach Symbols()
    If Symbols()\Typ = "int"
      AddElement(CStack())
      CStack() = ValF(Symbols()\s)
      
    ElseIf Symbols()\Typ = "op"
      Res.f = 0
      op2.f = CStack()
      DeleteElement(CStack())
      op1.f = CStack()
      Select Symbols()\s
        Case "+"
          Res = op1 + op2
        Case "-"
          Res = op1 - op2
        Case "*"
          Res = op1 * op2
        Case "/"
          Res = op1 / op2
      EndSelect
      
      CStack() = Res
      
    EndIf
    
  Next
  
  ProcedureReturn CStack()
EndProcedure
Edit
Geht nun auch für Float-Version
Zuletzt geändert von remi_meier am 06.05.2005 16:16, insgesamt 2-mal geändert.
traumatic
Beiträge: 478
Registriert: 27.11.2004 15:42

Beitrag von traumatic »

remi_meier hat geschrieben:aaaaaber sicher :wink:
Geil! :twisted:
Antworten