Sml 验证二叉树的大小?

Sml 验证二叉树的大小?,sml,smlnj,Sml,Smlnj,我有一个这样的数据类型 datatype 'a bin_tree = Leaf of 'a | Node of 'a bin_tree (* left tree *) * int (* size of left tree *) * int (* size of right tree *) * 'a bin_tree (* right tree *) 因此,正确树的示

我有一个这样的数据类型

datatype 'a bin_tree = 
    Leaf of 'a
  | Node of 'a bin_tree    (* left tree *)
           * int           (* size of left tree *)
           * int           (* size of right tree *)
           * 'a bin_tree   (* right tree *)
因此,正确树的示例如下:

val tree1 =
  Node(Node(Node(Leaf 47, 1, 1, Leaf 38),
            2,1,
            Leaf 55),
       3,2,
       Node(Leaf 27, 1, 1, Leaf 96))
违反树的一个例子是

val tree1false =
  Node(Node(Node(Leaf 47, 1, 1, Leaf 38),
            2,1,
            Leaf 55),
       4,2,
       Node(Leaf 27, 1, 1, Leaf 96))
我如何编写这样的谓词测试

- test tree1;
val it = true : bool
- test tree1false;
val it = false : bool

这是一个递归问题。在解决树上的递归问题之前,最好牢牢掌握列表上的递归。你可以说树是列表的泛化,或者列表是树的特例:列表有一个尾部,根据树的类型,树可以有任意数量的尾部。下面是如何使用列表重建和解决问题:

如果有一个列表,而不是典型的列表定义,该列表也会记录其自身的长度:

(* datatype 'a list = [] | :: of 'a * 'a list *)

datatype 'a lenlist = Nil | Cons of int * 'a * 'a lenlist
然后可以测试存储的长度是否与实际值的数量一致

首先,我将创建一个计数函数来说明函数中执行递归的部分:

(* For regular built-in lists *)
fun count0 [] = 0
  | count0 (x::xs) = 1 + count0 xs

(* Counting the memoized list type disregarding the n *)
fun count1 Nil = 0
  | count1 (Cons (n, x, xs)) = 1 + count1 xs
下一部分是,在每个递归步骤中,我想测试存储的数字
n
是否也与实际计数一致。此函数的返回类型是什么?嗯,你想要的
test
函数应该是
'a lenlist->bool
,我做的
count
函数是
'a lenlist->int

我建议你做一个
testcount
,这两者都可以。但您可以通过多种方式来实现这一点,例如,为其提供“额外参数”,或为其提供“额外返回值”。我将演示两者,只是为了说明有时其中一个比另一个更好,经验会告诉你哪一个更好

下面是一个
val testcount1:'a lenlist->bool*int
函数:

fun testcount1 Nil = (true, 0)
  | testcount1 (Cons (n, x, xs)) =
    let val (good_so_far, m) = testcount1 xs
        val still_good = good_so_far andalso n = m + 1
    in (still_good, m + 1)
    end

val goodList = Cons (4, #"c", Cons (3, #"o", Cons (2, #"o", Cons (1, #"l", Nil))))
val badList = Cons (3, #"d", Cons (2, #"e", Cons (1, #"r", Cons (0, #"p", Nil))))
测试这个,

- testcount1 goodList;
> val it = (true, 4) : bool * int
- testcount1 badList;
> val it = (false, 4) : bool * int
这表明
testcount1
返回数字是否相加以及列表的实际长度,这在递归过程中是必要的,以测试数字在每个步骤中是否相加,但最终不再需要。您可以将这个
testcount
函数封装在一个更简单的
test
函数中,该函数只关心
bool

fun test xs = #1 (testcount1 xs)
下面是另一种方法:
testcount1
递归的方式有一些不太令人满意的地方。它不断计算
m+1
s,即使它能够确定列表(例如,在
Cons(0,#“p”,Nil)
)被破坏

下面是另一个
val testcount2:'a lenlist*int->bool
,它在一个额外的参数中存储一个数字:

fun testcount2 (Nil, 0) = true
  | testcount2 (Nil, _) = false
  | testcount2 (Cons (n, x, xs), m) =
      n = m andalso testcount2 (xs, m - 1)
这对我来说似乎更为简洁:函数是尾部递归的,当它感觉到有可疑的东西时,它会立即停止。因此,如果它的头部被破坏,它不需要遍历整个列表。缺点是它需要知道长度,我们还不知道。但我们可以通过假设广告中的任何内容都是正确的来补偿,直到它明确地成为事实

测试此函数时,您不仅需要给它一个
goodList
badList
,还需要给它一个
m

- testcount2 (goodList, 4);
> val it = true : bool
- testcount2 (badList, 4);
> val it = false : bool
- testcount2 (badList, 3);
> val it = false : bool
重要的是,此函数不只是比较
n=m
,因为在
badList
中,这将导致同意
badList
有3个元素长,因为
n=m
对于所有
Cons
情况下的每次迭代都是正确的。这在要求我们达到
0
的两种
Nil
情况下有所帮助,而不是像
badList
那样的
~1

此函数也可以包装在
test
中,以隐藏这样一个事实,即我们向它提供了一个从
'a lenlist
本身派生的额外参数:

fun size Nil = 0
  | size (Cons (n, _, _)) = n

fun test xs = testcount2 (xs, size xs)
迄今为止的一些道德:

  • 有时需要创建帮助函数来解决初始问题
  • 这些helper函数不限于具有与您交付的函数相同的类型签名(无论是用于学校练习,还是用于外部API/库)
  • 有时,它有助于扩展函数返回的类型
  • 有时,它有助于扩展函数的参数
  • 仅仅因为您的任务是“编写一个函数
    foo->bar
    ”,这并不意味着您不能通过编写返回大大多于或少于
    foo
    bar
    的函数来创建它

现在,关于在二叉树上解决此问题的一些提示:

重复数据类型

datatype 'a bin_tree = 
    Leaf of 'a
  | Node of 'a bin_tree    (* left tree *)
           * int           (* size of left tree *)
           * int           (* size of right tree *)
           * 'a bin_tree   (* right tree *)
您可以根据上述想法为您的功能构建一个框架:

fun testcount3 (Leaf x, ...) = ...
  | testcount3 (Leaf x, ...) = ...
  | testcount3 (Node (left, leftC, rightC, right), ...) = ...
我在这里嵌入了som提示:

  • 您的解决方案可能应该包含针对
    叶x
    节点(左、左、右、右)
    的模式匹配。对于“额外参数”类型的解决方案(至少对于列表来说很好,但我们会看到),需要两个
    Leaf x
    案例。为什么会这样
  • 如果在列表的情况下,“额外参数”
    m
    表示列表的预期长度,那么在树的情况下,“额外参数”表示什么?你不能说“这是列表的长度”,因为它是一棵树。如何捕捉树的分支部分
  • 如果这仍然太难,考虑解决列表中的问题而不复制粘贴。也就是说,您可以查看此答案中的解决方案(但尽量不要),但不允许复制粘贴代码。如果你想复制它,就必须键入它
  • 首先,尝试定义辅助函数
    size
    ,该函数用于从
    testcount2
    生成
    test
    ,但用于树。因此,可以将其命名为
    sizeTree
    ,以避免名称重叠,但除此之外,请尝试使其类似。这里有一个骨架:

testcount3
sizeTree
粘在一起,一经编写,应该像tau一样简单。

到目前为止,您做了哪些尝试?
fun sizeTree (Leaf x) = ...
  | sizeTree (Node (left, leftC, rightC, right)) = ...