Reputation: 13
I write a function that print all contents of file like that
let rec print_file channel =
try
begin
print_endline (input_line channel);
print_file channel
end
with End_of_file -> ()
And because of print_file is last operation I was thinking that it will be optimized to regular loop. but when I had runned my program on really big file, I had got stack overflow. So I has tried to wrap input_line function to input_line_opt that don't raise exception and little change code of 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 -> ()
And now it works like regular tail recursive function and don't overflow my stack. What difference between this two function?
Upvotes: 1
Views: 262
Reputation: 66823
In the first example, try ... with
is an operation that occurs after the recursive call to print_file
. So the function is not tail recursive.
You can imagine that try
sets up some data on the stack, and with
removes the data from the stack. Since you remove the data after the recursive call, the stack gets deeper and deeper.
This was a consistent problem in earlier revisions of OCaml. It was tricky to write tail-recursive code for processing a file. In recent revisions you can use an exception
clause of match
to get a tail position for the recursive call:
let rec print_file channel =
match input_line channel with
| line -> print_endline line; print_file channel
| exception End_of_file -> ()
Upvotes: 3