From: Gil Citro
Subject: Testing for EOF
Date: 
Message-ID: <3u1f98$l9n@usenet.srv.cis.pitt.edu>
I am starting to write Lisp and am trying to read the lines of a file
into a list of strings such that each element of the list is one line
from the file.  I have figured out how to read lines with read-line,
but I can't fugure out how to test for the end of the file.  I'm sure
it must be easy but I've looked through Steele's book on Common Lisp
and through the FAQ and I can't find it.  I'd appreciate it if someone
could give me a tip.

Thanks,
Gil Citro

From: Barry Margolin
Subject: Re: Testing for EOF
Date: 
Message-ID: <3u1q6n$8us@tools.near.net>
In article <··········@usenet.srv.cis.pitt.edu> ······@pitt.edu (Gil Citro) writes:
>I am starting to write Lisp and am trying to read the lines of a file
>into a list of strings such that each element of the list is one line
>from the file.  I have figured out how to read lines with read-line,
>but I can't fugure out how to test for the end of the file.  I'm sure
>it must be easy but I've looked through Steele's book on Common Lisp
>and through the FAQ and I can't find it.  I'd appreciate it if someone
>could give me a tip.

READ-LINE will return NIL (or whatever value you specify in the <eof-value>
parameter) when it reaches EOF.

(defun list-lines (filename)
  (with-open-file (stream filename :direction :input)
    (loop for line = (read-line stream)
          while (not (null line))
          collect line)))

When using READ it's often a good idea to provide a distinguishable
<eof-value> parameter (since NIL is likely to be valid, normal input), but
since READ-LINE always returns a string in the normal case and NIL in the
EOF case, the default is usually adequate here.
-- 
Barry Margolin
BBN Planet Corporation, Cambridge, MA
······@bbnplanet.com
Phone (617) 873-3126 - Fax (617) 873-5124
From: Thomas A. Russ
Subject: Re: Testing for EOF
Date: 
Message-ID: <TAR.95Jul13094955@hobbes.ISI.EDU>
Unless this was changed between CLtL2 and ANSI...

In article <...> ······@nic.near.net (Barry Margolin) writes:

I thought the default behavior for read-line was to signal an error if
it encountered an EOF at the start of reading a line unless the second
optional parameter (eof-error-p) were nil.

 > READ-LINE will return NIL (or whatever value you specify in the <eof-value>
 > parameter) when it reaches EOF.
 > 
 > (defun list-lines (filename)
 >   (with-open-file (stream filename :direction :input)
 >     (loop for line = (read-line stream)
                        ^^^^^^^^^^^^^^^^^^
                        (read-line stream nil)

 >           while (not (null line))
 >           collect line)))
 > 
 > When using READ it's often a good idea to provide a distinguishable
 > <eof-value> parameter (since NIL is likely to be valid, normal input), but
 > since READ-LINE always returns a string in the normal case and NIL in the
 > EOF case, the default is usually adequate here.

--
Thomas A. Russ,  USC/Information Sciences Institute          ···@isi.edu    
From: Gil Citro
Subject: Re: Testing for EOF
Date: 
Message-ID: <3u1r54$m88@usenet.srv.cis.pitt.edu>
Gil Citro (······@pitt.edu) wrote:
) I am starting to write Lisp and am trying to read the lines of a file
) into a list of strings such that each element of the list is one line
) from the file.  I have figured out how to read lines with read-line,
) but I can't fugure out how to test for the end of the file.  I'm sure
) it must be easy but I've looked through Steele's book on Common Lisp
) and through the FAQ and I can't find it.  I'd appreciate it if someone
) could give me a tip.

Thanks to those who replied.  I have the answer.  Sorry if this
question was very basic. I did try to find it on my own.  I very much
appreciate your taking the time to help me out!

-Gil
From: Gil Citro
Subject: Re: Testing for EOF
Date: 
Message-ID: <3u3sgb$ret@usenet.srv.cis.pitt.edu>
From ···@smi.med.pitt.edu Thu Jul 13 15:08:23 1995
Date: Thu, 13 Jul 95 15:06:59 -0400 (EDT)
From: Gil Citro <···@smi.med.pitt.edu>
To: ······@pitt.edu


