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)) = ...