2016-05-30 106 views
1

對於項目,我們被賦予編寫命令式語言並通過Haskell執行的任務。解析器(這裏省略)和部分評估完成。現在只剩下編碼效果了。操縱一個小機器人。執行IO操作,但返回其他類型

考慮下面的代碼:

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 

我怎麼會去執行函數sendToMotor(返回IO())在我的評價功能evalMotorInstruct應該返回Env?我對於如何執行我的'動作'功能有點不知所措,只會讓我的Env退出評估功能。

請注意,evalMotorInstruct的當前代碼不正確。該函數應該返回一個Env,但實際上返回IO Env

謝謝

+3

您已經發現Haskell的一個有趣屬性 - 它的純度。你不能執行一個IO操作並返回一個結果*不包含在IO中。這是因爲Haskell函數不能執行任意的副作用;他們必須是純粹的。根據你對'do'和'return'的使用,你可以*返回一個'IO Env',這似乎是你在這裏尋找的東西。 –

+0

@AlexisKing這是否意味着我應該轉換所有其他的「eval」函數以返回IO Env的呢?在各種情況下「解開」它們? – MrKickkiller

+1

你可能想用一個免費的monad來避免提交'IO'。 http://www.haskellforall.com/2012/06/you-could-have-invented-free-monads.html – user2297560

回答

2

你AExpr和BExpr類型代表了你的語言純粹的計算 - 不僅不自己做任何IO,但他們也不要不改變環境。因此你不應該修改它們的eval函數。

因此,您只需修改Stmt值的評估。類型簽名將變爲:

eval :: Stmt -> Env -> IO Env 

eval Seq將如何改變一個例子:

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 

注意eval If並不需要改變:

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

evalMotorInstruct將編譯如果您將其簽名更改爲:

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

我會把剩下的給你。

重構時,只需註釋掉不編譯的代碼即可。然後在添加另一行之前逐行遞增地添加行,以便每個行進行編譯。如果需要,請使用... = undefined。回來後再填寫。

+0

一個潛在的改進就是使用'StateT Env IO'作爲monad,它可以讓你省略手動傳遞'Env',還可以寫'eval(Seq ss)= mapM_ eval ss'等。 – Cactus