【发布时间】:2018-12-10 10:52:18
【问题描述】:
我正在尝试为我的简单命令式语言解析器编写一个 eval 函数,但是当我使用 Control.Monad 和 State 编写它时遇到了一些问题>.
evalComms Let case 我需要解开类型 (m Int) 以传递 Int 来更新函数,有吗有办法吗?
同样在evalComms Seq case,当递归以两种方式打开时,我需要连接两个 evalComm 函数。 liftM 是这种情况的替代方案吗?
type Env = [(Variable,Int)]
initState :: Env
initState = []
newtype State a = State { runState :: Env -> (a, Env) }
instance Monad State where
return x = State (\s -> (x, s))
m >>= f = State (\s -> let (v, s') = runState m s in
runState (f v) s')
instance Functor State where
fmap = liftM
instance Applicative State where
pure = return
(<*>) = ap
class Monad m => MonadState m where
lookfor :: Variable -> m Int
update :: Variable -> Int -> m ()
instance MonadState State where
lookfor v = State (\s -> (lookfor' v s, s))
where lookfor' v ((u, j):ss) | v == u = j
| v /= u = lookfor' v ss
update v i = State (\s -> ((), update' v i s))
where update' v i [] = [(v, i)]
update' v i ((u, _):ss) | v == u = (v, i):ss
update' v i ((u, j):ss) | v /= u = (u, j):(update' v i ss)
eval :: Comm -> Env
eval p = snd (runState (evalComm p) initState)
evalComm :: MonadState m => Comm -> m ()
evalComm c = case c of
Skip -> return ()
Let v i -> update v (evalIntExp i)
Seq c1 c2 -> return (liftM2 (:) (evalComm c2) (evalComm c1))
evalIntExp :: MonadState m => IntExp -> m Int
evalIntExp v = case v of
Const x -> return (fromInteger x)
Var x -> lookfor x
UMinus x -> liftM (*(-1)) (evalIntExp x)
Plus x y -> liftM2 (+) (evalIntExp x) (evalIntExp y)
Minus x y -> liftM2 (-) (evalIntExp x) (evalIntExp y)
Times x y -> liftM2 (*) (evalIntExp x) (evalIntExp y)
Div x y -> liftM2 div (evalIntExp x) (evalIntExp y)
evalBoolExp :: MonadState m => BoolExp -> m Bool
evalBoolExp b = case b of
BTrue -> return True
BFalse -> return False
Eq x y -> liftM2 (==) (evalIntExp x) (evalIntExp y)
Lt x y -> liftM2 (<) (evalIntExp x) (evalIntExp y)
Gt x y -> liftM2 (>) (evalIntExp x) (evalIntExp y)
And b0 b1 -> liftM2 (&&) (evalBoolExp b0) (evalBoolExp b1)
Or b0 b1 -> liftM2 (||) (evalBoolExp b0) (evalBoolExp b1)
Not b -> liftM not (evalBoolExp b)
请注意,evalComm 的代码不起作用,它可能不正确。
这是我的抽象语法树:
type Variable = String
data IntExp = Const Integer
| Var Variable
| UMinus IntExp
| Plus IntExp IntExp
| Minus IntExp IntExp
| Times IntExp IntExp
| Div IntExp IntExp
| Quest BoolExp IntExp IntExp
deriving Show
data BoolExp = BTrue
| BFalse
| Eq IntExp IntExp
| Lt IntExp IntExp
| Gt IntExp IntExp
| And BoolExp BoolExp
| Or BoolExp BoolExp
| Not BoolExp
deriving Show
data Comm = Skip
| Let Variable IntExp
| Seq Comm Comm
| Cond BoolExp Comm Comm
| While BoolExp Comm
| Repeat Comm BoolExp
deriving Show
【问题讨论】:
-
我仍然是 Haskell 的初学者,所以我没有完全遵循您的代码 - 但关于“我需要解开类型 (m Int) 以仅传递 Int 来更新函数,有没有办法做到这一点?”,这正是一元“绑定”运算符 (
>>=) 的设计初衷吗? -
@RobinZigmond 我尝试使用它,但我无法让它工作!我会再尝试。如果你能提供一个例子,或者如果其他人可以,那就太好了
-
@victor.ja
(>>=)绝对是这项工作的正确工具,所以请展示您尝试过的内容以及遇到的错误。 -
这样做:
return (liftM2 (:) (evalComm c2) (evalComm c1))编译?evalComm c1不是list,而是m ()。 -
@LuisMorillo 不,它没有。我澄清了 evalComm 是错误的。也许我可以把它加粗,因为它在问题的中间!
标签: parsing haskell functional-programming monads