尾部文件OCaml的递归读取

问题描述 投票:0回答:1

我编写了一个可以打印文件所有内容的函数

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 -> ()

现在它像常规的尾部递归函数一样工作,不会溢出我的堆栈。这两个函数有什么区别?

exception ocaml stack-overflow tail-recursion
1个回答
0
投票

在第一个示例中,try ... with是在递归调用print_file之后发生的操作。因此该函数不是尾递归的。

您可以想象try在堆栈上设置了一些数据,with从堆栈中删除了数据。由于您在递归调用之后删除了数据,因此堆栈变得越来越深。

这在OCaml的早期版本中一直是一个一致的问题。编写尾部递归代码以处理文件非常棘手。在最近的修订中,您可以使用exceptionmatch子句来获取递归调用的尾部位置:

let rec print_file channel =
    match input_line channel with
    | line -> print_endline line; print_file channel
    | exception End_of_file -> () 
© www.soinside.com 2019 - 2024. All rights reserved.