Why does the OCaml std lib have so many non-tail-recursive functions?

北城以北 提交于 2019-12-21 07:32:38

问题


I have been rewriting many OCaml standard library functions to be tail-recursive lately. Given that this has entailed straight-forward CPS transformation, I am left puzzling over why the default versions are not written this way.

As an example, in the standard library, map is defined as:

let rec map f = function
    []   -> []
  | a::l -> let r = f a in r :: map f l

I have rewritten it to be:

let map f l =
  let rec aux l k = match l with
      []   -> k []
    | a::l -> aux l (fun rest -> k (f a :: rest))
  in aux l (fun x -> x)

回答1:


In my experience, tail recursive versions of non-trivial functions often trade space efficiency against time efficiency. In other words, the functions in the standard library might easily be faster for smallish inputs.




回答2:


Well, your code is building and passing along a "linked list" of closures (each of the closures captures the previous one as k) in the heap instead of a stack of frames on the call stack.

A more common, equivalent way to do it tail-recursively is to pass along the result list so far (in reverse, since you can only efficiently add to the front), and then reverse it in the end:

let map f l =
  let rec aux l acc = match l with
      []   -> List.rev acc
    | a::l -> aux l (f a :: l)
  in aux l

(this is basically the same as List.rev (List.rev_map f l))

In this case, the thing that is accumulated is the list of results so far (in reverse), rather than a closure. But the effect is exactly the same.

In both cases we need linear space for some kind of intermediate representation (other than the input nor the output list), so in terms of complexity of memory usage, there is no advantage over the non-tail-recursive version. Although it is true that there is more memory on the heap than the stack, so using the tail-recursive version will probably work for bigger lists than the non-tail-recursive version. In terms of absolute memory usage, the accumulating the list option is probably the most efficient, because closures and stack frames both have more overhead.



来源:https://stackoverflow.com/questions/12012585/why-does-the-ocaml-std-lib-have-so-many-non-tail-recursive-functions

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!