问题描述
我以这种方式具有数据类型
config.py
因此,正确的树的示例为:
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,Leaf 38),2,Leaf 55),3,Node(Leaf 27,Leaf 96))
如何编写这样的谓词测试
val tree1false =
Node(Node(Node(Leaf 47,4,Leaf 96))
解决方法
这是一个递归问题。在解决树上的递归问题之前,牢牢掌握列表上的递归是一个好主意。您可以说树是列表的概括,或者列表是树的特殊情况:列表只有一条尾巴,树可以有任意数量的尾巴,具体取决于树的类型。因此,您可以使用列表来重建和解决问题:
如果您有一个列表也可以记住其自身的长度,而不是典型的列表定义:
(* 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,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,Cons (1,#"l",Nil))))
val badList = Cons (3,#"d",#"e",#"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
处的列表已损坏,它也会继续计算Cons (0,Nil)
。
这里是备用val testcount2 : 'a lenlist * int -> bool
,它在一个额外的参数中存储数字:
fun testcount2 (Nil,0) = true
| testcount2 (Nil,_) = false
| testcount2 (Cons (n,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
情况下,每次迭代均适用。这在需要我们达到Nil
而不是例如0
的两个~1
案例中有帮助。 badList
,就像test
一样。
此函数也可以包装在'a lenlist
中,以掩盖我们从fun size Nil = 0
| size (Cons (n,_,_)) = n
fun test xs = testcount2 (xs,size xs)
本身派生的额外参数的事实:
foo -> bar
到目前为止有些道德:
- 有时有必要创建帮助函数来解决您的最初问题。
- 这些辅助函数不限于与您提供的函数具有相同的类型签名(无论是在学校中进行练习还是用于外部API /库)。
- 有时有助于扩展函数返回的类型。
- 有时有助于扩展函数的参数。
- 仅仅因为您的任务是“编写函数
foo
”,这并不意味着您不能通过编写返回大于或等于bar
或{{1} }。
现在,对于在二叉树上解决此问题的一些提示:
重复数据类型,
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提示:
- 您的解决方案可能应该包含针对
Leaf x
和Node (left,right)
的模式匹配。考虑到“额外参数”类型的解决方案(至少证明对列表有用,但我们会看到)需要两个Leaf x
案例。为什么会这样? - 如果在列表的情况下,“额外参数”
m
表示列表的预期长度,那么在树的情况下,“额外参数”表示什么?您不能说“这是列表的长度”,因为它是一棵树。您如何捕获树枝的一部分? - 如果这仍然太困难,请考虑解决列表中没有复制粘贴的问题。也就是说,您可以查看此答案中的解决方案(但请不要这样做),但不允许您复制粘贴代码。如果要复制,则必须输入。
- 首先,尝试定义用于从
size
生成test
的辅助函数testcount2
,但对于树。因此,可以将其命名为sizeTree
以避免名称重叠,但除此之外,请尝试使其相似。这是骨架:
fun sizeTree (Leaf x) = ...
| sizeTree (Node (left,right)) = ...
将testcount3
和sizeTree
一起粘贴后,就很容易像tau一样。