我们给出一个包含两种元素的树。它由以下数据结构定义。
type ( 'a , 'b ) tree =
Empty
| Vertexa of 'a * ( 'a , 'b ) tree list
| Vertexb of 'b * ( 'a , 'b ) tree list
写一个函数split:(' a,' b)树 - > '列表*&#b; b列表,用于将类型' a的所有元素保存到第一个列表中,并将所有类型的元素保存在第二个列表中。
我有一个想法,递归地做这个,但我有点坚持这个。即使它根本不起作用,我也会附上我的尝试:/
let rec one drevo_list=
match drevo_list with
| [Empty]->Empty
| Empty::tl -> one tl
| Vertexa(a,b)::tl -> Vertexa(a,b@tl)
| Vertexb(a,b)::tl -> Vertexb(a,b@tl)
此功能将列表转换为树。我需要它用于递归,因为Vertexa或Vertexb中的第二个参数是一个列表。这很有效 但递归部分没有。
let rec split drevo=
match drevo with
| Empty -> [],[]
| Vertexa(a,b)-> split (one b)
| Vertexb(a,b)-> split (one b)
这部分不起作用,我不知道如何完成它。有没有人知道如何完成这个?
答案 0 :(得分:3)
您不需要drevo_list
功能来解决此问题。它实际上会导致你走错方向。
您需要使用List.map
在树列表中应用拆分。您将获得('a list * 'b list) list
类型的值。现在您需要一个帮助函数concat_pairs
,它将此值展平为一对类型'a list * 'b list
(c.f。,标准concat
函数)。要实现此功能,您可以使用List.fold_left
。其余的都是微不足道的。
注意,这当然是一个贪婪的解决方案。完成后,您可能会尝试找到更好的解决方案,更高效,尾递归。
答案 1 :(得分:1)
此功能至少有两部分难以编写:
返回一对列表的函数需要在每个递归步骤中打包和解包其返回值,例如:辅助函数,匹配语句或让绑定。一种方法是编写一个函数,将一个元素插入一对内的列表中:
let insertA a (xs, ys) = (a::xs, ys)
let insertB b (xs, ys) = (xs, b::ys)
对树类型和嵌入列表类型进行递归的函数需要两个递归模式的组合。这可以使用一组相互递归的函数或使用列表的高阶组合器来解决。以下是使用以前策略的解决方案概述:
let rec split s =
match s with
| Empty -> ([], [])
| Vertexa (a, ts) -> (* if we had just one t: insertA a (split t) *)
| Vertexb (a, ts) -> (* if we had just one t: insertB b (split t) *)
因此,您需要一个函数splitMany : ('a, 'b) tree list -> ('a list, 'b list)
,可以为每个单独的树回调split
。
and rec splitMany ts =
match ts with
| [] -> ([], [])
| (t:ts') -> (* merge (split t) with (splitMany ts') *)
对于高阶函数方法,可以通过让函数将自身传递给一组高阶函数来避免显式的相互递归,从而不会在高阶函数的实现中纠缠它:
let rec split s =
match s with
| Empty -> [],[]
| Vertexa (a, ts) -> insertA (concat_pairs (map split ts))
| Vertexb (a, ts) -> insertB (concat_pairs (map split ts))
其中concat_pairs
是ivg的发明。