以下玩具示例通过调用函数anyFunction
来计算非确定性数字,然后仅保留偶数选项。我怎么能写出一个类似的代码来保持最大的选择而不是偶数的选择呢?我需要一个不同的monad堆栈签名吗?
anyFunction :: StateT Int [] Int
anyFunction = undefined
test :: StateT Int [] Int
test = do
s <- anyFunction
put s
v <- get
if even v then return v else mzero
答案 0 :(得分:2)
StateT Int [] Int
可能不是你想要的。扩展定义我们可以看到它是Int -> [(Int, Int)]
。当我们执行s <- lift [1, 2, 3, 4]
时,我们为每个数字运行多个有状态操作,然后将返回值和已修改状态都收集到列表中。
这意味着这个monad无法使用lift
计算列表的最大值,因为列表中的每个数字都有不同的状态(每个状态对其他状态都是不可见的)。
我们在mapM
或flipM
处使用简单的State Int Int
更好:
import Control.Monad
import Control.Monad.State.Strict
test :: State Int Int
test = do
forM_ [1, 2, 3, 4] $ \n -> do
modify $ max n
get
这直接对应于计算最大值的通常命令计数器。
答案 1 :(得分:2)
你想要的是“在[]
”下运行StateT
可以说,获得Int
的所有anyFunction
结果,同时保留其余的StateT Int [] Int -> State Int [Int]
monad尽可能多地堆叠。
您希望某个类型与Int
类似的函数。这将获得所有StateT Int [] Int
s,以便您可以计算最大值。
但是考虑到你的monad堆栈,你的功能很难实现。计算的每个分支路径都有自己的状态“线程”,但是当你将State Int [Int]
减少到ListT (State Int) Int
时,我们应该保留哪个“线程”状态?没有一种解决方案看起来很自然。
现在,假设您正在使用runListT
monad堆栈。这里所有分支都共享相同的“线程”状态。专门化ListT (State Int) Int -> State Int [Int]
,它具有签名anyFunction :: ListT (State Int) Int
anyFunction = undefined
test :: ListT (State Int) Int
test = do
-- preserve non-ListT parts of the stack
-- and re-wrap the result into a list
s <- ListT $ liftM (\l -> [maximum l]) $ runListT anyFunction
put s
v <- get
if even v then return v else mzero
。
示例可以写成如下:
{{1}}