笔记
70A. Tree Construction From a Node String
A multiway tree is composed of a root element and a (possibly empty) set of successors which are multiway trees themselves. A multiway tree is never empty. The set of successor trees is sometimes called a forest.
To represent multiway trees, we will use the following type which is a direct translation of the definition:
# type 'a mult_tree = T of 'a * 'a mult_tree list;; type 'a mult_tree = T of 'a * 'a mult_tree listThe example tree depicted opposite is therefore represented by the following OCaml expression:
# T ('a', [T ('f', [T ('g', [])]); T ('c', []); T ('b', [T ('d', []); T ('e', [])])]);; - : char mult_tree = T ('a', [T ('f', [T ('g', [])]); T ('c', []); T ('b', [T ('d', []); T ('e', [])])])We suppose that the nodes of a multiway tree contain single characters. In the depth-first order sequence of its nodes, a special character
^has been inserted whenever, during the tree traversal, the move is a backtrack to the previous level.By this rule, the tree in the figure opposite is represented as:
afg^^c^bd^e^^^.Write functions
string_of_tree : char mult_tree -> stringto construct the string representing the tree andtree_of_string : string -> char mult_treeto construct the tree when the string is given.# let t = T ('a', [T ('f', [T ('g', [])]); T ('c', []); T ('b', [T ('d', []); T ('e', [])])]);; val t : char mult_tree = T ('a', [T ('f', [T ('g', [])]); T ('c', []); T ('b', [T ('d', []); T ('e', [])])])
来了来了。巧妙的是它是用一个多余 ^ 来表明自己的结束的,就像是自己的根上层还有父一样,正好符合递归了。
let rec string_of_tree root =
let rec string_of_l l = match l with
| [] -> ""
| h::t -> string_of_tree h ^ (string_of_l t)
in
match root with
| T (v, sons) -> String.make 1 v ^ string_of_l sons ^ "^";;
val string_of_tree : char mult_tree -> string = <fun>
这个递归有点麻烦哦。呃呃呃,好像没法搞了,我这两个内部函数互相调用,但 OCaml 又不是那种申明式的,编译都无法通过吧?
完了啊,噶了,这个递归这么麻烦,我写了这么久。这下寄了。
癌,有了有了,查了下文档发现可以用 and 这样写。以前的标准答案也用过啊,傻了。
let rec tree_of_string s =
let eat_one str = Str.string_after str 1 in
let rec aux remain = let (sonss, remain1) = sons (eat_one remain) in
(T(remain.[0], sonss), remain1)
and
sons remain = if remain.[0]='^' then ([], eat_one remain) else
let (bro, remain1)=aux remain in
let (other_bros, remain2) = sons remain1 in
(bro::other_bros, remain2)
in
aux s;;
val tree_of_string : string -> char mult_tree * string = <fun>
咋回事呢,你把你 tree_of_string 给我交了。
又用个 buffer 是吧。
# (* We could build the final string by string concatenation but this is expensive due to the number of operations. We use a buffer instead. *) let rec add_string_of_tree buf (T (c, sub)) = Buffer.add_char buf c; List.iter (add_string_of_tree buf) sub; Buffer.add_char buf '^' let string_of_tree t = let buf = Buffer.create 128 in add_string_of_tree buf t; Buffer.contents buf;; val add_string_of_tree : Buffer.t -> char mult_tree -> unit = <fun> val string_of_tree : char mult_tree -> string = <fun>
70B. Count the Nodes of a Multiway Tree
# count_nodes (T ('a', [T ('f', []) ]));; - : int = 2
泪目了,简单的来了,和回家了一样安心。
我发现多叉树的总是要有这种复合互相调用的递归,神秘哦。
let rec count_nodes (T(v, sons)) =
let rec aux l = match l with
| [] -> 0
| h::t -> count_nodes h + aux t
in
1 + aux sons;;
val count_nodes : 'a mult_tree -> int = <fun>
还得是你啊,fold_left,学,好吧,学。
# let rec count_nodes (T (_, sub)) = List.fold_left (fun n t -> n + count_nodes t) 1 sub;; val count_nodes : 'a mult_tree -> int = <fun>
71. Determine the Internal Path Length of a Tree
We define the internal path length of a multiway tree as the total sum of the path lengths from the root to all nodes of the tree. By this definition, the tree
tin the figure of the previous problem has an internal path length of 9. Write a functionipl treethat returns the internal path length oftree.# ipl t;; - : int = 9
let ipl root =
let rec aux (T(v,sons)) depth = match sons with
| [] -> depth
| l -> List.fold_left (fun n t -> n + aux t (depth+1)) depth sons
in
aux root 0;;
val ipl : 'a mult_tree -> int = <fun>
哦,不用 match 的,fold_left 自然能处理 [] 的情况。
# let rec ipl_sub len (T(_, sub)) = (* [len] is the distance of the current node to the root. Add the distance of all sub-nodes. *) List.fold_left (fun sum t -> sum + ipl_sub (len + 1) t) len sub let ipl t = ipl_sub 0 t;; val ipl_sub : int -> 'a mult_tree -> int = <fun> val ipl : 'a mult_tree -> int = <fun>
72. Construct the Bottom-Up Order Sequence of the Tree Nodes
Write a function
bottom_up twhich constructs the bottom-up sequence of the nodes of the multiway treet.# bottom_up (T ('a', [T ('b', [])]));; - : char list = ['b'; 'a'] # bottom_up t;; - : char list = ['g'; 'f'; 'c'; 'd'; 'e'; 'b'; 'a']
效率很差,用了太多次 list 连接。应该能改的好点的,传 acc 下去,List.rev 什么的。
let rec bottom_up (T(v,sons)) =
let son_str = List.fold_left (fun n t -> n @ bottom_up t) [] sons in
son_str @ [v];;
val bottom_up : 'a mult_tree -> 'a list = <fun>
嗯?fold_right 吗。。。还是你牛。
# let rec prepend_bottom_up (T (c, sub)) l = List.fold_right (fun t l -> prepend_bottom_up t l) sub (c :: l) let bottom_up t = prepend_bottom_up t [];; val prepend_bottom_up : 'a mult_tree -> 'a list -> 'a list = <fun> val bottom_up : 'a mult_tree -> 'a list = <fun>
73. Lisp-Like Tree Representation
There is a particular notation for multiway trees in Lisp. The picture shows how multiway tree structures are represented in Lisp.
Note that in the “lispy” notation a node with successors (children) in the tree is always the first element in a list, followed by its children. The “lispy” representation of a multiway tree is a sequence of atoms and parentheses ‘(‘ and ‘)’. This is very close to the way trees are represented in OCaml, except that no constructor
Tis used. Write a functionlispy : char mult_tree -> stringthat returns the lispy notation of the tree.# lispy (T ('a', []));; - : string = "a" # lispy (T ('a', [T ('b', [])]));; - : string = "(a b)" # lispy t;; - : string = "(a (f g) c (b d e))"
lisp 是这么表达树的吗,很神秘哦。
let rec lispy (T(v,sons)) = match sons with
| [] -> String.make 1 v
| l -> "(" ^ (String.make 1 v) ^ (List.fold_left (fun n t-> n ^ " " ^ lispy t) "" l) ^ ")";;
val lispy : char mult_tree -> string = <fun>
又玩你的 buffer 是吧。哦,可以 iter,因为没有 fold 的初始值,差不多吧。
# let rec add_lispy buf = function | T(c, []) -> Buffer.add_char buf c | T(c, sub) -> Buffer.add_char buf '('; Buffer.add_char buf c; List.iter (fun t -> Buffer.add_char buf ' '; add_lispy buf t) sub; Buffer.add_char buf ')' let lispy t = let buf = Buffer.create 128 in add_lispy buf t; Buffer.contents buf;; val add_lispy : Buffer.t -> char mult_tree -> unit = <fun> val lispy : char mult_tree -> string = <fun>
总结
多路树和二叉树不同比想象的大哦,是个 list 递归的时候麻烦一点。
文档信息
- 本文作者:nyaaar
- 本文链接:https://nyaaarlathotep.github.io/2024/09/13/Ocaml-99-%E9%97%AE%E9%A2%98%E7%AC%94%E8%AE%B0-5/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)

