我正在尝试解决平衡括号问题。我不想做连续的IO,宁愿只调用getLine并解析生成的字符串。因此,解决问题的函数将处理两种不同的状态:输入字符串的未消耗部分和括号堆栈。
我想设置一些操作堆栈的函数:
type Stack = String
pop :: Stack -> (Char,Stack)
pop (x:xs) = (x,xs)
push :: Char -> Stack -> ((),Stack)
push a xs = ((),a:xs)
如果我在州monad运营,那就好了,不过我在StateT monad中运营
balanced :: StateT Stack (State String) Bool
我知道我被告知不要在堆栈中有重复的monad。我这样做是因为我喜欢它如何简化推送和流行定义。
两个问题:
这是代码的其余部分
next :: String -> (Maybe Char,String)
next "" = (Nothing,[])
next (x:xs) = (Just x,xs)
balanced = do
c <- lift (state next)
case c of
Nothing -> return True
Just c -> if elem c open
then (push c) >> balanced
else if elem c close
then pop >>= \x ->
if eq x c
then balanced
else return False
else balanced
where open = "<{(["
close = "])}>"
eq '(' ')' = True
eq '{' '}' = True
eq '<' '>' = True
eq '[' ']' = True
eq _ _ = False
答案 0 :(得分:7)
你的问题是你的push
和pop
只是普通的非monadic函数,你试图在monadic do-block中使用它。您正在使用next
,因为您使用state
函数调用它,但正如您可能已经注意到的那样,state
仅适用于普通State
monad而不是{{1} }}
我们可以像这样实现StateT
的monad变换器版本:
state
然后在stateT :: Monad m => (s -> (a, s)) -> StateT s m a
stateT f = do
(x, s') <- gets f
put s'
return x
函数balanced
和push
中使用它。
pop
该函数的调用如下:
balanced :: StateT Stack (State String) Bool
balanced = do
c <- lift (state next)
case c of
Nothing -> return True
Just c -> if elem c open
then (stateT $ push c) >> balanced
else if elem c close
then stateT pop >>= \x ->
if eq x c
then balanced
else return False
else balanced
where open = "<{(["
close = "])}>"
eq '(' ')' = True
eq '{' '}' = True
eq '<' '>' = True
eq '[' ']' = True
eq _ _ = False
其中evalState (evalStateT balanced []) s
是初始字符串,s
是初始堆栈。