的確,您可以給Tree一個類型參數,如Alexander Poluektov的示例。夠簡單!但爲什麼要在那裏停下我們可以有更多的樂趣。而不是遞歸結構多態數據,您可以使結構多態在遞歸本身!
首先,抽象掉樹對自身的引用,與抽象出Int
的引用相同,用新參數t
替換遞歸引用。這給我們留下了這個相當含糊的數據結構:
data TNode t a = Empty
| Leaf a
| Node (t a) a (t a)
deriving (Eq, Ord, Show, Read)
這已被更名爲TNode
這裏,因爲它是不是一個真正的樹了;只是一個簡單的數據類型。現在,爲了恢復原始遞歸和創建樹,我們擰TNode
周圍,飼料它本身:
newtype Tree a = Tree (TNode Tree a) deriving (Eq, Ord, Show, Read)
現在我們可以使用這個Tree
遞歸,但可悲的是,在一些額外的空話的成本,就像這樣:
Tree (Node (Tree Empty) 5 (Tree (Leaf 2)))
那麼,除了額外的打字,你會問這是什麼給我們的?簡單地說,我們已經將基本樹結構與它所包含的數據以及數據構造和處理的方法分開,從而允許我們編寫更通用的函數來處理某個方面。例如,我們可以用額外的數據修飾樹,或者將額外的東西拼接到樹中,而不影響任何通用樹函數。假設我們想給一個名字在每件樹:
newtype NameTree a = NameTree (String, TNode NameTree a) deriving (Eq, Ord, Show, Read)
在另一方面,我們可以編寫通用的樹遍歷邏輯:
toList f t = toList' f (f t) []
where toList' f (Node t1 x t2) xs = toList' f (f t1) (x : toList' f (f t2) xs)
toList' f (Leaf x) xs = x:xs
toList' _ Empty xs = xs
給出一個函數來提取當前TNode
從遞歸樹,我們可以在任何這樣的結構使用:
treeToList = toList (\(Tree t) -> t)
nameTreeToList = toList (\(NameTree (_, t)) -> t)
當然,這可能遠遠超過你在找什麼做的,但它只是多少味道不錯多態性和泛型代碼Haskell允許(不鼓勵)程序員創建。