不完全確定你在問什麼。但是,是的,你提供TreeAlgebra
到foldTree
對應於你想在樹上執行的計算。例如,要總結在樹中的所有元素的Int
是你可以使用這個代數:
sumAlgebra :: TreeAlgebra Int Int
sumAlgebra = TreeAlgebra { leaf = id
, branch = (+) }
這意味着,要獲得葉的總和,適用id
(什麼都不做)的值在葉。爲了得到一個分支的總和,加上每個孩子的總和。
事實上,我們可以說分支(+)
而不是,例如,\x y -> sumTree x + sumTree y
是變質的基本屬性。它說爲了計算一些函數f
在某些遞歸數據結構上,只需要爲它的直接子對象賦值f
即可。
Haskell是一個非常獨特的語言,我們可以抽象地形式化變形的想法。讓我們爲您的樹中的單個節點的數據類型,參數超過其孩子:
data TreeNode a child
= Leaf a
| Branch child child
見我們去做什麼了?我們用我們選擇的類型替換了遞歸子代。這樣我們可以在摺疊時將子樹的總和放在那裏。
現在的真正神奇的事情。我將在pseudohaskell中編寫它 - 將它編寫成真正的Haskell是可能的,但我們必須添加一些註釋來幫助類型分析器,這可能會讓人困惑。我們採用參數化數據類型的「固定點」 - 也就是構造一個數據類型T
,例如T = TreeNode a T
。他們稱這個運營商爲Mu
。
type Mu f = f (Mu f)
請仔細看看這裏。 Mu
的參數不是類型,如Int
或Foo -> Bar
。這是一個類型構造函數像Maybe
或TreeNode Int
- Mu
本身的參數需要一個參數。 (對類型構造函數進行抽象化的可能性是Haskell的類型系統在其表現力上真正脫穎而出的原因之一)。
所以Mu f
這個類型被定義爲取f
並用Mu f
本身填充它的類型參數。我要定義一個同義詞,以減少部分噪音:
type IntNode = TreeNode Int
擴大Mu IntNode
,我們得到:
Mu IntNode = IntNode (Mu IntNode)
= Leaf Int | Branch (Mu IntNode) (Mu IntNode)
你怎麼看Mu IntNode
相當於你Tree Int
?我們剛剛將遞歸結構分開,然後使用Mu
將其重新組合。這給了我們優勢,我們可以一次談論所有類型的Mu
。這給了我們我們需要定義一個變形。
讓我們來定義:
type IntTree = Mu IntNode
我說catamorphism的本質屬性是計算一些功能f
,只須有f
的值,它的直接孩子。我們稱之爲我們試圖計算的東西的類型r
,並且數據結構node
(IntNode
將是這種可能的實例)。因此,要計算特定節點上的r
,我們需要將其子節點替換爲r
的節點。該計算的類型爲node r -> r
。因此,一個catamorphism說,如果我們有這些計算中的一個,那麼我們可以計算r
整個遞歸結構(記住遞歸表示明確這裏Mu
):
cata :: (node r -> r) -> Mu node -> r
使這一具體在我們的例子,這看起來像:
cata :: (IntNode r -> r) -> IntTree -> r
重申,如果我們可以採取一個節點與r
S表示它的孩子和計算的r
,那麼我們可以計算一個r
爲整個樹。
爲了實際計算這個,我們需要node
爲Functor
- 這是我們需要能夠在節點的子節點上映射任意函數。
fmap :: (a -> b) -> node a -> node b
對於IntNode
,這可以直接完成。現在
fmap f (Leaf x) = Leaf x -- has no children, so stays the same
fmap f (Branch l r) = Branch (f l) (f r) -- apply function to each child
,最後,我們可以給一個定義cata
(在Functor node
約束只是說,node
具有合適的fmap
):
cata :: (Functor node) => (node r -> r) -> Mu node -> r
cata f t = f (fmap (cata f) t)
我使用的參數名稱t
用於助記符「樹」的價值。這是一個抽象的,密集的定義,但它非常簡單。它說:遞歸地執行cata f
- 我們在樹上進行的計算 - 在t
的每個孩子(它們本身Mu node
s)上得到node r
,然後將結果傳遞給f
計算t
本身的結果。
將此回溯到一開始,您定義的代數本質上是一種定義node r -> r
函數的方法。事實上,由於一個TreeAlgebra
,我們可以很容易地得到摺疊功能:
foldFunction :: TreeAlgebra a r -> (TreeNode a r -> r)
foldFunction alg (Leaf a) = leaf alg a
foldFunction alg (Branch l r) = branch alg l r
因此樹catamorphism可以在我們的一個通用的術語來定義如下:
type Tree a = Mu (TreeNode a)
treeCata :: TreeAlgebra a r -> (Tree a -> r)
treeCata alg = cata (foldFunction alg)
我出來的時候。我知道真的很抽象,但我希望它至少能給你一個新的觀點來幫助你學習。祝你好運!
無關注:標籤「catamporphisms」拼寫錯誤;它有一個額外的'p'。顯然,我不夠酷,無法編輯,因爲這將構成一個新的標籤。 (耶穌哭了) – 2010-12-13 23:10:38
@Derrick Turk:這個標籤只有三個問題。把它們全部重新標記並不難。 – fuz 2010-12-14 00:32:45
@FUZxxl:顯然你需要1500個聲望來創建新的標籤,而在那個時候「變形」還不存在。 – ephemient 2010-12-14 01:04:26