函數式編程語言該如何表示樹結構呢?
比如在Erlang、Scala、Kotlin里通常是如何表示一個樹結構的呢?
慣用法是利用類似list這樣的類型嗎?
在函數式語言中,樹之類的遞歸數據結構大概有這些表示方法:
- 用普通的algebraic data type遞歸地表示。如果語言支持GADT甚至inductive families的話,可以在類型里表達更強的性質來約束樹結構,比如規定所有子樹等高之類
- 用某個base functor的不動點表示(Data types à la carte )
- 用衍生自某個functor的free monad表示,比如list的free monad用來表示rose tree
- 不使用data type,只使用普通的函數來表示,也就是所謂的encoding。常見的有Mogensen-Scott encoding或者Boehm-Berarducci encoding
歸根到底,你只需要兩樣東西:1. 構造一棵樹的方法 2. 在這棵樹上進行某種運算的方法。以上手段都可滿足
另外值得一提的是:函數式語言里的樹是immutable的,修改特定節點生成新樹的原理是path copying,也就是從根節點到目標節點路徑上的節點都會被拷貝,而不是原地賦值修改,這是很多函數式數據結構上的操作O(log n)時間複雜度的來源。但是,我們還有一個叫做zipper的好東西!zipper是指向樹上某個節點的類似iterator一樣的東西。可以在O(1)的時間內實現:- 上下左右移動
- 修改當前指向的節點
- 取回以指向節點為根的子樹,或者從某個樹的根節點轉化而來
給定一個遞歸的樹定義,可以用很簡單的方法計算出它的zipper定義和各項操作的實現。這是一個非常實用的函數式語言里的樹結構相關的數據結構。
詳細的代碼和引用真的沒空貼了,又是好幾篇專欄的事。。等下周考完期中慢慢折騰。。https://www.coursera.org/learn/progfun1/programming/Ogg05/object-oriented-sets
參見 Purely Functional Data Structure
咦, 好巧, 前幾個月做過一題樹結構的題, 代碼還在~如圖, 我們要實現這樣一個樹結構, 每個節點有它自己的值. 我們需要達到這些功能:
- - 修改當前節點的值;
- - 往一個節點的左邊, 右邊添加節點;
- - 為一個節點添加子節點;
- - 訪問左, 右, 父, 子;
- - 刪除當前節點以及它的孩子;
初看有些複雜, 但代碼很簡單. 首先, 我們採訪了根節點, 「請問根先森, 你姓胡嗎? 噢,不, 我是說, 您叫什麼名? 家裡幾個娃? 鄰居都有誰? 鄰居幾個娃? 祖宗都叫啥? 鄰居的祖宗….」, 然後我們就得到了這樣一張小卡片:
現在我們就可以開始實現需要的功能:修改當前值: def replace({t, {l, [{_val, children}|r]}}, val), do:
{t, {l, [{val,children}|r]}}
def insert_left({thread, {l, r}}, val), do:
{thread, {[{val, {[], []}}|l], r}}
def insert_right({thread, {l, [h|t]}}, val), do:
{thread, {l, [h|[{val, {[], []}}|t]]}}
def insert_child({t, {l, [{v, {left, right}}|r]}}, val), do:
{t, {l, [{v, {left, [{val, {[], []}}|right]}}|r]}}
def left({thread, {[h|t], r}}), do:
{thread, {t, [h|r]}}
def right({thread, {l, [h|t]}}), do:
{thread, {[h|l], t}}
def children({thread, {l, [{val, children}|r]}}), do:
{[{l,[val|r]}|thread], children}
def parent({[{parentL, [val|parentR]}|thread], {l, r}}), do:
{thread, {parentL, [{val, {[], Enum.reverse(l)++r}}|parentR]}}
def delete({thread, {l, [_|r]}}), do:
{thread, {l, r}} |&> parent
這裡的`|&>`是elixir的管道符, 表示將左邊的結果作為右邊函數的第一個參數來執行.
完成: ) ferd的一篇文章有專門講到這些 ferd.ca -&> Yet Another Article on Zippers, in Erlang謝邀
可以看SICP:Another way to implement scale-tree is to regard the tree as a sequence of sub-trees and use map. We map over the sequence, scaling each sub-tree in turn, and return the list of results. In the base case, where the tree is a leaf, we simply multiply by the factor:
Many tree operations can be implemented by similar combinations of sequence operations and recursion.
(define (scale-tree tree factor)
(map (lambda (sub-tree)
(if (pair? sub-tree)
(scale-tree sub-tree factor)
(* sub-tree factor)))
tree))
其實就是List of List
二叉樹在有代數數據類型(Algebraic data type)的語言可以這樣來表達 (Haskell)data Tree a = Empty
| Branch a (Tree a) (Tree a)
換C裡面就是Tagged union
更加"廣泛"的樹可以叫Rose treedata RoseTree a = RoseTree a [RoseTree a]
BTW, 函數式數據結構入門的可以看 ML程序設計教程 (豆瓣)
「樹」由節點組成,每個節點包含多個值,其中一個值是這個節點保存的值(value)。除了value,每個節點還需要0個到多個子樹。
有些函數式語言允許定義新的類型,為了表達「樹」這個結構,我們需要定義一個新的數據類型,tree。
函數式編程語言有一個數據結構叫list,即一連串同類型的數據(和array很像)。我們可以基於list來定義tree。以二叉樹為例,即:(value,left,right)(* 定義一個新類型,名為「tree」 *)
(* 該類型有兩個constructor:Br開頭的生成內部節點;Lf生成葉子節點。 *)
(* 內部節點的結構是(value,left,right) *)
(* 葉子節點表示null *)
(* 『a是這種樹的節點可以存儲的數據類型的佔位符 *)
type "a tree =
Br of "a * "a tree * "a tree
| Lf
(* 定義一個新函數來求一棵樹的非葉子節點的個數 *)
(* 「rec」表示這個函數存在遞歸 *)
(* 函數內部對參數tree進行pattern matching,根據tree類型的定義,傳入的參數要麼是內部節點,即Br (v,left,right)的形式;要麼是葉子節點。 *)
(* 如果是內部節點,那麼值為左右子樹非葉子節點個數的和加一(加上該節點) *)
(* 如果是葉子節點,那麼不計算,直接返回0 *)
let rec size tree =
match tree with
Br (v, left, right) -&> 1 + size left + size right
| Lf -&> 0
謝邀,我google給您:
https://www.google.com/webhp?sourceid=chrome-instantion=1espv=2ie=UTF-8#q=erlang%20tree
https://www.google.com/webhp?sourceid=chrome-instantion=1espv=2ie=UTF-8#q=scala%20tree
https://www.google.com/webhp?sourceid=chrome-instantion=1espv=2ie=UTF-8#newwindow=1q=kotlin+tree
親請慢用哦。只有葉子節點帶數據:data Tree a = Leaf a | Node (Tree a) (Tree a)都帶:data Tree a = Leaf a | Node a (Tree a) (Tree a)
可以搜Recursive data type
謝邀~
&<感覺大家都好學術啊哈哈&>
樹的結構,我比較喜歡在實踐中定義 Node 結構,按需求對子節點進行引用,有點兒類似鏈表的感覺。就是類似下面的寫法吧,這個在 Leetcode 的題目中也好常見。 data class Node(val value: Int, var left: Node?, var right: Node?)推薦閱讀:
※函數式編程所倡導使用的「不可變數據結構」如何保證性能?
※每一個 Haskell 中的「範疇論的」概念都可以去 co 嗎?
※Haskell的Lens是一個怎樣的庫?
※如何解釋 Lisp 中 call/cc 的概念?
※如何向一個只有命令式編程背景的人解釋函數式編程中的函數?