Haskell 如何定义状态单子?

Haskell 如何定义状态单子?,haskell,monads,Haskell,Monads,我想定义一个管理错误的状态单元组(在某种意义上类似于Maybe):如果在“do”计算过程中发生错误/问题,它是信号引导的,并通过>=传播。 错误还应该包含一个描述它的字符串。 之后,我想将此monad应用于mapTreeM,使用for map函数将状态假设为数字和包含数字的树,并在每个访问步骤通过添加当前叶的值来更新当前状态;生成的树必须包含一个具有旧叶值和访问瞬间状态的对。如果在计算过程中状态变为负值,则该访问必须失败,如果为正值,则访问必须成功 e、 g.给定此树:分支(分支(叶7)(分支(

我想定义一个管理错误的状态单元组(在某种意义上类似于Maybe):如果在“do”计算过程中发生错误/问题,它是信号引导的,并通过
>=
传播。 错误还应该包含一个描述它的字符串。 之后,我想将此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)(右分支)
--可遍历需要可折叠
实例可折叠树在哪里