Sml折叠一棵树

时间:2016-09-22 00:37:49

标签: function tree sml fold

到目前为止,我正试图通过使用fold函数来获取树的产品。我对如何在横向树上使用折叠方法感到困惑

datatype 'a bin_tree = Leaf of 'a 
| Node of 'a bin_tree * 'a bin_tree


fun treefold g z Empty = z
| treefold g z (Node (l, x, r)) = g(x, g(treefold g z l, treefold g z r)

2 个答案:

答案 0 :(得分:2)

首先,一些关于你尝试的内容不完整的指示。

  1. treefold函数的基本大小写与值构造函数Empty匹配,但您没有定义bin_tree数据类型以包含Empty值构造函数。< / LI>
  2. 正如John Coleman在评论中指出的那样,你已经定义了Node值构造函数来成对,但在递归的情况下,你将Node与三元组匹配。考虑到Simon Shine在二叉树上解释折叠的优秀答案,我们可以推断出这个原因:你的treefold函数需要一个规范的二叉树(每个节点都有一个值和两个分支)但数据结构你已定义不实现此结构。我不确定你所定义的数据结构是什么,尽管给了它一个好的谷歌(虽然我认为我之前已经实现了它!)。
  3. 您将g应用于z两次,一次向左分支,一次向右分支。这意味着传递到该位置的值将被包含在每个分支的折叠中两次。我不认为这就是你的意图。
  4. 一旦您尝试编译或加载代码,

    (1)和(2)都应该由类型检查器捕获。您应该尝试这个,如果您还没有,并确保您了解类型检查器提供的反馈。这是一个非常有价值的工具。

    (3)与您打算编写的函数的性质有关,如果您不理解它,它只会出现故障。

    这是定义您定义的数据结构的折叠的一种方法。

    structure Tree =
    struct
        datatype 'a tree = Leaf of 'a
                         | Node of 'a tree * 'a tree
    
        fun foldl f x t =
          case t
           of Leaf y => f (x, y)              (* The base case *)
            | Node (treeL, treeR) =>
              let
                  val l = foldl f x treeL     (* Recurse down the left branch *)
              in
                  foldl f l treeR             (* Recurse down the right branch *)
              end
    end
    

    请注意,将foldl放在Tree模块中,我们现在如何使用一个函数来反映foldl结构中的List函数(以及其他地方) :

    - List.foldl;
    val it = fn : ('a * 'b -> 'b) -> 'b -> 'a list -> 'b
    - Tree.foldl;
    val it = fn : ('a * 'b -> 'a) -> 'a -> 'b bin_tree -> 'a
    

    这样可以更容易地对列表或树进行参数化。

    它的工作原理如下:

    - foldl op+ 0 (Node (Leaf 3, Node (Node (Leaf 1, Leaf 2), Leaf 5)));
    val it = 11 : int
    

答案 1 :(得分:2)

折叠二叉树时,

datatype 'a tree = Leaf | Branch of 'a tree * 'a * 'a tree

你可以在different ways中遍历它。你有共同的策略,

(* Pre-order *)
fun treefold_preorder f acc1 Leaf = acc1
  | treefold_preorder f acc1 (Branch (left, a, right)) =
    let val acc2 = treefold_preorder f acc1 left
        val acc3 = f (a, acc2)
        val acc4 = treefold_preorder f acc3 right
    in acc4 end

(* In-order *)
and treefold_inorder f acc1 Leaf = acc1
  | treefold_inorder f acc1 (Branch (left, a, right)) =
    let val acc2 = f (a, acc1)
        val acc3 = treefold_inorder f acc2 left
        val acc4 = treefold_inorder f acc3 right
    in acc4 end

(* Post-order *)
and treefold_postorder f acc1 Leaf = acc1
  | treefold_postorder f acc1 (Branch (left, a, right)) =
    let val acc2 = treefold_postorder f acc1 left
        val acc3 = treefold_postorder f acc2 right
        val acc4 = f (a, acc3)
    in acc4 end

维基百科很好地说明了,

Source: Wikipedia's Tree traversal article

用法

val treelist = treefold op:: []
val treeproduct = treefold op* 1
val treecount = treefold (fn (_, count) => count + 1) 0

附加

如果每个分支/节点都没有&#39; 值,则有序遍历并不重要。

另请参阅如何应用tail-recursion on trees以避免堆栈溢出。

对于涉及树遍历的一些问题,提供遍历的上下文可能很有用paramorphisms

fun treecata_preorder f acc1 Leaf = acc1
  | treecata_preorder f acc1 (branch as Branch (left, a, right)) =
    let val acc2 = treecata_preorder f acc1 left
        val acc3 = f (a, branch, acc2)
        val acc4 = treecata_preorder f acc3 right
    in acc4 end

这是treefold_preorder的略微概括,其中f被提供给整个branch

这可以让你举例如在祖先树中查找谓词为其子树保留的人,

fun treefilter pred =
    treecata_preorder (fn (x, xtree, acc) => if pred xtree then x::acc else acc) []

fun branchValue Leaf = NONE
  | branchValue (Branch (_, value, _)) = SOME value

fun parents Leaf = []
  | parents (Branch (left, _, right)) =
    List.mapPartial (fn xopt => xopt) [branchValue left, branchValue right]

type name = string
type age = int
datatype person = Person of name * age

fun retired (Person (_, age)) = age >= 70
fun hasRetiredParent tree = List.exists retired (parents tree)
val personsWithRetiredParents = treefilter hasRetiredParent

树遍历的另一个巧妙概念是zippers(LYAH章节)。