Haskell 如何定义状态单子?
我想定义一个管理错误的状态单元组(在某种意义上类似于Maybe):如果在“do”计算过程中发生错误/问题,它是信号引导的,并通过Haskell 如何定义状态单子?,haskell,monads,Haskell,Monads,我想定义一个管理错误的状态单元组(在某种意义上类似于Maybe):如果在“do”计算过程中发生错误/问题,它是信号引导的,并通过>=传播。 错误还应该包含一个描述它的字符串。 之后,我想将此monad应用于mapTreeM,使用for map函数将状态假设为数字和包含数字的树,并在每个访问步骤通过添加当前叶的值来更新当前状态;生成的树必须包含一个具有旧叶值和访问瞬间状态的对。如果在计算过程中状态变为负值,则该访问必须失败,如果为正值,则访问必须成功 e、 g.给定此树:分支(分支(叶7)(分支(
>=
传播。
错误还应该包含一个描述它的字符串。
之后,我想将此monad应用于mapTreeM
,使用for map函数将状态假设为数字和包含数字的树,并在每个访问步骤通过添加当前叶的值来更新当前状态;生成的树必须包含一个具有旧叶值和访问瞬间状态的对。如果在计算过程中状态变为负值,则该访问必须失败,如果为正值,则访问必须成功
e、 g.给定此树:分支(分支(叶7)(分支(叶(-1))(叶3)))(分支(叶(-2))(叶9))
我们得到一棵树(考虑初始状态0):分支(分支(叶(7,7))(分支(叶(-1,6))(叶(3,9)))(分支(叶(-2,7))(叶(9,16))
如果我们把-18
放在第二个叶子中,我们应该得到一个错误的值,表明我们达到了负状态(-11)
我做了这样一件事来打印树而不管理错误…我不知道怎么做。
以下是我的代码:
module Main where
-- State monad
newtype State st a = State (st -> (st, a))
instance Monad (State state) where
return x = State(\s -> (s,x))
State f >>= g = State(\oldstate ->
let (newstate, val) = f oldstate
State newf = g val
in newf newstate)
-- Recursive data structure for representing trees
data Tree a = Leaf a | Branch (Tree a) (Tree a)
deriving (Show,Eq)
-- Utility methods
getState :: State state state
getState = State(\state -> (state,state))
putState :: state -> State state ()
putState new = State(\_ -> (new, ()))
mapTreeM :: (Num a) => (a -> State state b) -> Tree a -> State state (Tree b)
mapTreeM f (Leaf a) =
f a >>= (\b -> return (Leaf b))
mapTreeM f (Branch lhs rhs) = do
lhs' <- mapTreeM f lhs
rhs' <- mapTreeM f rhs
return (Branch lhs' rhs')
numberTree :: (Num a) => Tree a -> State a (Tree (a,a))
numberTree tree = mapTreeM number tree
where number v = do
cur <- getState
putState(cur+v)
return (v,cur+v)
-- An instance of a tree
testTree = (Branch
(Branch
(Leaf 7) (Branch (Leaf (-1)) (Leaf 3)))
(Branch
(Leaf (-2)) (Leaf (-20))))
runStateM :: State state a -> state -> a
runStateM (State f) st = snd (f st)
main :: IO()
main = print $ runStateM (numberTree testTree) 0
modulemain其中
--状态单子
newtype State st a=状态(st->(st,a))
实例Monad(State-State),其中
返回x=状态(\s->(s,x))
状态f>>=g=状态(\oldstate->
let(newstate,val)=f oldstate
状态newf=g val
在newf newstate中)
--表示树的递归数据结构
数据树a=叶a |分支(树a)(树a)
推导(显示,等式)
--实用方法
getState::State
getState=State(\State->(State,State))
putState::state->state状态()
putState new=状态(\ \ \->(新,())
mapTreeM::(Num a)=>(a->状态b)->树a->状态(树b)
mapTreeM f(叶a)=
fa>>=(\b->返回(叶b))
mapTreeM f(分支lhs rhs)=do
我能为你的问题提出一个替代方案吗?虽然单子在很多方面都很好,但是您可以通过一个简单的
跟踪错误。
下面我的函数transferVal
就是这样一个函数的例子。
函数transferVal
遍历
树
从左到右,同时保留找到的最后一个值。如果发生错误,函数将返回错误并停止遍历树
。
与其使用Maybe
,不如在出现错误时使用或来获得更清晰的错误。在我的示例中,我使用([ChildDir],a)
,其中[ChildDir]
包含
牵连节点的“方向”和a
是触发错误的错误值。函数printErrorsOrTree
是如何使用transferVal
和main
的输出的一个示例,其中包含4个示例,前三个示例是正确的,最后一个示例触发您预期的错误
module Main where
import Data.List (intercalate)
import Control.Monad (mapM_)
data Tree a = Leaf a | Branch (Tree a) (Tree a)
deriving (Show,Eq)
-- given a Branch, in which child the error is?
data ChildDir = LeftChild | RightChild
deriving Show
-- an error is the direction to get to the error from the root and the
-- value that triggered the error
type Error a = ([ChildDir],a)
-- util to append a direction to an error
appendDir :: ChildDir -> Error a -> Error a
appendDir d (ds,x) = (d:ds,x)
transferVal :: (Ord a,Num a) => Tree a -> Either (Error a) (Tree (a,a))
transferVal = fmap fst . go 0
where go :: (Ord a,Num a) => a -> Tree a -> Either (Error a) (Tree (a,a),a)
go c (Leaf x) = let newC = x + c
in if newC < 0
then Left ([],newC)
else Right (Leaf (x,newC),newC)
go c (Branch t1 t2) = case go c t1 of
Left e -> Left $ appendDir LeftChild e
Right (newT1,newC) -> case go newC t2 of
Left e -> Left $ appendDir RightChild e
Right (newT2,newC') -> Right (Branch newT1 newT2,newC')
printErrorsOrTree :: (Show a,Show b) => Either (Error a) (Tree b) -> IO ()
printErrorsOrTree (Left (ds,x)) = putStrLn $ "Error in position " ++ (intercalate " -> " $ map show ds) ++ ". Error value is " ++ show x
printErrorsOrTree (Right t) = putStrLn $ "Result: " ++ show t
main :: IO ()
main = mapM_ runExample
[(Leaf 1)
,(Branch (Leaf 1) (Leaf 2))
,(Branch (Branch (Leaf 7) (Branch (Leaf (-1)) (Leaf 3))) (Branch (Leaf (-2)) (Leaf 9)))
,(Branch (Branch (Leaf 7) (Branch (Leaf (-11)) (Leaf 3))) (Branch (Leaf (-2)) (Leaf 9)))]
where runExample orig = do
let res = transferVal orig
print orig
printErrorsOrTree res
modulemain其中
导入数据列表(插入)
导入控制.Monad(mapM)
数据树a=叶a |分支(树a)(树a)
推导(显示,等式)
--给定一个分支,错误在哪个子级?
数据ChildDir=LeftChild | RightChild
衍生节目
--错误是从根目录和
--触发错误的值
类型错误a=([ChildDir],a)
--util将方向附加到错误
appendDir::ChildDir->错误a->错误a
附录d(ds,x)=(d:ds,x)
transferVal::(Ord a,Num a)=>树a->任一(错误a)(树a,a))
transferVal=fmap fst。0
去哪里::(Ord a,Num a)=>a->Tree a->Earth(错误a)(Tree(a,a),a)
去c(叶x)=让newC=x+c
如果newC<0,则为in
然后左([],newC)
else Right(叶(x,newC,newC)
go c(分支t1 t2)=案例go c t1
左e->左$appendDir左e
右(newT1,newC)->案例go newC t2
左e->左$appendDir右子e
右(newT2,newC')->右(分支newT1,newT2,newC'))
printErrorsOrTree::(显示a,显示b)=>任一(错误a)(树b)->IO()
printErrorsOrTree(左(ds,x))=putStrLn$“位置“++(插入“->”$map show ds)++”中的错误。错误值为“++显示x”
printErrorsOrTree(右t)=putStrLn$“结果:”++显示t
main::IO()
main=mapM_uu运行示例
[(第1页)
,(枝(叶1)(叶2))
,(枝(枝(叶7)(枝(叶(-1))(叶3)))(枝(叶(-2))(叶9)))
,(枝(枝(叶7)(枝(叶(-11))(叶3)))(枝(叶(-2))(叶9)))]
其中runExample orig=do
let res=转让原价
印刷原稿
打印错误树
通过使您的树
数据类型成为可遍历的实例,您可以使用mapM
(来自Data.Traversable
)在树
上映射操作。您还可以将StateT
monad转换器分层到任一
monad之上,以提供错误处理
import Control.Monad.State
import Control.Applicative
import Control.Monad.Error
import Data.Monoid
import Data.Foldable
import Data.Traversable
import qualified Data.Traversable as T
-- our monad which carries state but allows for errors with string message
type M s = StateT s (Either String)
data Tree a = Leaf a | Branch (Tree a) (Tree a)
deriving (Show,Eq)
-- Traversable requires Functor
instance Functor Tree where
fmap f (Leaf a) = Leaf (f a)
fmap f (Branch lhs rhs) = Branch (fmap f lhs) (fmap f rhs)
-- Traversable requires Foldable
instance Foldable Tree where
foldMap f (Leaf a) = f a
foldMap f (Branch lhs rhs) = foldMap f lhs `mappend` foldMap f rhs
-- Finally, we can get to Traversable
instance Traversable Tree where
traverse f (Leaf a) = Leaf <$> f a
traverse f (Branch lhs rhs) = Branch <$> traverse f lhs <*> traverse f rhs
testTree = (Branch
(Branch
(Leaf 7) (Branch (Leaf (-1)) (Leaf 3)))
(Branch
(Leaf (-2)) (Leaf (-20))))
numberTree :: (Num a, Ord a) => Tree a -> M a (Tree (a,a))
numberTree = T.mapM number where
number v = do
cur <- get
let nxt = cur+v
-- lift the error into the StateT layer
when (nxt < 0) $ throwError "state went negative"
put nxt
return (v, nxt)
main :: IO ()
main =
case evalStateT (numberTree testTree) 0 of
Left e -> putStrLn $ "Error: " ++ e
Right t -> putStrLn $ "Success: " ++ show t
import Control.Monad.State
导入控制
导入控制.Monad.Error
导入数据.幺半群
导入数据。可折叠
导入数据。可遍历
导入符合条件的数据。可作为T进行遍历
--我们的monad携带状态,但允许字符串消息出错
类型M s=状态T s(任一字符串)
数据树a=叶a |分支(树a)(树a)
推导(显示,等式)
--可遍历函子
实例函子树,其中
fmap f(叶a)=叶(叶a)
fmap f(左、右分支)=左、右分支(fmap f)(右分支)
--可遍历需要可折叠
实例可折叠树在哪里