2016-05-30 12 views
1

プロジェクトでは、命令型言語を記述し、Haskell経由で実行するタスクが与えられました。パーサー(ここでは省略)と評価の部分が行われます。現在は、効果をコード化するだけです。小さなロボットを操作する。どのように私はEnvを返す必要があります私の評価関数evalMotorInstructで(IO()を返す)関数sendToMotorを実行するに行くか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 

は、次のコードを考えると?私は私の「行動」機能をどのように実行するのかというだけではなく、評価機能から私のEnvを取り戻すだけです。

evalMotorInstructの現在のコードが正しくないことに注意してください。関数はEnvを返すことになっているが、実際IO Env

+3

あなたはハスケルの興味深い特性の1つ、その純度を発見しました。 IOアクションを実行してIOにラップされていない結果*を返すことはできません。これは、Haskell関数が任意の副作用を実行できないためです。彼らは純粋でなければならない。あなたは、 'do'と' return'の使用に基づいて、ここで探しているような 'IO Env'を返すことができます。 –

+0

@AlexisKingこれはIO Envを返すために私の他のすべての "eval"関数を変換すべきことを意味しますか?そして、あらゆる状況でそれらを「アンラッピング」しますか? – MrKickkiller

+1

あなたは 'IO'にコミットするのを避けるためにフリーなモナドを使うことを検討したいかもしれません。 http://www.haskellforall.com/2012/06/you-could-have-invented-free-monads.html – user2297560

答えて

2

あなたAExprとBExprタイプがあなたの言語で、純粋な計算を表すありがとう返している - だけでなく、彼らはすべてのIOを行いませんが、また、彼らドン環境を変更しないでください。したがって、評価関数を変更する必要はありません。

Stmtの値を変更するだけで済みます。型シグネチャが変更された:

eval :: Stmt -> Env -> IO Env 

eval Seqがどのように変化するかの例:

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

evalMotorInstructがあれば、コンパイルします:変更する必要はありません

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ことあなたはその署名を次のように変更します:

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

私はあなたに残ります。

リファクタリング時にコンパイルされないコードをコメントアウトするだけです。それから、別々の行を追加する前に、それぞれをコンパイルするために、インクリメンタルに行を1つずつ戻します。必要に応じて... = undefinedを使用してください。戻って来て、後でそれらを記入してください。

+0

これ以上の潜在的な改善は、あなたが 'Env'を手で渡すことを省略することができ、' eval(Seq ss)= mapM_eval ss'などと書くことができるモナドとして 'StateT Env IO'を使うことです。 – Cactus

関連する問題