PLT Scheme offers the
read-line procedure for reading lines of text from an input port.
The following are some useful procedures which use read-line to make it easy to process
an entire file, one line at a time. This code was adapted from the comp.lang.scheme thread,
How to read lines from a text file.
The following is a straightforward implementation which allows the programmer to provide a procedure which will be called with each new line read from a file.
(define (for-each-line-in-file filename proc . mode)
(with-input-from-file
filename
(lambda () (apply for-each-line proc (current-input-port) mode))))
(define (for-each-line proc . port+mode)
(let while ()
(let ((line (apply read-line port+mode)))
(unless (eof-object? line)
(proc line)
(while)))))
For documentation of the optional
mode and
port+mode parameters, see the
PLT read-line documentation.
The following will display the source code from the file
ReadingLines.scm, with line numbers.
(for-each-line-in-file
"ReadingLines.scm"
(let ((line-num 0))
(lambda (line)
(printf "~a: ~a ~n" line-num line)
(set! line-num (add1 line-num)))))
In the above example, the
line-num variable is mutated on each line. The design of
for-each-line does not allow for state to be accumulated as reading progresses, so it is necessary to set up external state (using
let in the above example) and mutate that state within the user-supplied procedure.
The standard functional programming idiom to deal with this situation is a
fold, which is a functional generalization of iteration. Here are folding versions of the above functions:
(define (fold-lines-in-file filename proc init . mode)
(with-input-from-file
filename
(lambda () (apply fold-lines proc init (current-input-port) mode))))
(define (fold-lines proc init . port+mode)
(let while ((accum init))
(let ((line (apply read-line port+mode)))
(if (eof-object? line) accum
(while (proc line accum))))))
Although these functions are similar in size and complexity to the previous versions, they are more powerful because they allow state to be passed from one invocation of the user-supplied procedure to the next.
The above functions allow the previous example to be rewritten more simply, as follows:
(fold-lines-in-file
"ReadingLinesWithFold.scm"
(lambda (line line-num)
(printf "~a: ~a ~n" line-num line)
(add1 line-num))
1)
Notice that line numbering is achieved without needing to mutate a variable. The user-supplied procedure simply accepts a
line-num argument, and when it's done, returns the next value for that argument. The initial line number is supplied as the last argument to
fold-lines-in-file.
This general pattern is used by all
fold operations, such as the canonical
foldl function for folding over lists.
--
AntonVanStraaten - 03 Apr 2004
See comp.lang.scheme thread for other contributors
Source code is in:
ReadingLines,
ReadingLinesSample,
ReadingLinesWithFold,
ReadingLinesWithFoldSample