From: Cad Bilbao
Subject: Unable to read a list with 'with-open-file'
Date: 
Message-ID: <604dab8.0111220655.28c1fb26@posting.google.com>
Hi!

I'm trying to read a list ("c:/input.txt") and storing it within
'myList' by using this piece of code:

-----------------//------------
(setf myList '())
(with-open-file (f "c:/input.txt" :direction :input)
  (loop for line = (read-line f nil nil)
     while line
       do ( 
         (setf myList(append myList line)
       );end-do
     );end-while  
   );end-loop
 );end-with
-------------------//-------------

But I get this error:

------------------//---------------
LISP error:
(SETF TEST::MYLIST (APPEND TEST::MYLIST LINE)) is invalid as a
function
-----------------//---------------

Any suggestion? Than you very much

From: Christophe Rhodes
Subject: Re: Unable to read a list with 'with-open-file'
Date: 
Message-ID: <squ1vmzvxr.fsf@cam.ac.uk>
···@bilbao.com (Cad Bilbao) writes:

> -----------------//------------
> (setf myList '())
> (with-open-file (f "c:/input.txt" :direction :input)
>   (loop for line = (read-line f nil nil)
>      while line
>        do ( 
>          (setf myList(append myList line)
>        );end-do
>      );end-while  
>    );end-loop
>  );end-with
> -------------------//-------------

Let's reformat this to be more like what a lisper would be happy to
read:

(setf myList '())
(with-open-file (f "c:/input.txt" :direction :input)
  (loop for line = (read-line f nil nil)
        while line
          do ((setf myList (append myList line)))))

So, I hope that it's obvious now that you have the syntax of the `do'
clause wrong -- you are trying to call the function 
(setf my-list ...), as 

> ------------------//---------------
> LISP error:
> (SETF TEST::MYLIST (APPEND TEST::MYLIST LINE)) is invalid as a
> function
> -----------------//---------------

is trying to tell you.

Apart from this, you may want to use
(defvar *my-list* '())
rather than 
(setf myList '()), as the setf of a variable that hasn't been
def[var|parameter]ed at top-level is undefined;

alternatively you might wish to do
(with-open-file (f ...)
  (loop for line = (read-line f nil nil)
        with my-list = '()
        while line
          collect line into my-list
        finally
          return my-list))

and use the return value.

Hope that helps,

Christophe
-- 
Jesus College, Cambridge, CB5 8BL                           +44 1223 510 299
http://www-jcsu.jesus.cam.ac.uk/~csr21/                  (defun pling-dollar 
(str schar arg) (first (last +))) (make-dispatch-macro-character #\! t)
(set-dispatch-macro-character #\! #\$ #'pling-dollar)
From: Erik Naggum
Subject: Re: Unable to read a list with 'with-open-file'
Date: 
Message-ID: <3215439930132888@naggum.net>
* Cad Bilbao
| (setf myList '())
| (with-open-file (f "c:/input.txt" :direction :input)
|   (loop for line = (read-line f nil nil)
|      while line
|        do ( 
|          (setf myList(append myList line)
|        );end-do
|      );end-while  
|    );end-loop
|  );end-with

  Well, get rid of the terribly confusing ;end-crap first (the comments do
  not even match what they say they match), and try to see how other people
  write their Common Lisp code.  If you do things differently from more
  experienced people, you do _not_ know better because you are new at this.
  That attitude may be at the core of your problem, actually, since you
  have not paid attention to how parentheses "work" in Common Lisp.  From
  your indentation style alone, it appears that you think they have a very
  different function than what they actually have.

  When you have figured out the invalid function error, retrace your steps
  and figure out where you got the terrible idea to use append, then do the
  same for that global variable.

  Otherwise, you have it mostly right.

| But I get this error:
| 
| ------------------//---------------
| LISP error:
| (SETF TEST::MYLIST (APPEND TEST::MYLIST LINE)) is invalid as a
| function
| -----------------//---------------
| 
| Any suggestion? Than you very much

  The error message is actually very precise.  Try to evaluate ((+ 1 1))
  and see what it says.  Compare with (+ 1 1).  Also count the number of
  parentheses you use in your code carefully.  Get an editor that helps you
  keep track of them and takes care of that indentention stuff for you, too.

///
-- 
  Norway is now run by a priest from the fundamentalist Christian People's
  Party, the fifth largest party representing one eighth of the electorate.
-- 
  Carrying a Swiss Army pocket knife in Oslo, Norway, is a criminal offense.
From: Tim Bradshaw
Subject: Re: Unable to read a list with 'with-open-file'
Date: 
Message-ID: <fbc0f5d1.0111230456.5f0d9dd5@posting.google.com>
···@bilbao.com (Cad Bilbao) wrote in message news:<···························@posting.google.com>...
> (setf myList '())
> (with-open-file (f "c:/input.txt" :direction :input)
>   (loop for line = (read-line f nil nil)
>      while line
>        do ( 
>          (setf myList(append myList line)
>        );end-do
>      );end-while  
>    );end-loop
>  );end-with

> Any suggestion? Than you very much


Four suggestions:

1. Almost half the lines in your program are comments which tell you
nothing that the editor will not immediately tell you too, except it
will not get it wrong if you change the program, whereas yhour
comments will likely rapidly become incorrect (in fact, they are
already incorrect).  Concentrate on comments that tell you things that
you or the editor can't immediately work out from the code.

(with-open-file (f "c:/input.txt" :direction :input)
  ;; third arg NIL means no error on EOF
  ;; fourth NIL means return NIL in that case.
  (loop for line = (read-line f nil nil)
        while line
        do ( 
            (setf myList (append myList line)))))

2. You have misunderstood the syntax of LOOP: all the forms following
DO until another LOOP keyword are evaluated, not a single list of
forms.

(with-open-file (f "c:/input.txt" :direction :input)
  ;; third arg NIL means no error on EOF
  ;; fourth NIL means return NIL in that case.
  (loop for line = (read-line f nil nil)
        while line
        do (setf myList (append myList line))))

3. Your algorithm is quadratic in file length, since APPEND is linear
in list length.  Obsessives will also worry that APPEND repeatedly
copies the list, but
that is a small problem.  Either build the list backwards and reverse
it, resulting in a linear algorithm, or use one of the
building-a-list-forwards tricks either via one of the COLLECT-style
macros or using LOOP's COLLECT clause:

(with-open-file (f "c:/input.txt" :direction :input)
  ;; third arg NIL means no error on EOF
  ;; fourth NIL means return NIL in that case.
  (loop for line = (read-line f nil nil)
        while line collect line into lines
        finally (setf mylist lines)))

4. The whole (SETF ...) then read the file and assign is really
horrible.  If this is not extracted from a function it's also
technically ill-defined.  If you want to define a top-level variable
do this:

(defparameter *my-lines*
              (with-open-file (f "c:/input.txt" :direction :input)
                ;; third arg NIL means no error on EOF
                ;; fourth NIL means return NIL in that case.
                (loop for line = (read-line f nil nil)
                      while line collect line)))

If you want to write a function that returns the lines in the file do
this:

(defun read-lines-from-file (file)
  ;; Return a list of all the lines in FILE.  FILE is either a string
or
  ;; a pathname
  (with-open-file (in file :direction ':input)
    ;; no real line can be NIL, so we don't need to worry about
    ;; inventing a unique return value here
    (loop for line = (read-line in nil nil)
          while line collect line)))

(defparameter *my-lines* (read-lines-from-file "c:/input.txt"))

Note that this program is the same length in lines as yours, but
includes four lines of comments which are actually useful: two of them
describe the function, and two describe why the READ-LINE works OK
(when, say, READ would need more care), one blank line for
readability, and defines a function which can be used elsewhere.  And
it works (unless I made a typo, it is untested code), and is
efficient.

--tim

Note for CLL people: I think this is a great use of LOOP.  It's *so*
easy to see what is happening here:

   loop for line = <get next line from file, NIL on EOF>
        while line collect line

Of course it's not pure functional Lisp.  But *so what*?