微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在Haskell的do块中的let中使用状态

如何解决在Haskell的do块中的let中使用状态

我具有以下数据结构和功能

data BTree a = BLeaf | BNode (BTree a) a (BTree a) deriving (Show,Eq)
freshNodesS :: BTree String -> State [String] (BTree String)
freshNodesS BLeaf = return BLeaf
freshNodesS (BNode l m r) = do  l' <- freshNodesS l
                                let m' = getFresh m s
                                let s' = m : s
                                r' <- freshNodesS r s'
                                return (BNode l' m' r')

一个问题,我实际上要使用freshNodesS l的状态,该状态应提供(BTree String,[String])输出,但是在do块中我不能使用(l',s) <- freshNodesS l,我看到的唯一选择是将所有内容都放在lambda函数中。 但是有办法我仍然可以使用do表示法吗?


在@chi说完之后,我这样做了:

freshNodesS BLeaf           = return BLeaf
freshNodesS (BNode l m r)   = do    l' <- freshNodesS l
                                    m' <- getFreshS m
                                    r' <- freshNodesS r
                                    return (BNode l' m' r')
                    
getFreshS :: String -> State [String] String
getFreshS x = state $ (\s -> let newx = getFresh x s in (newx,newx: s))

那行得通。

解决方法

State monad的全部要点是自动传递状态,而无需您明确地进行操作。几乎没有函数应该将s作为参数或返回它。

例如,

let m' = getFresh m s

可疑,可能应该阅读

m' <- getFresh m

我们将拥有getFresh :: String -> State [String] String的地方。

整个代码应读为

       do l' <- freshNodesS l
          m' <- getFresh m
          r' <- freshNodesS r
          return (BNode l' m' r')

请注意,从未提及过ss'。这应该看起来像命令式代码,其中每个函数调用都会修改一个可变状态变量,即使该代码没有明确提及。

现在,在getFresh中,您将不得不处理州,因为没有办法解决。如果您的State monad是标准的monad,则可以使用getput访问状态。您可能需要类似

getFresh :: String -> State [String] String
getFresh m = do
   s <- get          -- read the current state
   let m' = ...      -- compute a fresh name m'
   let s' = m' : s   -- mark m' as used
   put s'            -- write the current state
   return m'
,

do-notation只是句法糖重写<-绑定与>>=绑定和lambda。如果可以用一个书写,则可以用另一个书写。因此,如果您认为可以使用lambda编写代码,则建议您这样做。然后,将其重写以使用do-notation,您将学到一些东西。但是我怀疑您会遇到同样的绊脚石,因为正如我说的那样,使用lambda代替do-notation实际上并没有什么特别之处。

我很难说出您打算写什么,因为您没有为getFresh提供类型签名。令人费解的是,此函数将状态作为直接参数,而不是像程序的其余部分一样参与State monad。我建议将其重写以具有签名

getFresh :: String -> State [String] String
getFresh m = do {...}

您当然必须更改实现,我建议您查看getput操作。但是进行了此更改后,您的freshNodesS函数将无需执行任何手动穿插state参数的操作,因为它将完全由State机制按预期方式处理:

freshNodesS (BNode l m r) = do
  l' <- freshNodesS l
  m' <- getFresh m
  r' <- freshNodesS r
  return (BNode l' m' r')

或者,您可以改用应用样式编写此代码:

freshNodesS (BNode l m r) = 
  BNode <$> freshNodesS l <*> getFresh m <*> freshNodesS r

这样,您就可以清楚地知道,除了共享相同的状态外,这三个操作都不相互依赖,并且不需要为执行不多的变量命名。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?