【问题标题】:Why wasn't OCaml List.fold_right implemented as tail-recursive?为什么 OCaml List.fold_right 没有实现为尾递归?
【发布时间】:2013-02-28 08:37:17
【问题描述】:

List.fold_right 不是tail-recursive 此处指出的http://caml.inria.fr/pub/docs/manual-ocaml/libref/List.html

我的问题是为什么 List.fold_right 没有实现为 tail-recursive

我认为这样做并不难

let rev l = 
  let rec revr acc = function
    | [] -> acc
    | hd::tl -> revr (hd::acc) tl
  in 
  revr [] l;;

let fold_right f l b =
  let rev_l = rev l 
  in 
  let rec folder_rr acc = function
    | [] -> acc
    | hd::tl -> folder_rr (f hd acc) tl
  in 
  folder_rr b rev_l;;

我还在库中发现,很多函数不是tail-recursive,而可以实现为tail-recursive制作者是如何做出这样的决定的?

【问题讨论】:

    标签: functional-programming ocaml


    【解决方案1】:

    这种尾递归实现可以将fold_right 应用于较大的列表,但代价是较短的列表速度较慢,因为您必须遍历列表两次。最初的开发人员认为,允许列表的极端用例(如果您有数千个元素,则无论如何都应该计划一个更高效的数据结构)以牺牲更丑陋的代码和使其他所有人的性能更差为代价,这不是一个好的权衡.

    有很多不同的方法来获得尾递归映射、fold_right 等。您会在扩展标准库、古老的 Extlib 以及更新的 Batteries 和 Core 的库中找到它们。实现技术从你的,到对前一千个左右的项目乐观地使用非 tailrec 版本的技巧,然后切换到 tailrec 版本,到丑陋的不安全技巧,以 cons cell 突变为代价进行单次遍历(这也会降低性能)。

    【讨论】:

    • 你有任何证据表明尾递归版本更慢吗?我不相信... :-)
    • 使fold_right尾递归的简单方法,正如上面的原始帖子所做的那样,是先反转列表,然后使用 fold_left。这具有很容易在基准上衡量的成本。可以使用更复杂的技术,例如在通常的遍历旁边使用计数器,并且只有 rev-ing 列表的其余部分在计数器通过时向左折叠;这允许小列表的开销非常小。我们已经实现了这个in Batteries,性能还可以。
    【解决方案2】:

    这个问题以前被问过,可能很多次了。你可以在这里阅读一些以前的答案:

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

    我的快速回答是,对于小型案例,非尾递归实现通常要快得多。实施者显然认为这是一个很好的权衡。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多