2016-05-30 14 views
1

Für ein Projekt hatten wir die Aufgabe, eine imperative Sprache zu schreiben und über Haskell auszuführen. Der Parser (hier weggelassen) und Teile der Auswertung sind fertig. Jetzt bleibt nur noch die Wirkung zu kodieren. Einen kleinen Roboter manipulieren.IO-Aktion ausführen, aber anderen Typ zurückgeben

den folgenden Code Gegeben:

data Env = Env [Binding] 
instance Show Env where 
    show (Env (x:xs)) = show x ++ ", " ++ show (Env xs) 
    show (Env []) = "" 

data Binding = Binding (String,Int) 
instance Show Binding where 
    show (Binding x) = fst x ++ " : " ++ show (snd x) 

lookup' :: String -> Env -> Int 
lookup' zoek (Env env) = case elemIndex zoek [fst x | Binding x <- env] of 
    Just y -> y 
    Nothing -> error "Not found" 


eval :: Stmt -> Env -> Env 
eval (Seq s) env = foldl (flip eval) env s 
eval (Assign varName aexpr) env = evalAssign varName aexpr env 
eval (If bool stmt1 stmt2) env = evalIf bool stmt1 stmt2 env 
eval (While bool stmt) env = undefined 
eval (MotorInstruct string aExpr) env = undefined 
eval (SensorRead string) env = undefined 
eval Skip env = env 

evalAExpr :: AExpr -> Env -> Int 
evalAExpr (IntConst int) _ = fromInteger int 
evalAExpr (Neg a) env = - evalAExpr a env 
evalAExpr (ABinary Add a b) env = evalAExpr a env + evalAExpr b env 
evalAExpr (ABinary Subtract a b) env = evalAExpr a env - evalAExpr b env 
evalAExpr (ABinary Multiply a b) env = evalAExpr a env * evalAExpr b env 
evalAExpr (ABinary Divide a b) env = evalAExpr a env `div` evalAExpr b env 
evalAExpr (Var x) env = getElementAtEnv env (lookup' x env) 
    where 
    getElementAtEnv (Env env) index = getSndFromBinding (env !! index) 
    getSndFromBinding (Binding (_,t)) = t 


evalBExpr :: BExpr -> Env -> Bool 
evalBExpr (BoolConst bool) _ = bool 
evalBExpr (Not expr) env = not $ evalBExpr expr env 
-- Boolean operators 
evalBExpr (BBinary And a b) env = evalBExpr a env && evalBExpr b env 
evalBExpr (BBinary Or a b) env = evalBExpr a env || evalBExpr b env 
-- Relational operators 
evalBExpr (RBinary Greater a b) env = evalAExpr a env > evalAExpr b env 
evalBExpr (RBinary Less a b) env = evalAExpr a env < evalAExpr b env 
evalBExpr (RBinary Equal a b) env = evalAExpr a env == evalAExpr b env 


evalIf :: BExpr -> Stmt -> Stmt -> Env -> Env 
evalIf expr s1 s2 env = if evalBExpr expr env 
    then 
    eval s1 env 
    else 
    eval s2 env 


evalAssign :: String -> AExpr -> Env -> Env 
evalAssign term s (Env env)= if term `elem` transform 
    then 
    Env (take (lookup' term (Env env)) env ++ [Binding (term, evalAExpr s (Env env))]++ drop (lookup' term (Env env) + 1) env) 
    else 
    Env (env ++ [Binding (term, evalAExpr s (Env env))]) 
    where transform = [ fst ele | Binding ele <- env] 


zoekMotor :: String -> Int 
zoekMotor "left" = 0x9 
zoekMotor "right" = 0xa 
zoekMotor _ = error "No such motor" 


sendToMotor :: String -> Int -> IO() 
sendToMotor m s = do 
    bot <- openMBot 
    sendCommand bot $ setMotor (zoekMotor m) s s 
    closeMBot bot 

evalMotorInstruct :: String -> AExpr -> Env -> Env 
evalMotorInstruct welke waarde env = do 
    sendToMotor welke (evalAExpr waarde env) 
    return env 

Wie würde ich mich über die Funktion Ausführen sendToMotor in meiner Bewertungsfunktion (die IO() zurückgibt) evalMotorInstruct die eine Env zurückgeben sollte? Ich bin etwas zu sehr darüber im Unklaren, wie ich meine 'action' - Funktion ausführen und nur meine Env wieder aus der Auswertungsfunktion herausholen würde.

Beachten Sie, dass der aktuelle Code für evalMotorInstruct nicht korrekt ist. Die Funktion soll eine Env zurück, aber tatsächlich gibt ein IO Env

Danke

+3

Sie haben eine der interessanten Eigenschaften von Haskell-seine Reinheit entdeckt. Sie können keine E/A-Aktion ausführen und ein in IO eingeschlossenes Ergebnis * not * zurückgeben. Dies liegt daran, dass Haskell-Funktionen keine beliebigen Nebenwirkungen ausführen können. Sie müssen rein sein. Sie * könnten * ein 'IO Env' zurückgeben, das hier zu sein scheint, basierend auf Ihrer Verwendung von' do' und 'return'. –

+0

@AlexisKing Bedeutet dies, dass ich alle meine anderen "eval" -Funktionen konvertieren sollte, um auch IO Env's zurückzugeben? Und sie "auspacken" in jeder Situation? – MrKickkiller

+1

Vielleicht möchten Sie sich mit einer freien Monade beschäftigen, um zu vermeiden, dass Sie sich an 'IO' binden. http://www.haskellforall.com/2012/06/you-could-have-vent-free-monads.html – user2297560

Antwort

2

Ihre AExpr und BExpr Typen repräsentieren reine Berechnungen in Ihrer Sprache - nicht nur nicht, dass sie alle IO tun, aber sie auch don Ändere die Umgebung nicht. Folglich sollten Sie ihre Prüffunktionen nicht ändern müssen.

Sie müssen nur die Bewertung von Stmt Werten ändern. Die Art Signatur ändert sich zu:

eval :: Stmt -> Env -> IO Env 

Ein Beispiel, wie eval Seq ändern:

eval (Seq []) env  = return env 
eval (Seq (s:ss)) env = do env' <- eval s env -- eval the first statement 
          eval (Seq ss) env' -- eval the rest 

Beachten Sie, dass eval If nicht geändert werden muss:

eval (If bool stmt1 stmt2) env = 
    if evalBExpr bool env 
    then eval stmt1 env 
    else eval stmt2 env 

evalMotorInstruct kompiliert, wenn Sie ändern die Signatur zu:

evalMotorInstruct :: String -> AExpr -> Env -> IO Env 

Ich überlasse den Rest Ihnen.

Beim Refactoring einfach den Code auskommentieren, der nicht kompiliert wird. Fügen Sie dann schrittweise die Zeilen nacheinander hinzu, um alle zu kompilieren, bevor Sie eine weitere Zeile hinzufügen. Verwenden Sie ... = undefined, wenn Sie müssen. Komm zurück und fülle diese später aus.

+0

Eine mögliche Verbesserung wäre die Verwendung von 'StateT Env IO' als Monad, was es Ihnen erlauben würde, die Umgehung des' Env' von Hand wegzulassen und auch 'eval (Seq ss) = mapM_eval ss' etc . – Cactus

Verwandte Themen