home Funktionale Programmierung: Ausdrucksauswertung mit Zuweisungen und Schleifen Prof. Dr. Uwe Schmidt FH Wedel

Ausdrucksauswertung mit Zuweisungen und Schleifen

weiter

weiter

Aufgabe

Ausdrucksauswertung
und Monaden für Ausdrücke mit Variablen
Gegeben
Eine einfache Datenstruktur für arithmetische Ausdrücke mit ganzen Zahlen und Variable wie in der vorigen Aufgabe über Monaden und ein Interpretierer in monadischer Form mit Environment und Fehlerbehandlung.
weiter
Syntaktische Bereiche
data Expr  = Const  Int
           | Var    Id
           | Let    Id    Expr Expr
           | Binary BinOp Expr Expr
             deriving (Show)
 
data BinOp = Add | Sub | Mul | Div | Mod
             deriving (Eq, Show)
 
type Id    = String
Semantische Bereiche
Das Resultat war eine Funktion von einem Environment auf einen Wert oder eine Fehlermeldung.
 
data ResVal a
           = Val { val :: a }
           | Exc { exc :: String }
             deriving (Show)
 
newtype Result a
           = Res { unRes :: Env -> ResVal a }
 
type Env   = [(Id, Int)]
Veränderung
der Ausdruckssprache
 
Eine andere Richtung der Erweiterung um Variablen ist die, mit Programmvariablen und Zuweisungen zu arbeiten. In diesem Fall sind natürlich auch ein Sequenz-Operator und Schleifen sinnvoll.
Neue syntaktische Bereiche
data Expr  = Const  Int
           | Var    Id
           | Assign Id    Expr
           | If     Expr  Expr Expr
           | While  Expr  Expr
           | Binary BinOp Expr Expr
             deriving (Show)
 
data BinOp = ... | Seq
             deriving (Eq, Show)
 
type Id    = String
Aufgabe
Das vorige Beispiel soll so verändert werden, dass nicht mehr auf einem Environment gearbeitet wird (und nur gelesen wird), sondern auf einem jederzeit veränderbaren Zustand. Eine Operation kann also nicht nur einen Wert berechnen, sondern auch einen neuen Zustand. Es muss die Bedeutungsfunktion so abgeändert werden, dass sie diese beiden Werte als Resultat liefert.
Neue semantische Bereiche
data ResVal a
      = Val { val :: a }
      | Exc { exc :: String }
        deriving (Show)
 
newtype Result a
      = Res { unRes :: VState -> (ResVal a, VState) }
 
type VState = [(Id, Int)]
MonadState
Für die Verwaltung so eines Zustands gibt es wieder eine Typklasse MonadState, die hier sinnvollerweise verwendet werden sollte.
Definition
der Monaden-Klassen
 
instance Monad Result where
  return x      = Res $ ...
  (Res f) >>= g = Res $ ...
 
instance MonadError String Result where
  throwError s  = Res $ ...
 
instance MonadState VState Result where
  get       = Res $ ...
  put new   = Res $ ...
Interpretierer-
Veränderungen und Erweiterungen sind an den folgenden Stellen nötig:
 
eval (Var id)
  = do ...
 
eval (Assign id e1)
  = do ...
 
eval (If c t e)
    = do ...
 
eval st@(While c b)
    = do ...
 
...
 
mft :: [(BinOp, MF)]
mft
  = [ ...
    , (Seq, ...)
    ]
 
evalSt :: Expr -> VState -> (ResVal Int, VState)
evalSt = unRes . eval
Ausdrücke
zum Testen
 
e1 = Binary Mul (Binary Add (Const 2)
                            (Const 4)
                )
                (Const 7)
e2 = Binary Div (Const 1) (Const 0)
e3 = Binary Mod (Const 1) (Const 0)
e4 = Var "x"
e5 = Binary Mul (Binary Add e4
                            (Const 1)
                ) e4
 
e4' = Binary Seq (Assign "x" (Const 42)) e4
e5' = Binary Seq (Assign "x" (Const 6))  e5
 
e6' = foldr1 (Binary Seq) $
      [ Assign "x" (Const 42)
      , Assign "y" (Const 13)
      , Assign "t" (Var "x")
      , Assign "x" (Var "y")
      , Assign "y" (Var "t")
      ]
 
e7' = foldr1 (Binary Seq) $
      [ e6'
      , Assign "r" (Binary Sub (Var "x") (Var "y"))
      , If (Var "r") (Var "r") (Var "x")
      ]
 
e8  = Binary Seq e4' $
      While (Var "x")
            (Assign "x" (Binary Sub (Var "x")
                                    (Const 1)))
Erweiterung
um Ein- und Ausgabe
 
Möchte man die Sprache um Ein- und Ausgabe erweitern, beispielhaft um das Einlesen und Ausgeben einer Zahl, so wird man den Resultat-Typ erweitern, so dass er in der IO-Monade läuft. Außerdem wird man normale IO-Aktionen in diese Monade liften müssen. Hierfür gibt es wieder eine vorgefertigte Schnittstelle MonadIO und eine Funktion liftIO. Der Ausdrucksdatentyp wird anschließend um Lese- und Schreiboperationen erweitert. Diese werden dann mit Hilfe von einfachen IO-Aktionen implementiert.
Syntaktische Bereiche
data Expr  = ...
           | Read
           | Write  String Expr
Semantische Bereiche
newtype Result a
  = Res {unRes :: VState -> IO (ResVal a, VState)}
Monaden-
Erweiterung
 
instance Monad Result where
  return x      = Res $ ...
  (Res f) >>= g = Res $ ...
 
instance MonadError String Result where
  throwError s  = Res $ ...
 
instance MonadState VState Result where
  get       = Res $ ...
  put new   = Res $ ...
 
instance MonadIO Result where
  liftIO a  = Res $ ...
Interpretierer-
Erweiterungen
 
eval Read
    = ...
 
eval (Write msg e)
    = ...

Letzte Änderung: 27.03.2015
© Prof. Dr. Uwe Schmidt
Prof. Dr. Uwe Schmidt FH Wedel