链接状态 Monad

Chaining State Monad

我有一个功能

step :: Int -> State Int Int
step n = get >>= \x ->  put  (x `div` n) >> return (x `mod` n)

λ> runState (step 25) 41
(16,1)

我如何 运行 一个 step 的序列,具有不同的 n 值,并且每个步骤都使用上一步的状态?

例如,步骤如下

第一步产生 (16,1),然后我想将其用作下一步 n = 10 的输入,它应该产生 (6, 2)。添加第一步中的 1,第一步中的 16 为 mod,新的 n.

n = 25 gives (16,1) then
n = 10 gives (6,2)  then
n = 5  gives (1,3) then
n = 1 gives (0,4)

我知道在这里使用 State 可能不正确;但我正在尝试将其作为一种学习方式。

可能的目标是用状态 monad 来实现这个功能。

greedy :: Double -> Int
greedy owed = snd $ foldl go (pennies,0) [25, 10, 5, 1]
  where
    pennies                     = floor . (*100) $ owed
    go (remaining,counter) coin = (remaining',counter')
      where
        remaining' = remaining `mod` coin
        counter'   = counter + remaining `div` coin

函数,

mapM step [25,10,5,1]

或更一般的

traverse step [25,10,5,1]

[25,10,5,1] 的每个列表运行 step。调用

runState  (mapM step [25,10,5,1]) 41

运行初始状态设置为41的函数,返回步骤输出列表和最终状态。

([16,1,0,0],0)

如果您想将状态与输出一起列出,只需修改 step 以包含它们。

step n = get >>= \x ->  put  (x `div` n) >> return ((x `mod` n),(x `div` n))

或者,换句话说

step n = do 
  x <- get
  let (r,x') = (x `mod` n,x `div` n)
  put  x'
  return (r,x')

结果是,([(16,1),(1,0),(0,0),(0,0)],0),仍然不是你要找的,但更接近了。恐怕我不太了解你的方程式的细节,无法得到你要找的东西,但这应该有助于理清状态部分,让你专注于数学。

要实现上述 go 功能:

go n = do
   (r,c) <- get
   let (r',c') = (r `mod` n, c + (r `div` n))
   put (r',c')
   return (r',c')

runState (mapM go [25,10,5,1]) (41,0)

产量,

([(16,1),(6,2),(1,3),(0,4)],(0,4))

希望对您有所帮助