我一点一点地学习一些Haskell并且(慢慢地)正在努力理解State monad,尝试编写一个重复State计算的函数,直到状态满足一些布尔测试并在列表中收集返回值以获得整体结果.我终于成功了:
collectUntil :: (s -> Bool) -> State s a -> State s [a]
collectUntil f s = do s0 <- get
let (a,s') = runState s s0
put s'
if (f s') then return [a] else liftM (a:) $ collectUntil f s
Run Code Online (Sandbox Code Playgroud)
以便
simpleState = state (\x -> (x,x+1))
*Main> evalState (collectUntil (>10) simpleState) 0
[0,1,2,3,4,5,6,7,8,9,10]
Run Code Online (Sandbox Code Playgroud)
这是否是这项任务的合理功能,还是有更惯用的方式?
当我第一次开始编写monadic代码时,你犯的错误与我所做的完全相同 - 使它过于复杂,过度使用liftM和使用不足>>=(相当于使用<-符号).
理想情况下,您根本不需要提及州内monad runState或evalState在州内monad.您想要的功能如下:
f,那么返回s并将其结果添加到输出你可以直接这样做:
collectUntil f comp = do
s <- get -- Get the current state
if f s then return [] -- If it satisfies predicate, return
else do -- Otherwise...
x <- comp -- Perform the computation s
xs <- collectUntil f comp -- Perform the rest of the computation
return (x:xs) -- Collect the results and return them
Run Code Online (Sandbox Code Playgroud)
请注意,如果它们属于同一个monad,则可以嵌套do语句!这非常有用 - 它允许您在一个do块中进行分支,只要if语句的两个分支都导致相同的monadic类型.
此函数的推断类型是:
collectUntil :: MonadState t m => (t -> Bool) -> m a -> m [a]
Run Code Online (Sandbox Code Playgroud)
如果您愿意,可以将其专门化为该State s类型,但您不必:
collectUntil :: (s -> Bool) -> State s a -> State s [a]
Run Code Online (Sandbox Code Playgroud)
如果你想稍后使用不同的monad,保持更一般的状态可能更为可取.
每当s进行有状态计算并且你在状态monad中时,你就可以做到
x <- s
Run Code Online (Sandbox Code Playgroud)
而x现在将有计算的结果(如同您叫evalState并且在初始状态下供给).如果您需要检查状态,您可以这样做
s' <- get
Run Code Online (Sandbox Code Playgroud)
并且s'将当前状态的值.
| 归档时间: |
|
| 查看次数: |
2744 次 |
| 最近记录: |