Exception 文件OCaml的尾部递归读取

Exception 文件OCaml的尾部递归读取,exception,ocaml,stack-overflow,tail-recursion,Exception,Ocaml,Stack Overflow,Tail Recursion,我写了一个函数,可以像那样打印文件的所有内容 let rec print_file channel = try begin print_endline (input_line channel); print_file channel end with End_of_file -> () 因为print_文件是最后一个操作,所以我认为它将被优化为常规循环。但当我在一个很大的文件上运行我的程序时,我的堆栈溢出了。 所以我尝试将i

我写了一个函数,可以像那样打印文件的所有内容

let rec print_file channel =
    try
    begin
        print_endline (input_line channel);
        print_file channel
    end
    with End_of_file -> ()
因为print_文件是最后一个操作,所以我认为它将被优化为常规循环。但当我在一个很大的文件上运行我的程序时,我的堆栈溢出了。 所以我尝试将input_line函数包装为input_line_opt,这样不会引发异常,也不会对print_文件产生太多更改代码


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 -> ()
现在,它的工作原理类似于常规的尾部递归函数,不会使堆栈溢出。
这两个函数之间有什么区别?

在第一个示例中,
试试。。。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 -> ()