如何编写使用尾部递归添加列表元素的函数?

时间:2018-11-05 10:16:12

标签: function functional-programming ocaml

我需要在OCaml中编写一个函数,该函数在两个不同的递归中添加两个列表的元素:简单和尾部。我简单地做了一个:

let rec add1 a b = 
match (a, b) with
      ([], []) -> []
    | (head1::tail1, []) -> head1 :: add1 tail1 []
    | ([], head2::tail2) -> head2 :: add1 [] tail2
    | (head1::tail1, head2::tail2) -> head1 + head2 :: add1 tail1 tail2
;;

它是这样的:

add1 [1;2;3] [4;5;6;7];;

此返回:

int list = [5; 7; 9; 7]

[1+4; 2+5; 3+6; 0+7]:0被添加到7,因为在第一个列表的该位置上没有元素。

所以,我的问题是:

如何通过尾递归实现?

2 个答案:

答案 0 :(得分:3)

使该尾部递归的方法是将结果向后构建,并在递归中传递它,最后将其反转。

let add1 a b =
  let rec loop acc = function
    | (xs, [])
    | ([], xs) -> List.rev_append acc xs
    | (x::xs, y::ys) -> loop ((x + y)::acc) (xs, ys)
  in
  loop [] (a, b)

注意:如果一个列表的长度大于另一个列表的长度,则无需为每个元素添加0。尾巴已经是结果。因此,我使用List.rev_append反转累积的值,并一次性添加剩余的尾巴。

注2:List.rev_append也可以追加空白列表,因此不需要匹配[[],[])。

答案 1 :(得分:1)

尾部递归包括具有递归功能,该功能仅对自身进行调用而无需任何其他操作。

以下阶乘不是尾部递归的,因为最后一条语句并不执行对事实的简单调用,而是需要乘法:

    let rec fact n = 
        if n = 0 then 1
        else n*(fact (n-1))

通过使用累加器,可以使该函数尾部递归,最后一条语句执行对事实的调用,因此可以使用跳转而不是调用来进行编译:

    let rec fact n r =
        if n = 0 then r
        else fact (n-1) (r*n)

以及用法:

    fact 5 1

对于列表添加,如果两个列表的长度至少相同,则可以以相同的方式进行。