rev_append 与(追加或@)
rev_append vs (append or @)
如果我们有两个列表 l1
和 l2
并且我们想连接它们,我们可以使用 @
或 append
,它位于 O(n1)
中n1
是 l1
的长度。或者我们可以根据文档使用 rev_append
:
equivalent to List.rev l1 @ l2, but rev_append is tail-recursive and more efficient.
那么rev_append
是比@
更有效率还是比List.rev + @
更有效率?当我们不关心顺序时,用它代替 @
和 append
会更好吗?
OCaml 列表是不可变的。第二个列表不需要更改,但必须复制第一个列表,以便副本可以指向第二个列表。因此,您将不得不以某种方式遍历第一个列表。您无能为力会改变追加的大 O 时间复杂度。
由于只能在列表的开头添加新元素,所以如果想让结果保持第一个列表的顺序,就需要倒序遍历第一个列表。
最明显的方法是递归调用,直到到达第一个列表的末尾,然后在每个递归调用中 return 添加前缀。然而,这不是尾递归的。即,它将消耗与第一个列表的长度成比例的堆栈 space。当第一个列表很长时,您可以 运行 出栈 space(又名栈溢出)。
这就是 @
的工作方式。它花费的时间和堆栈 space 与第一个列表的长度成正比。
另一个想法是放弃维护第一个列表的顺序。如果以相反的顺序为第一个列表添加前缀,则可以轻松地使操作尾递归。这就是 List.rev_append
的目的。它需要常量堆栈 space.
如果你想保持原来的list顺序,又想用常量栈space你可以把第一个list倒过来(用List.rev
),然后用List.rev_append
.
普通 List.rev_append
比 @
快,因为它不必进行内部函数调用——它可以只是一个循环。它也明显比 List.rev
加 List.rev_append
.
快
综上所述,如果你不关心最后的顺序,那么 List.rev_append
比 @
快,是的。它也不会溢出堆栈。它不会更快,因为时间复杂度基本相同。
如果我们有两个列表 l1
和 l2
并且我们想连接它们,我们可以使用 @
或 append
,它位于 O(n1)
中n1
是 l1
的长度。或者我们可以根据文档使用 rev_append
:
equivalent to List.rev l1 @ l2, but rev_append is tail-recursive and more efficient.
那么rev_append
是比@
更有效率还是比List.rev + @
更有效率?当我们不关心顺序时,用它代替 @
和 append
会更好吗?
OCaml 列表是不可变的。第二个列表不需要更改,但必须复制第一个列表,以便副本可以指向第二个列表。因此,您将不得不以某种方式遍历第一个列表。您无能为力会改变追加的大 O 时间复杂度。
由于只能在列表的开头添加新元素,所以如果想让结果保持第一个列表的顺序,就需要倒序遍历第一个列表。
最明显的方法是递归调用,直到到达第一个列表的末尾,然后在每个递归调用中 return 添加前缀。然而,这不是尾递归的。即,它将消耗与第一个列表的长度成比例的堆栈 space。当第一个列表很长时,您可以 运行 出栈 space(又名栈溢出)。
这就是 @
的工作方式。它花费的时间和堆栈 space 与第一个列表的长度成正比。
另一个想法是放弃维护第一个列表的顺序。如果以相反的顺序为第一个列表添加前缀,则可以轻松地使操作尾递归。这就是 List.rev_append
的目的。它需要常量堆栈 space.
如果你想保持原来的list顺序,又想用常量栈space你可以把第一个list倒过来(用List.rev
),然后用List.rev_append
.
普通 List.rev_append
比 @
快,因为它不必进行内部函数调用——它可以只是一个循环。它也明显比 List.rev
加 List.rev_append
.
综上所述,如果你不关心最后的顺序,那么 List.rev_append
比 @
快,是的。它也不会溢出堆栈。它不会更快,因为时间复杂度基本相同。