1

下面的玩具示例通过调用函数来不确定地计算一个数字anyFunction,然后只保留偶数选择。我怎样才能编写一个类似的代码来保持最大选择而不是偶数选择?我需要不同的单子堆栈签名吗?

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
4

2 回答 2

2

StateT Int [] Int可能不是你想要的。扩展定义我们可以看到它是Int -> [(Int, Int)]。当我们这样做时s <- lift [1, 2, 3, 4],我们为每个数字运行多个有状态的操作,然后返回值和修改的状态都被收集到一个列表中。

这意味着这个 monad 不能真正使用 计算列表的最大值lift,因为我们对列表中的每个数字都有单独的状态(并且每个状态对其他状态都是不可见的)。

我们最好用一个简单的mapM或在这里:flipMState 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

这直接对应于计算最大值的常用命令式计数器。

于 2014-08-04T19:11:21.863 回答
2

你想要的是“运行在[]下面StateT”,可以这么说,获得 的所有Int结果anyFunction,同时尽可能保留 monad 堆栈的其余部分。

您想要一个类型类似于 的函数 StateT Int [] Int -> State Int [Int]。这得到所有Ints 以便您可以计算最大值。

但是考虑到你的 monad 堆栈,你的函数很难实现。计算的每个分支路径都有自己的状态“线程”,但是当您将 a 简化 StateT Int [] Int为 aState Int [Int]时,我们应该保留哪个状态“线程”?没有看起来很自然的解决方案。

现在,假设您正在使用ListT (State Int) Intmonad 堆栈。这里所有分支共享相同的状态“线程”。专业runListT,有签名 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
于 2014-08-05T07:11:40.580 回答