文件 OCaml 的尾递归读取
Tail recursive reading of file OCaml
我写了一个打印文件所有内容的函数
let rec print_file channel =
try
begin
print_endline (input_line channel);
print_file channel
end
with End_of_file -> ()
并且由于 print_file 是最后一个操作,我认为它会针对常规循环进行优化。但是当我在非常大的文件上运行我的程序时,我遇到了堆栈溢出。
所以我试图将 input_line 函数包装到 input_line_opt 中,这不会引发异常并且 print_file.
的代码几乎没有变化
let input_line_opt channel =
try Some (input_line channel)
with End_of_file -> None
let rec print_file channel =
let line = input_line_opt channel in
match line with
Some line -> (print_endline line; print_file channel)
| None -> ()
现在它像常规尾递归函数一样工作并且不会溢出我的堆栈。
这两个函数有什么区别?
在第一个例子中,try ... with
是在递归调用print_file
之后发生的操作。所以这个函数不是尾递归的。
你可以想象try
在栈上设置一些数据,with
从栈中移除数据。由于你在递归调用后删除了数据,所以堆栈越来越深。
这是 OCaml 早期版本中的一个一致问题。编写用于处理文件的尾递归代码非常棘手。在最近的修订版中,您可以使用 match
的 exception
子句来获取递归调用的尾部位置:
let rec print_file channel =
match input_line channel with
| line -> print_endline line; print_file channel
| exception End_of_file -> ()
我写了一个打印文件所有内容的函数
let rec print_file channel =
try
begin
print_endline (input_line channel);
print_file channel
end
with End_of_file -> ()
并且由于 print_file 是最后一个操作,我认为它会针对常规循环进行优化。但是当我在非常大的文件上运行我的程序时,我遇到了堆栈溢出。 所以我试图将 input_line 函数包装到 input_line_opt 中,这不会引发异常并且 print_file.
的代码几乎没有变化
let input_line_opt channel =
try Some (input_line channel)
with End_of_file -> None
let rec print_file channel =
let line = input_line_opt channel in
match line with
Some line -> (print_endline line; print_file channel)
| None -> ()
现在它像常规尾递归函数一样工作并且不会溢出我的堆栈。 这两个函数有什么区别?
在第一个例子中,try ... with
是在递归调用print_file
之后发生的操作。所以这个函数不是尾递归的。
你可以想象try
在栈上设置一些数据,with
从栈中移除数据。由于你在递归调用后删除了数据,所以堆栈越来越深。
这是 OCaml 早期版本中的一个一致问题。编写用于处理文件的尾递归代码非常棘手。在最近的修订版中,您可以使用 match
的 exception
子句来获取递归调用的尾部位置:
let rec print_file channel =
match input_line channel with
| line -> print_endline line; print_file channel
| exception End_of_file -> ()