【问题标题】:Repeat state until it doesn't change / is stable重复状态直到它不改变/稳定
【发布时间】:2017-06-05 20:40:31
【问题描述】:

我正在尝试重复运行状态转换,直到状态不变。我已经searched Hoogle

Eq s => State s () -> State s ()

但没有找到任何看起来合适的东西。

如何重复运行状态转换直到它不改变?

【问题讨论】:

    标签: haskell monads


    【解决方案1】:

    您可以在State 中执行此操作,但没有特别的好处。为什么不:

    idempotently :: Eq a => (a -> a) -> a -> a
    idempotently f a = if a' == a then a else idempotently f a' where a' = f a
    
    idempotentlyM :: Eq s => (s -> s) -> State s ()
    idempotentlyM f = modify (idempotently f)
    

    如果您以State s () 开头,您可以通过execState 来获取您的s -> s

    当然,不能保证f 不会发散,所以idempotently f 可能永远不会终止。

    【讨论】:

    • 这是一个公平的观点,您只需要查看状态,但如果您想做一些额外的副作用,如liftIO 的情况,这不会够了。所以 +1 来回避这个问题,但是...... ;)
    • 没有liftIOing 可以与State s () 一起完成。
    • 我概括为StateT s m () ;)
    • 顺便说一句,你计算 f a 两次,这里的 let-binding 会很好;)
    【解决方案2】:

    您正在计算状态转换的固定点。 但是我们不能使用fix,因为我们处于一元上下文中。 所以让我们改用一元定点组合器。 输入mfix

    import Control.Monad (unless)
    import Control.Monad.State (MonadState, StateT, get, put)
    import Control.Monad.Fix (mfix)
    import Control.Monad.IO.Class (liftIO)
    
    untilStable :: MonadState s m => (s -> s -> Bool) -> m a -> m ()
    untilStable p = mfix $ \f st -> p <$> get <* st <*> get >>= (`unless` f)
    

    我还冒昧地概括了您的函数,以便您可以提供用户提供的二进制谓词。

    使用 ghci runState (untilStable (==) $ modify (+1)) 2 将永远不会终止。 但是:

    comp :: StateT Int IO ()
    comp = do
      s1 <- (+1) <$> get
      liftIO $ print s1
      let s2 = if s1 >= 3 then 3 else s1
      put s2
    

    你得到:

    > runStateT (untilStable (==) comp) 0
    1
    2
    3
    4
    ((),3)
    

    这个untilStable可以进一步概括为:

    untilStable :: MonadState s m => (s -> s -> Bool) -> m a -> m a
    untilStable p = mfix $ \f st -> do
      before <- get
      a <- st
      after  <- get
      if p before after then pure a else f
    

    现在我们已经释放了计算可以产生的类型。

    修正你想用fix实现idempotently,你可以这样做:

    import Data.Function (fix)
    
    idempotently :: Eq a => (a -> a) -> a -> a
    idempotently = fix $ \i f a ->
      let a' = f a
      in if a' == a then a else i f a'
    

    【讨论】:

    • fix 即使不是在单子上下文中也适用吗?我怀疑它不是完全需要的,因为它似乎没有任何比较连续迭代值的概念。
    • fix,即mfix的非单子变体可用于实现idempotently。我将更新我的答案以实现idempotently。你所说的值,是指状态的值,还是计算的值?我还得问,你为什么要计算状态变换直到一个固定点?你唯一的目标是住在State 内而不是一些StateT s m a, m /= Identity 内吗?如果要对术语进行规范化,请考虑基于 Writer Any 的规范化 monad,因为在每一步都使用 (==) 效率低。
    • Writer 仅对惰性幺半群有效。你想做什么?
    • @dfeuer (&lt;&gt;) for Any 如果第一个参数是 True,则在第二个参数中应该是惰性的,但如果第一个参数是 False,则在所有参数中都是严格的。 Writer [a] 将效率低下,因为 (++) 在第一个参数中具有 O(n) 复杂性。但是对于Any,复杂度总是O(1)。我们可以使用AnyTrue 代表一个变化,False 代表唯一的范式,然后我们可以继续应用规则直到我们得到False
    • @Centril 我知道如何使用fix...谢谢!我的具体情况:我在codereview.stackexchange.com/a/164380/34049 更改数独求解器的停止条件。状态是每个单元格的潜在值矩阵,当潜在值在迭代之间没有变化时,我想停止迭代。据我所知,我只想住在State
    【解决方案3】:

    您可以使用getunless 滚动您自己的更高级别的递归转换

    untilStable :: Eq s => State s () -> State s ()
    untilStable stateTransform = do
      before <- get
      stateTransform
      after <- get
      unless (before == after) $ untilStable stateTransform
    

    【讨论】:

      【解决方案4】:

      扩展来自https://stackoverflow.com/a/44381834/1319998 的答案,因此将迭代排除在State 之外,但通过更清晰地使用until 来避免函数monad 魔法

      untilStable :: Eq a => (a -> a) -> a -> a
      untilStable f a = until (\w -> f w == w) f a
      

      ... 并类似地使用 execState 从状态中提取 s -> s

      untilStable (execState stateTransformToRunRepeatedly) initialState
      

      没有使用无点样式,但对我来说更清楚发生了什么。

      另外,我想知道这是否揭示了 this 和 https://stackoverflow.com/a/44381834/1319998 的效率低下,其中 f a 可能会计算两次:一次在 until 内部私下计算,一次在用于测试是否停止的谓词中。

      【讨论】:

        【解决方案5】:

        https://stackoverflow.com/a/44378096/1319998https://stackoverflow.com/a/23924238/1319998 中汲取灵感,您可以在State 之外执行此操作,但使用一些函数单子魔法...

        untilStable :: Eq a => (a -> a) -> a -> a
        untilStable = until =<< ((==) =<<)
        

        ...从使用execState的状态中提取s -&gt; s

        untilStable (execState stateTransformToRunRepeatedly) initialState
        

        【讨论】:

        • 我喜欢 untilStable 实现的不可穿透性。
        猜你喜欢
        • 2015-06-11
        • 2021-11-28
        • 1970-01-01
        • 1970-01-01
        • 2020-09-30
        • 1970-01-01
        • 2017-11-19
        • 2020-09-15
        • 2021-09-11
        相关资源
        最近更新 更多