Thanks again to everyone who helped me.  I got a request to post what
I did with it so here are the functions I wrote.  I'm sure by looking
at this code you will be able to tell I am a Lisp novice. Probably I
have reinvented the wheel with little pointy corners.  It does seem to
work however.

-Gil

;
;Return the concatenation of all strings in the list GIVEN-STRINGS.
;

(defun join (given-strings)
  (cond ((atom given-strings) (if (stringp given-strings) given-strings nil))
	((null given-strings) nil)
	((and (atom (car given-strings)) (not (stringp (car given-strings))))
	 (join (cdr given-strings)))
	((stringp (car given-strings))
	 (coerce (append (coerce (car given-strings) 'list)
			 (coerce (join (cdr given-strings))
				 'list)) 'string))
	((listp (car given-strings))
	 (coerce (append (coerce (join (car given-strings)) 'list)
			 (coerce (join (cdr given-strings))
				 'list)) 'string))))

;
;Return a stream to the file FILE-NAME, with the optional extention
;EXTENTION, if it exists, and nil otherwise.
;

(defun extended-open (file-name &optional extention)
  (if (probe-file file-name) (open file-name)
    (if	(and (not (null extention))
	     (probe-file (join (list file-name "." extention))))
	 (open (join (list file-name "." extention)))
      nil)))

;
;Return a list of strings each element of which is a line from the 
;file FILE-NAME, with optional extention EXTENTION, if it exists,
;and nil otherwise.
;

(defun lines-to-list (file-name &optional extention)
  (setq stream (extended-open file-name (if extention extention nil)))
  (if stream (do ((line (read-line stream) (read-line stream nil 'eof))
                  (list-of-lines nil (append list-of-lines (list line))))
                 ((eq line 'eof) list-of-lines)) nil))
From: Barry Margolin
Subject: Re: Testing for EOF
Date: 
Message-ID: <3u6hsc$gdu@tools.near.net>
In article <··········@usenet.srv.cis.pitt.edu> ······@pitt.edu (Gil Citro) writes:
>(defun join (given-strings)
>  (cond ((atom given-strings) (if (stringp given-strings) given-strings nil))
>	((null given-strings) nil)

The above clause will never be used, because (atom nil) is true, so the
null case will be handled by the previous clause.

>	((and (atom (car given-strings)) (not (stringp (car given-strings))))
>	 (join (cdr given-strings)))
>	((stringp (car given-strings))
>	 (coerce (append (coerce (car given-strings) 'list)
>			 (coerce (join (cdr given-strings))
>				 'list)) 'string))
>	((listp (car given-strings))
>	 (coerce (append (coerce (join (car given-strings)) 'list)
>			 (coerce (join (cdr given-strings))
>				 'list)) 'string))))

You seem to be doing an awful lot of work to emulate what

	(concatenate 'string ...)

does.  Also, it would simplify the use of the function if you used an
&REST argument, rather than requiring the caller to cons a list.

>(defun lines-to-list (file-name &optional extention)
>  (setq stream (extended-open file-name (if extention extention nil)))

Why the use of IF?  (if x x nil) is always equivalent to x (unless x is a
form that has side effects, since it could be executed twice).

>  (if stream (do ((line (read-line stream) (read-line stream nil 'eof))
>                  (list-of-lines nil (append list-of-lines (list line))))
>                 ((eq line 'eof) list-of-lines)) nil))
>

One problem with this function is that it never closes the stream.  You
should use WITH-OPEN-STREAM to ensure that this is done:

(with-open-stream (stream (extended-open file-name extension))
  (when stream
    (do ...)))

There are also some performance issues, but since it's not appropriate to
optimize early in the implementation cycle I won't go into them in detail.
I'll just point out that if you make use of high-level operators like LOOP,
you can usually depend on the implementor to have dealt with these issues
so you don't have to worry about them.
-- 
Barry Margolin
BBN Planet Corporation, Cambridge, MA
······@bbnplanet.com
Phone (617) 873-3126 - Fax (617) 873-5124
From: Scott D. Anderson
Subject: Re: Testing for EOF
Date: 
Message-ID: <ANDERSON.95Jul14175810@earhart.cs.umass.edu>
In article <··········@usenet.srv.cis.pitt.edu> ······@pitt.edu (Gil Citro) writes:

> I am starting to write Lisp and am trying to read the lines of a file
> into a list of strings such that each element of the list is one line
> from the file.  I have figured out how to read lines with read-line,
> but I can't fugure out how to test for the end of the file.  I'm sure
> it must be easy but I've looked through Steele's book on Common Lisp
> and through the FAQ and I can't find it.  I'd appreciate it if someone
> could give me a tip.
> 
> Thanks,
> Gil Citro
> 

As describe in CLtL2, the arglist for "read-line" is 

&optional input-stream eof-error-p eof-value recursive-p

The first arg is the stream you're reading from.  The second arg is
whether you want to get an error at the end of the file.  The third arg is
what you want returned if you don't want an error.  Ignore the fourth arg.

You don't want an error on eof, instead, you want some value you can test
for.  Let's use NIL.  So, here's some code to count the number of
characters in a file:

(defun char-count (file)
  (with-open-file (in file)
    (loop for x = (read-line in nil nil)
          while x
          sum (length x) into count
          finally (return count))))

Does that answer your question?

Scott D. Anderson
········@cs.umass.edu
From: Carl L. Gay
Subject: Re: Testing for EOF
Date: 
Message-ID: <CGAY.95Jul15101459@ix.cs.uoregon.edu>
   From: ········@earhart.cs.umass.edu (Scott D. Anderson)
   Date: 14 Jul 1995 21:58:10 GMT

   ······@pitt.edu (Gil Citro) writes:
   [...a question about read-line...]
   As describe in CLtL2, the arglist for "read-line" is 

   &optional input-stream eof-error-p eof-value recursive-p

   The first arg is the stream you're reading from.  The second arg is
   whether you want to get an error at the end of the file.  The third arg is
   what you want returned if you don't want an error.  Ignore the fourth arg.

   You don't want an error on eof, instead, you want some value you can test
   for.  Let's use NIL.  So, here's some code to count the number of
   characters in a file:

   (defun char-count (file)
     (with-open-file (in file)
       (loop for x = (read-line in nil nil)
	     while x
	     sum (length x) into count
	     finally (return count))))

   Does that answer your question?

Don't forget that the string returned by read-line doesn't include the
newline character, so if you want to count the number of characters in
a file using read-line you have to add 1 for each line, except the
last, which may not end with newline:

(defun char-count (file)
  (with-open-file (in file)
    (let ((count 0))
      (loop (multiple-value-bind (x y) (read-line in nil nil)
	      (if (null x)
		  (return count)
		(incf count (+ (length x) (if y 0 1)))))))))

I didn't see the original question, so I hope this still applies...

-Carl
From: Simon Brooke
Subject: Re: LisP aesthetics (was Re: Testing for EOF)
Date: 
Message-ID: <DCG7Fp.Hy0@rheged.dircon.co.uk>
To reiterate: this is about matters of taste and philosophy. If you
disagree, fine. I'm writing about what *I* find elegant and
understandable, not offering the law and the prophets.

Several people have sent me privately improvements on my suggested
function; thanks to all. Interestingly, one of the cleanest was posted
with a throw-away line:

> From: ·······@lindy.cs.umass.edu (Robert St. Amant)
> Why go halfway?  How about the function below:

> (defun char-count (file)
>   (with-open-file (stream file)
>     ;; New and improved, with 100% more recursion
>     ;; and no ugly iteration
>     (labels ((process-line (&optional line (count 0))
>                (if (eq line :eof)
> 		   count
> 		   (process-line (read-line stream nil :eof)
> 				 (+ count (length line) 1)))))
>       (process-line))))

> I wrote the above with tongue in cheek; I actually think it's pretty
> awful.  While Simon writes that LisP doesn't pretend to be like a
> human language, people do have to read it, and I think the original
> code, with the loop, is a lot clearer.

This guy baffles me. Of course we write source code to communicate
with other people. That's why we develop clear, elegant, orthogonal,
unambiguous codes. It's no use pretending that natural language is
clear or elegant or orthogonal or unambiguous. It ain't. It's a
complete mystery that it's usable for communication at all.

LisP in its cleaner forms (yes, Schemers, you may take a bow) is a
pure language of communication in which it is difficult to write a
line which is not poetic, transparent, acute.

I can read Robert's 'tongue in cheek' function and immediately
appreciate its force. His second version, with LOOP, is to me much
less clear.

Scott Anderson makes the same argument:

In article <······················@earhart.cs.umass.edu>,
>
>Simon has a good point.  Common Lisp has several "sub-languages" that are
>completely different from Lisp.  One is the LOOP macro, with its keyword
>syntax, and another is the FORMAT language.  (Quick, what does
>····@~a~~^,~}" do?)  
>
>First, is LOOP simple and clear?  Yes, I think simple stuff is clear:
>
>    (loop for i from 1 to 10 collect i)
>
>versus the following or any of its variants
>
>    (let ((list nil)) 
>       (do ((i 1 (1+ i)))
>           ((>= i 10) (nreverse list))
>        (push i list)))
>
>I'll grant that complicated uses of LOOP can be unclear, but I think
>that's the nature of the problem, not the syntax of LOOP.

But iteration rarely produces a clear statement of a complex problem.
Recursion is almost always clearer for the reader (let the compiler
take care of the efficiency issues):

(defun series (limit (n 1))		;; Cambridge LISP, not CL
					;; I *HATE* keywords!
  (cond ((eq n limit) (list n))
	(t (cons n (series limit (add1 n))))))

You claim this is not clear? You claim that interpreting the above
needs special knowledge? Well, of course it does, and anyone can see
it does. But interpreting (loop for i from 1 to 10 collect i) also
requires special knowledge. What will it return? A list? Where does it
say so? What order will the list be in? It's superficial clarity hides
deep ambiguity.

Most importantly, it is *another* special language. So the LisPer who
uses the loop macro needs not one special language, but two. They're
quite different and have quite different grammar, yet
loop-macro-language provides precisely no additional expressive power.
It's simply introducing a new area for confusion and error.

Back, briefly, to Scott:

>If LOOP can be simple, what about Lisp "design philosophy," as Simon puts
>it?  What's the point of all those parentheses, if we can do without them
>in languages like LOOP?
>
>I believe that the point of Lisp syntax is to write code in a form that is
>easily written by or analyzed by a program, particularly a Lisp program.

No: that's to put the cart before the horse. Yes, it's a fortunate
side-effect that making a language clear and elegant for human beings
to understand also makes it easy for machines to analyse. 

It's easy to analyse because it's easy to analyse. I spend a great
deal of my time analysing programs, and I appreciate clarity very
greatly. 

>I disagree with Simon about simplicity, clarity, and even aesthetics. I
>think Simon's new subject for this thread, "aesthetics," isn't right.  I
>don't mind Lisp's parentheses--I kinda like them--but they're there for
>function, not beauty.

But function *is* beauty, in code just as in physical structure. We
appreciate a suspension bridge because it resolves the forces on it
with the least possible use of material, the clearest, simplest shape.
Lists *are* that clearest, simplest shape for containing meaning.

(with (one (single) 
	   (orthogonal) 
	   (building block)) 
      (the delimited list))

we can build what those who use languages which lack a clear
mathematical basis require a 

wholly baroque( and arbitrary)
{
     collection( &of, widelyvarying, syntactic->junk);
}

Seriously: which do you prefer? Which do you find easier to read?


-- 
------- ·····@rheged.dircon.co.uk (Simon Brooke)

			-- mens vacua in medio vacuo --
From: J W Dalton
Subject: Re: LisP aesthetics (was Re: Testing for EOF)
Date: 
Message-ID: <DD5nFy.5ps@festival.ed.ac.uk>
········@earhart.cs.umass.edu (Scott D. Anderson) writes:

>In ·····@rheged.dircon.co.uk (Simon Brooke) writes:

>> ······@pitt.edu (Gil Citro), a new LisPer, posted a question about
>> slurping a text file into a list of strings, and Barry Margolin and
>> Scott Anderson responded with very similar sample solutions. Both made
>> use of the new LOOP macro. Here is Scott's solution:
>> 
>> >   From: ········@cs.umass.edu (Scott D. Anderson)
>> >   Date: 14 Jul 1995 21:58:10 GMT
>> 
>> >   (defun char-count (file)
>> >     (with-open-file (in file)
>> >       (loop for x = (read-line in nil nil)
>> >	     while x
>> >	     sum (length x) into count
>> >	     finally (return count))))
>> 
>> I read it, and didn't believe it, and then read it again and didn't
>> understand it, and finally read it and profoundly disliked it. We all
>> have our own ideas about what the soul of LisP is, and one of the good
>> things about LisP is undoubtedly the facility with which macros can be
>> used to implement new mini-languages.

>Even though Simon has singled me out for an especially visible place in
>his hall of shame, I am going to -- *partially* -- agree with him.  I'm
>not going to agree that my code is unreadable or ugly.  When someone asked
>me about Simon's article, I was able to recite my code from memory, which
>I don't think could be done if it weren't fairly clear and simple.  But
>I'll leave that to others to judge.

Well, why does it have the count variable?  The following seems
to work:

  (defun char-count (file)
    (with-open-file (in file :direction :input)
      (loop for line = (read-line in nil nil)
            while line
            sum (length line))))

Such simple uses of loop could be rewritten in a more Lispy way
without much trouble.  A reduce applied a lazy list of file lines,
for example.  There are also some fairly simple and efficient
collection macros that one might use.

>What's the point of all those parentheses, if we can do without them
>in languages like LOOP?

>I believe that the point of Lisp syntax is to write code in a form that is
>easily written by or analyzed by a program, particularly a Lisp program.

That may be *a* point.

But I prefer Lisp syntax even apart from macros, and I've happily
used Lisps that didn't have macros.  I do not in any way put up
with the Lisp syntax in order to get some compensating benefits.

I don't mind if you see things differently, but you shouldn't
say "_the_ point of Lisp syntax" [emphasis added].

>I disagree with Simon about simplicity, clarity, and even aesthetics. I
>think Simon's new subject for this thread, "aesthetics," isn't right.  I
>don't mind Lisp's parentheses--I kinda like them--but they're there for
>function, not beauty. 

That's an opinion, sure.

BTW, the source syntax of Lisp could be fairly different without
changing how macros worked at all.  Most Lisp programmers have
preferred Lisp syntax to such alternatives.

-- jd
From: Cyber Surfer
Subject: Re: LisP aesthetics (was Re: Testing for EOF)
Date: 
Message-ID: <808236420snz@wildcard.demon.co.uk>
In article <··········@festival.ed.ac.uk>
           ····@festival.ed.ac.uk "J W Dalton" writes:

> But I prefer Lisp syntax even apart from macros, and I've happily
> used Lisps that didn't have macros.  I do not in any way put up
> with the Lisp syntax in order to get some compensating benefits.

Same here. In fact, I feel that I put up with _infix_ notation, when
I use other languages. I felt this way long before I discovered Lisp,
in the mid-80s. A few years before that, I discovered Forth - at a
time when I was playing with non-infix ideas already.

> BTW, the source syntax of Lisp could be fairly different without
> changing how macros worked at all.  Most Lisp programmers have
> preferred Lisp syntax to such alternatives.

What I'd love is a way of defining symbols as infix, as you can with
some Prolog parsers. That way, I could have prefix most of the time,
and infix for those rare times when I have a use for it (ie, arithmetic).
-- 
<URL:http://www.demon.co.uk/community/index.html>
<URL:http://www.enrapture.com/cybes/namaste.html>
"You can never browse enough."