ocaml - ocaml 尾递归函数
问题描述
当我在很长的输入上运行以下函数时,我正在使用 utop 运行 Ocaml:
let string_to_list str =
let rec loop i limit =
if i = limit then []
else (String.get str i) :: (loop (i + 1) limit)
in
loop 0 (String.length str);;
它返回以下错误:
Stack overflow during evaluation (looping recursion?).
该函数的尾递归版本是什么?
解决方案
正如 Jeffrey Scofield 所指出的,您的函数不是尾递归的。将其转换为使用尾递归将涉及向您的函数引入累加器参数loop
。
let string_to_list str =
let rec loop i limit acc =
if i = limit then acc
else
let ch = String.get str i in
loop (i + 1) limit (ch :: acc)
in
loop 0 (String.length str) [] |> List.rev
这样,评估函数所需的所有信息都包含在一个堆栈帧中,编译器可以优化掉所有先前的堆栈帧。
为了在纯文本中提供一点视觉效果,我们看一下函数的非尾递归版本,称为 on "hello"
。
+-----------------------+
| string_to_list "hello"|->
+-----------------------+ |
^ v------------------+
| +--------+
<-|loop 0 5|->
+--------+ |
^ v---+
| +--------+
<-|loop 1 5|->
+--------+ |
^ v---+
| +--------+
<-|loop 2 5|->
+--------+ |
^ v---+
| +--------+
<-|loop 3 5|->
+--------+ |
^ v---+
| +--------+
<-|loop 4 5|->
+--------+ |
^ v---+
| +--------+
<-|loop 5 5|
+--------+
随着我们的进行,每个呼叫都需要前一个呼叫中的信息。但是,如果loop
通过参数传递所有必要的信息(累积的结果列表),则不需要loop
任何先前调用的迭代来完全评估。
+-----------------------+
| string_to_list "hello"|
+-----------------------+
|
v
+----------------------------------+
|loop 0 5 [] |
+----------------------------------+
|
v
+----------------------------------+
|loop 1 5 ['h'] |
+----------------------------------+
|
v
+----------------------------------+
|loop 2 5 ['e'; 'h'] |
+----------------------------------+
|
v
+----------------------------------+
|loop 3 5 ['l'; 'e'; 'h']. |
+----------------------------------+
|
v
+----------------------------------+
|loop 4 5 ['l'; 'l'; 'e'; 'h'] |
+----------------------------------+
|
v
+----------------------------------+
|loop 5 5 ['o'; 'l'; 'l'; 'e'; 'h']|
+----------------------------------+
|
v
+----------------------------------+
|List.rev ['o'; 'l'; 'l'; 'e'; 'h']|
+----------------------------------+
|
v
+----------------------------------+
|['h'; 'e'; 'l'; 'l'; 'o'] |
+----------------------------------+
推荐阅读
- java - 在另一个网络上运行套接字客户端时连接超时
- javascript - .then 不是函数/承诺
- ruby-on-rails - Rails:将 Mongoid 文档添加到临时存储以在文件导出表中使用
- javascript - 尝试根据子属性对对象键进行排序
- bash - 您如何配置 Puppet exec 以执行另一个命令作为 onlyif 的一部分
- apache - Apache NiFi TCP 客户端/服务器
- ms-access - 访问 VBA:将 GetRows 与书签结合使用
- python - 如何为具有动态字典的 python 类实例创建自定义 __hash__ 和 __eq__
- amazon-web-services - 如何查看 AWS Batch 计算环境错误?
- python - 将 Pylint 版本 1.9.2 升级到最新版本