使普通的一元函数与等效的一元转换器一起工作

发布于 2025-01-03 02:38:54 字数 1454 浏览 1 评论 0原文

我正在尝试解决平衡括号问题。我不想进行连续的 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。我这样做是因为我喜欢它如何简化推送和弹出定义。

两个问题:

  1. 无论我做什么,我都找不到将push和pop应用于 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

I'm trying to solve the balanced brackets problem. I don't want to do continuous IO, and would rather make a single call to getLine and parse the resulting string. Therefore the function that solves the problem will deal with two different states: The unconsumed part of the input string, and the bracket stack.

I want to set up some functions for manipulating a stack:

type Stack = String

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

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

That's all good if I'm operating in the state monad, however I'm operating in the StateT monad

balanced :: StateT Stack (State String) Bool

I know I've been told not to have duplicate monads in the stack. I'm doing it this way because I like how it simplifies the push and pop definitions.

Two problems:

  1. No matter what I do I can't find a way to apply push and pop to the
    Stack contained in the StateT.
  2. I have no idea how to call this from the main function

Here's the rest of the code

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

憧憬巴黎街头的黎明 2025-01-10 02:38:54

您的问题是您的 pushpop 只是普通的非单子函数,您试图在单子 do 块中使用它们。您正确使用了 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

然后在 balanced 函数中使用 pushpop.

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

Your problem is that your push and pop are just ordinary, non-monadic function which you are trying to use in the monadic do-block. You are using next correctly, since you call it using the state function, but as you probably noticed, state only works for the plain State monad and not StateT.

We can implement a monad transformer version of state like this:

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

And then use it in the balanced function with push and 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

The function is called like this:

evalState (evalStateT balanced []) s

Where s is the initial string and [] is the initial stack.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文