【发布时间】:2017-06-05 20:40:31
【问题描述】:
我正在尝试重复运行状态转换,直到状态不变。我已经searched Hoogle 为
Eq s => State s () -> State s ()
但没有找到任何看起来合适的东西。
如何重复运行状态转换直到它不改变?
【问题讨论】:
我正在尝试重复运行状态转换,直到状态不变。我已经searched Hoogle 为
Eq s => State s () -> State s ()
但没有找到任何看起来合适的东西。
如何重复运行状态转换直到它不改变?
【问题讨论】:
您可以在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 会很好;)
您正在计算状态转换的固定点。
但是我们不能使用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 仅对惰性幺半群有效。你想做什么?
(<>) for Any 如果第一个参数是 True,则在第二个参数中应该是惰性的,但如果第一个参数是 False,则在所有参数中都是严格的。 Writer [a] 将效率低下,因为 (++) 在第一个参数中具有 O(n) 复杂性。但是对于Any,复杂度总是O(1)。我们可以使用Any 和True 代表一个变化,False 代表唯一的范式,然后我们可以继续应用规则直到我们得到False。
fix...谢谢!我的具体情况:我在codereview.stackexchange.com/a/164380/34049 更改数独求解器的停止条件。状态是每个单元格的潜在值矩阵,当潜在值在迭代之间没有变化时,我想停止迭代。据我所知,我只想住在State。
扩展来自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 内部私下计算,一次在用于测试是否停止的谓词中。
【讨论】:
从https://stackoverflow.com/a/44378096/1319998 和https://stackoverflow.com/a/23924238/1319998 中汲取灵感,您可以在State 之外执行此操作,但使用一些函数单子魔法...
untilStable :: Eq a => (a -> a) -> a -> a
untilStable = until =<< ((==) =<<)
...从使用execState的状态中提取s -> s
untilStable (execState stateTransformToRunRepeatedly) initialState
【讨论】:
untilStable 实现的不可穿透性。