tail recursion vs. forward recursion

后端 未结 3 2066
萌比男神i
萌比男神i 2020-12-04 18:21

Can someone give me the difference between these two kinds recursions and example (specifically in OCaml)?

3条回答
  •  北海茫月
    2020-12-04 18:58

    For example, a recursive function build_word which takes a char list and combine them to a string i.e.['f'; 'o'; 'o'] to string "foo". The induction process can be visualized this way:

    build_word ['f'; 'o'; 'o']
    "f" ^ (build_word ['o'; 'o'])
    "f" ^ ("o" ^ (build_word ['o'])    // base case! return "o" and fold back
    "f" ^ ("o" ^ ("o"))
    "f" ^ ("oo")
    "foo"
    

    That was a normal recursion. Note that each pair of parentheses stands for a new stack frame or recursive call. The solution to this problem (i.e. "f", "fo", or "foo") cannot be derived before the end of the recursion (where the base case is met). Only then does the last frame return the last result back to the previous one before "popping", and vice versa.

    In theory, each call creates a new stack frame (or scope, if you will) to hold the "place" for the fragmented solution to be returned and collected toward the beginning. This can leads to stackoverflow (this link is a recursion btw).

    A tail call version would look something like this:

    build_word ['f'; 'o'; 'o'] ""
    build_word ['o'; 'o'], "f"
    build_word ['o'] ("f" ^ "o")
    build_word [] ("f" ^ "o" ^ "o")
    "foo"
    

    Here, the accumulated result (often stored in a variable known as accumulator) is being passed forward. With optimization, tail call wouldn't have to create a new stack frame because it does not have to maintain the previous ones. The solution is being solved "forward" rather than "backward".

    Here are the build_word functions in two versions:

    non-tail

    let build_word chars = 
      match chars with
      | [] -> None
      | [c] -> Some Char.to_string c
      | hd :: tl -> build_word tl
    ;;
    

    tail

    let build_word ?(acc = "") chars =
      match chars with
      | [] -> None
      | [c] -> Some Char.to_string c
      | hd::tl -> build_word ~acc:(acc ^ Char.to_string hd) tl
    ;;
    

    The forward recursion is well-explained in the accepted answer by @sepp2k.

提交回复
热议问题