【问题标题】:Making normal monadic functions work with the monad transformer equivalent使正常的单子函数与单子变换器等效
【发布时间】:2012-02-08 06:07:24
【问题描述】:

我正在尝试解决平衡括号问题。我不想做连续的 IO,宁愿只调用一次 getLine 并解析结果字符串。因此,解决问题的函数将处理两种不同的状态:输入字符串的未使用部分和括号堆栈。

我想设置一些函数来操作堆栈:

type Stack = String

pop :: Stack -> (Char,Stack)
pop (x:xs) = (x,xs)

push :: Char -> Stack -> ((),Stack)
push a xs = ((),a:xs)

如果我在 state monad 中操作,那就太好了,但是我在 StateT monad 中操作

balanced :: StateT Stack (State String) Bool

我知道有人告诉我不要在堆栈中有重复的单子。我这样做是因为我喜欢它简化了 push 和 pop 定义的方式。

两个问题:

  1. 无论我做什么,我都找不到将推送和弹出应用到 StateT 中包含的堆栈。
  2. 我不知道如何从主函数调用它

这是其余的代码

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

【问题讨论】:

  • 尝试使用Reader String 而不是State String 作为内部单子。

标签: parsing haskell stack monads monad-transformers


【解决方案1】:

您的问题是您的 pushpop 只是普通的非单子函数,您试图在单子 do-block 中使用它们。您正确使用了next,因为您使用state 函数调用它,但正如您可能注意到的那样,state 仅适用于普通的State monad 而不是StateT

我们可以像这样实现 state 的 monad 转换器版本:

stateT :: Monad m => (s -> (a, s)) -> StateT s m a
stateT f = do
    (x, s') <- gets f
    put s'
    return x

然后将它与pushpop 一起在balanced 函数中使用。

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 是初始字符串,[] 是初始堆栈。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-02-01
    • 2015-09-28
    • 1970-01-01
    • 2020-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多