有点棘手!这是一种非常好的机制,可以在最后一刻使用(==)比较值,并且只有在必要的情况下才会进行比较。但是,为什么您至少没有使用类型信息对其进行注释呢?
data Tree a = E | T (Tree a) a (Tree a)
insert :: (Ord a) => a -> Tree a -> Tree a
insert x t = const t `either` id $ insert' x t Nothing
where
insert' :: (Ord a) => a -> Tree a -> Maybe a -> Either (Tree a) (Tree a)
insert' x E Nothing = Right (T E x E)
insert' x E (Just v) | x==v = Left E
| otherwise = Right (T E x E)
insert' x t@(T l v r) _ | x<v = (\l' -> T l' v r) `fmap` insert' x l Nothing
| otherwise = (\r' -> T l v r') `fmap` insert' x r (Just v)
为什么你要使用Either,如果你丢弃了Left的情况然后使用复制?如果不保留该复制以替换相等的树,而是根本不构建相等的树,那么效率会更高。就像这样...
insert' :: (Ord a) => a -> Tree a -> Maybe a -> Maybe (Tree a)
如果你想要真正高效,那么不要为了之后的比较而构建(或许是)一个参数。
insert'1 :: (Ord a) => a -> Tree a -> Maybe (Tree a)
insert'2 :: (Ord a) => a -> Tree a -> a -> Maybe (Tree a)
解决方案如下所示:
insert :: (Ord a) => a -> Tree a -> Tree a
insert x t = fromMaybe t $ insert'1 x t
where
insert'1 :: (Ord a) => a -> Tree a -> Maybe (Tree a)
insert'2 :: (Ord a) => a -> Tree a -> a -> Maybe (Tree a)
insert'1 x E = Just (T E x E)
insert'1 x (T l v r) | x<v = do l' <- insert'1 x l
Just (T l' v r)
| otherwise = do r' <- insert'2 x r
Just (T l v r')
insert'2 x E v = guard (x/=v) >> Just (T E x E)
insert'2 x t _ = insert'1 x t
在Control.Monad.Error中定义了以下实例:
(EDIT:)
Error e => MonadError e (Either e)
那就意味着,(Either String) 可能是你正在寻找的内容。
insert :: (Ord a,MonadError String m) => a -> Tree a -> m (Tree a)
insert x t = maybe (throwError "Error: element already in tree") return $ insert'1 x t
where ...
Left
用于错误情况,那么你就无法使Either
成为一个函子(functor)、单子(monad)等,这才是更重要的;这不仅仅涉及到Right
的两个含义。 - Antal Spector-Zabusky