我想创建一个函数,它使用状态monad在奇数调用上加5,在偶数调用上减去6。
f 5 = 10
f 7 = 1
f 4 = 9
f 2 = -4
0是偶数,所以f 5加5。 1是奇数,所以f 7减去6,依此类推。
我现在所拥有的:
data Parity = Even | Odd deriving (Show, Eq)
not' :: Parity -> Parity
not' Even = Odd
not' Odd = Even
isOdd :: Int -> State Parity Int
isOdd x = state $ \(p) -> (if p == Odd then x + 5 else x - 6, not' p)
g n = do isOdd n
apply n = runState (g n) Even
我试图这样写,但是每次使用“ apply”时都不会保存状态。 由于末尾只有偶数,所以只加5。 如何使它保存状态,并且只初始化一次而不是每次都初始化呢?
答案 0 :(得分:3)
我几天前写的这篇answer可能会有所帮助。
简而言之,State s
只是将“有状态功能” f :: a -> b
模拟为纯功能f :: (a,s) -> (b,s)
的便捷方法。为了适应monad框架,尽管它们是经过咖喱处理的,所以(大致)形式为f :: a -> s -> (b,s)
。
类型State s b
大致为s -> (b,s)
,可以理解为“返回值b
和最终状态s
且需要初始状态的计算s
待运行”。因此,单子函数a -> State s b
是接受输入a
的函数,可以在给定初始状态s
的情况下运行,以产生值b
和最终状态s
。
您的函数isOdd
是
isOdd x :: Int -> State Parity Int
isOdd x = state $ \p -> (if p == Odd then x + 5 else x - 6, not' p)
大概是
isOdd' x :: Int -> Parity -> (Int,Parity)
isOdd' x p = (if p == Odd then x + 5 else x - 6, not' p)
您的电话
apply n = runState (isOdd n) Even
大概是
apply' n = isOdd' x Even
仅此而已。您实质上是在计算
apply' n = --definition of apply'
isOdd' n Even
-- definition of isOdd'
(\x p -> (if p == Odd then x + 5 else x - 6, not' p)) n Even
-- application to the arguments `n` and `Even`
= (if Even == Odd then n + 5 else n - 6, not' Even)
-- simplifying
= (n - 6, Odd)
所以
apply' n = (n - 6, Odd)
以下是如何正确排序函数的示例,
f :: Int -> State Parity Int
f n = isOdd n >>= (\x -> isOdd x)
或等效地
f :: Int -> State Parity Int
f n = do x <- isOdd n
isOdd x
通过例如apply n = runState (f n) Even
首先运行isOdd n Even
,以获得结果m
和新的最终状态False
,然后运行isOdd m False
。