我的任务是编写一个类型为‘a btree -> ‘a option list
的函数,该函数将给定的树以后缀顺序(postorder)存储在‘a option
类型的元素列表中。
内部节点将由None
表示,值为x
的外部节点(叶子)将由Some x
表示。
到目前为止,对于树叶很容易,但是如何将它放在'a option list
中?
type 'a btree = L of 'a | N of 'a btree * 'a btree ;;
let rec store t =
match t with
| L x -> Some x
| N (a,b) -> None ???
;;
我知道的第二个匹配案例不正确,但如何解决?
答案 0 :(得分:3)
如果你看看你的第一个案例,你会发现它也不完全存在;它正在返回'a option
,但您希望该函数返回'a option list
。
显然,你将返回一个列表,所以先修复它:
let rec store = function
| L x -> [Some x]
| N (a,b) -> [None] (* ??? *)
现在让我们解决第二种情况;我们想将None
附加到输出中,但在此之前,我们需要子树的节点:
let rec store = function
| L x -> [Some x]
| N (a,b) -> (store a) @ (store b) @ [None]
@
的类型为
'a list -> 'a list -> 'a list
即。它将列表连接在一起。我们想要加入左子树的结果列表,然后是右边的,然后是最后的内部节点的结果。
答案 1 :(得分:3)
如果有人对这种改进感兴趣,可以通过线程化额外的累加器参数,比Len的解决方案更好,性能更好。
我们的想法是从一个带有树并生成列表的函数store : 'a btree -> 'a option list
转移到一个函数store' : 'a btree -> 'a option list -> 'a option list
,它将树的元素添加到现有的列表作为参数传递。
let rec store' t acc = match t with
| L x -> Some x :: acc
| N (a, b) ->
store' a (store' b (None :: acc))
使用此定义,元素仅在最终结果列表中添加一次,而不是首先用于构建临时store a
列表,然后通过(@)
第二次附加到最终结果操作
参数顺序很重要,因为在t
之前编写acc
可以直观地了解列表中的最终元素顺序:t
的元素将之前的 > acc
中已存在的元素。这允许N
案例非常自然地阅读:很容易看到结果首先包含a
,然后b
,然后是None
,然后{ {1}}。
最后,你当然可以用acc
:
store
store'
习惯上将第一个定义包装在第二个定义中(如果你不想将这个“低级”函数暴露给用户),并赋予它与外部定义相同的名称(它不是不冲突,因为它不是递归的,所以不进入内部范围):
let store t = store' t []
当然,这个定义是否比Len更好“取决于你的评价标准是什么”。 Len的解决方案更短,更易于阅读,并且可以更紧密地映射原始问题。
(你可以通过使用惰性枚举而不是严格的列表来获得两全其美,但那是另一个故事。)