使用异常来突破文件行的迭代

z_a*_*xis 4 ocaml exception

有些文本文件需要逐行操作.我写了一个withFile函数如下:

let withFile fn handle =
    let rec iter_lines fh =
    try
        handle (input_line fh);
        iter_lines fh
    with _ -> close_in fh in
    iter_lines (open_in fn);;
Run Code Online (Sandbox Code Playgroud)

所以我可以将每个文件操作为:

withFile "file1.txt" (fun line -> (*...*))
withFile "file2.txt" (fun line -> (*...*))
...
Run Code Online (Sandbox Code Playgroud)

但是当我不想处理所有线路时,我不确定如何优雅地退出.例如:

withFile "file3.txt" (fun line ->
   (*when the line meets some condition, i will exit without handling other lines*)
);
Run Code Online (Sandbox Code Playgroud)

任何建议表示赞赏!

Jef*_*eld 6

您的函数iter_lines不是尾递归,这意味着如果以这种方式处理非常大的文件,您可能会耗尽堆栈空间.它不是尾递归的原因是它必须建立和拆除try ... with捕获异常的机制.

除此之外,这看起来对我很好.这种高阶函数是OCaml(和FP)的全部功能.

使尾部递归的一种方法是将异常处理移出到包含函数.我还会更具体地说明您要处理的异常.所以你得到这个:

let withFile fn handle =
    let rec iter_lines fh =
        handle (input_line fh);
        iter_lines fh
    in
    let fh = open_in fn in
    try iter_lines fh
    with End_of_file -> close_in fh
Run Code Online (Sandbox Code Playgroud)

如果您希望能够提前退出,一种简单的方法是让您的句柄函数返回一个布尔值,告诉您是否继续处理行.你会得到这样的东西:

let withFile fn handle =
    let rec iter_lines fh =
        if handle (input_line fh) then
            iter_lines fh
    in
    let fh = open_in fn in
    try iter_lines fh
    with End_of_file -> close_in fh
Run Code Online (Sandbox Code Playgroud)

如果您希望能够使用异常提前退出,则需要捕获所有异常withFile,关闭文件,然后重新引发除异常之外的任何异常End_of_file.这为您提供了如下代码:

let withFile fn handle =
    let rec iter_lines fh =
        handle (input_line fh);
        iter_lines fh
    in
    let fh = open_in fn in
    try iter_lines fh
    with e ->
        (close_in fh; if e <> End_of_file then raise e)
Run Code Online (Sandbox Code Playgroud)