2016-05-30 63 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