Subject: Re: lisp idiom for processing each line in a file?
From: rpw3@rpw3.org (Rob Warnock)
Date: Tue, 21 Feb 2006 02:23:47 -0600
Newsgroups: comp.lang.lisp
Message-ID: <94ednfTEPceOU2fenZ2dnUVZ_tGdnZ2d@speakeasy.net>
<spaecious@gmail.com> wrote:
+---------------
| Rob Warnock wrote:
| > ...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)))
...
| My perception is probably colored by experience with Scheme...
+---------------

I'm a former Schemer myself, and have found that one needs to
make a conscious effort to let go of "the Scheme way" and embrace
"the CL way" if one is to become fluent and idiomatic in CL,
e.g., using CL's builtin LOOP/DOTIMES/DOLIST/DO macros over
Scheme-style tail-recursive loops.

+---------------
| ...but I find your do version at least as easy to understand
| (apart from the #1 and #1#, which I haven't seen before)
+---------------

The #= and ## are reader notation for self-referential [sub]structure
in literal input [and output, if *PRINT-CIRCLE* is true], see:

    http://www.lispworks.com/documentation/HyperSpec/Body/02_dho.htm
    2.4.8.15 Sharpsign Equal-Sign

    http://www.lispworks.com/documentation/HyperSpec/Body/02_dho.htm
    2.4.8.16 Sharpsign Sharpsign

Many Schemes [MzScheme, for one] also support the #n=/#n# notation,
but I seldom saw it used there; people usually just wrote it all out:

    (do ((line (read-line stream nil nil) (read-line stream nil nil)))
	(...)
      ...)

I forget where I first saw the #n=/#n# notation used with DO
in CL -- it was a long time ago. [Hmmm... (*grep*) (*grep*)...]
Ah, yezzz... It was something Erik Naggum wrote in late 1997, see
<URL:news:3090081183347904@naggum.no> and the thread around that.

Note that DO [in both Scheme & CL] has a different default for the
"step" subform than "LOOP FOR var = ..." does -- the former defaults
the "step" form to the "var" [that is, no assignment done], whereas
the latter defaults the "step" form to the "init" form. That is this:

    (loop for line = (read-line stream nil nil)
      ...)

is just a more readable/convenient shorthand for this:

    (loop for line = (read-line stream nil nil) then (read-line stream nil nil)
      ...)

The fact that the default in LOOP is usually what you want for
stepping through I/O is certainly one of the reasons I learned
the basics of LOOP very soon after starting to use CL.

+---------------
| ...as your loop version, though familiarity breeds ease.
+---------------

It does, but you have to make a conscious effort to practice
CL style or it never gets familiar.


-Rob

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