@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
聲明前的源文件頂部。
確實沒有理由讓insert2的第二個參數是'Either String(BST2 a)','BST2 a'就足夠了! – adamse
'combine'就是'liftM2。節點2' – nponeccop