Использование исключений для прерывания итерации по строкам файла - PullRequest
4 голосов
/ 12 ноября 2011

Есть несколько текстовых файлов, которыми нужно манипулировать построчно. Я написал 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);;

Так что я могу манипулировать каждым файлом как:

withFile "file1.txt" (fun line -> (*...*))
withFile "file2.txt" (fun line -> (*...*))
...

Но я не уверен, как элегантно выйти, когда я не хочу обрабатывать все строки. Например:

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

Любое предложение приветствуется!

1 Ответ

6 голосов
/ 12 ноября 2011

Ваша функция 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

Если вы хотите иметь возможность досрочного выхода, один из простых способов - заставить вашу функцию дескриптора возвращать логическое значение, указывающее, продолжать ли обработку строк или нет. В итоге вы получите что-то вроде этого:

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

Если вы хотите использовать исключение для досрочного выхода, вам нужно перехватить все исключения в 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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...