Subject: Re: lisp idiom for processing each line in a file?
From: rpw3@rpw3.org (Rob Warnock)
Date: Mon, 20 Feb 2006 04:20:16 -0600
Newsgroups: comp.lang.lisp
Message-ID: <q8OdnQk1cdx9CmTenZ2dnUVZ_sSdnZ2d@speakeasy.net>
<spaecious@gmail.com> wrote:
+---------------
| I'm trying to process each line in a file, and am wondering if there is
| a simpler or more idiomatic way of doing it than the following:
| 
| ;;; Simplified version of my real process-line function
| (defun process-line (line)
|   (format t "~A~%" line))
| 
| ;;; Process stream, calling per-line-fn on each line of the stream
| (defun process (per-line-fn stream)
|   (let ((line (read-line stream nil)))
|     (if (not (null line))
|         (progn (funcall per-line-fn line)
|                (process per-line-fn stream)))))
+---------------

This can blow up if your file is large and your CL implementation
doesn't happen to perform tail-call optimization on the tail call
to PROCESS.   Note: Common Lisp is not Scheme.

+---------------
| And I kick off the process like so:
| 
| CL-USER> (with-open-file (stream "../../words/test.txt")
| 	   (process #'process-line stream))
+---------------

The standard CL idiom omits the intermediate PROCESS function
entirely, and does the whole thing in the WITH-OPEN-FILE call:

    (with-open-file (stream "../../words/test.txt")
      (loop for line = (read-line stream nil nil)
	    while line do
	(per-line-fn line)))

+---------------
| Is there a simpler way of doing what I'm trying to do here?
+---------------

See above.

+---------------
| I've seen some loop-based solutions, but am staying away from
| loop for the moment.
+---------------

Why?!? It's the most natural way to code this particular task, IMHO.

You don't have to use the entire repertoire of LOOP all at once.
Just pick a few simple idiomatic templates [such as the above]
and add more refinements as you discover you really need them.

Of course, you can also use DO, but to my taste it's a good deal
clunkier:

    (with-open-file (stream "../../words/test.txt")
      (do ((line #1=(read-line stream nil nil) #1#))
	  ((null line))
	(per-line-fn line)))


-Rob

-----
Rob Warnock			<rpw3@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607