Reputation: 8460
There are some text files which needed to be manipulated line by line. I wrote a withFile
function as below:
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);;
So i can manipulate each file as :
withFile "file1.txt" (fun line -> (*...*))
withFile "file2.txt" (fun line -> (*...*))
...
But I am not sure how to exit elegantly when i do not want to handle all lines. For example:
withFile "file3.txt" (fun line ->
(*when the line meets some condition, i will exit without handling other lines*)
);
Any suggestion is appreciated !
Upvotes: 4
Views: 511
Reputation: 66823
Your function iter_lines
isn't tail recursive, which means you will probably exhaust your stack space if you process a very large file this way. The reason it isn't tail recursive is that it has to establish and tear down the try ... with
mechanism for catching exceptions.
Other than that, this looks really good to me. This kind of higher-order function is what OCaml (and FP) is all about.
One way to make it tail recursive is to move the exception handling out to the containing function. I would also be more specific about the exception that you want to handle. So you get this:
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
If you want to be able to exit early, one easy way is to have your handle function return a boolean that tells whether to continue processing lines or not. You would end up with something like this:
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
If you want to be able to use an exception to exit early, you'd need to catch all exceptions in withFile
, close the file, then re-raise any exception other than End_of_file
. This gives you code that looks like this:
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)
Upvotes: 6