假设有一个二叉搜索树,我想在我们尝试插入已经存在的元素时返回错误。有没有办法使这项工作?
data BST2 a = EmptyBST2 | Node2 a (BST2 a) (BST2 a) deriving Show
insert2 :: a -> Either b (BST2 a) -> Either b (BST2 a)
insert2 elem (Right EmptyBST2) = Right (Node2 elem EmptyBST2 EmptyBST2)
insert2 elem (Right (Node2 root left right))
| (elem == root) = Left "Error: Element already exist."
| (elem < root) = (Node2 root (insert2 elem left) right)
| otherwise = (Node2 root left (insert2 elem right))
注意:我是Haskell的新手。
答案 0 :(得分:5)
@Andre只是试图为您的代码提供最小的修复。在Haskell中实现错误处理任务的惯用方法是使用Error
monad。主要原因是可能重用liftM2
库函数来实现combine
。 throwError
和return
可以替换为Left
和Right
,但通用函数可以更清楚地说明代码的用途。
module Err where
import Control.Monad (liftM2)
import Control.Monad.Error (throwError)
data BST2 a = EmptyBST2 | Node2 a (BST2 a) (BST2 a) deriving Show
combine root = liftM2 (Node2 root)
insert2 :: (Ord a) => a -> BST2 a -> Either String (BST2 a)
insert2 elem EmptyBST2 = return $ Node2 elem EmptyBST2 EmptyBST2
insert2 elem (Node2 root left right)
| (elem == root) = throwError "insert2 error: Element already exists."
| (elem < root) = combine root (insert2 elem left) (return right)
| otherwise = combine root (return left) (insert2 elem right)
请注意,combine
可以更短:combine = liftM2 . Node2
或更长:combine root left right = liftM2 (Node2 root) left right
。使用您最了解的风格。
还有一些关于错误@Andre修复的评论:
insert2
在错误类型中不是多态的 - 如果失败,它总是返回String
。所以他在类型声明中使用String
而不是b
。Ord a =>
约束,以表明必须为该类型实施<
和==
。insert2
返回Either
。您尝试将Left
或Right
传递给Node2
,Node2 root (Left foo) right
失败,因为它预计会Node2 a
,但会提供Either String (Node2 a)
。最后,使用throwError
和return
的另一个原因是该函数变得通用:
insert2 :: (Ord a, MonadError String m) => a -> BST2 a -> m (BST2 a)
并且您可以将其用于除MonadError
之外的Either
个实例,但您需要在{-# LANGUAGE FlexibleContexts #-}
声明之前在源文件的顶部添加module
pragma
答案 1 :(得分:3)
快速解决方案(不一定简单):
data BST2 a = EmptyBST2 | Node2 a (BST2 a) (BST2 a) deriving Show
combine :: a -> Either b (BST2 a) -> Either b (BST2 a) -> Either b (BST2 a)
combine a (Left b) _ = Left b
combine a _ (Left b) = Left b
combine a (Right left_subtree) (Right right_subtree) = Right (Node2 a left_subtree right_subtree)
insert2 :: (Ord a) => a -> Either String (BST2 a) -> Either String (BST2 a)
insert2 elem (Right EmptyBST2) = Right (Node2 elem EmptyBST2 EmptyBST2)
insert2 elem (Right (Node2 root left right))
| (elem == root) = Left "Error: Element already exist."
| (elem < root) = combine root (insert2 elem (Right left)) (Right right)
| otherwise = combine root (Right left) (insert2 elem (Right right))
-- test data
t1 = EmptyBST2
t2 = Node2 17 t1 t1
t3 = Node2 42 t2 t1
t4 = insert2 11 (Right t3)
t5 = insert2 17 (Right t3)
答案 2 :(得分:2)
使用辅助函数解决此问题更简单:
insert2 :: (Ord a) => a -> BST2 a -> Either String (BST2 a)
insert2 newVal tree
| contains newVal tree = Left "Error: element already in tree"
| otherwise = Right $ insert newVal tree
现在您需要contains
和insert
:
contains :: (Ord a) => a -> BST2 a -> Bool
contains .... implementation .... -- checks whether a BST2 contains an element
insert :: (Ord a) => a -> BST2 a -> BST2 a
insert .... implementation .... -- inserts an element if not already there,
-- otherwise returns original tree
或者,您可以使用Haskell's many other approaches之一来代替Either String (BST2 a)
而不是{{1}}。