From: smeckler
Subject: Newbie cluelessness continued...
Date: 
Message-ID: <b7s3tc$lt1$1@newsg1.svr.pol.co.uk>
Hello,

I'm getting started with CLISP and have run straight into trouble :(

OK, this code should get me the first line of a file, right?

(defun readfile(path)
 (progn
  (setf mystream (open path :direction :input))
  (print (read-line mystream))
  (close mystream)))

First question - when I invoke this, it prints the line but also prints
"*** - EVAL: variable L has no value".  What is that about?

Second question - Can anyone give me some general tips on how to develop
with CLISP (vague, I know).  Obviously I shouldn't be typing code like the
above into the command line, but in an editor (notepad's fine for the
moment).  How do I then use what I've written?  Using the compile-file
function to compile the above just gives me a load of "MYSTREAM is neither
declared nor bound, it will be treated as if it were declared SPECIAL.",
which I don't understand, and it doesn't work, bah!  Is compiling the only
option?

cheers all...

p.s. Sorry about all the dumb questions but I don't have much time outside
of work to spend on this, and so your assistance is really invaluable to me.

From: Matthew Danish
Subject: Re: Newbie cluelessness continued...
Date: 
Message-ID: <20030419155026.O13181@lain.cheme.cmu.edu>
On Sat, Apr 19, 2003 at 07:20:43PM +0100, smeckler wrote:
> Hello,
> 
> I'm getting started with CLISP and have run straight into trouble :(
> 
> OK, this code should get me the first line of a file, right?
> 
> (defun readfile(path)
>  (progn
>   (setf mystream (open path :direction :input))
>   (print (read-line mystream))
>   (close mystream)))
> 
> First question - when I invoke this, it prints the line but also prints
> "*** - EVAL: variable L has no value".  What is that about?

Not quite sure.  But there are other problems with this code.

> Second question - Can anyone give me some general tips on how to develop
> with CLISP (vague, I know).  Obviously I shouldn't be typing code like the
> above into the command line, but in an editor (notepad's fine for the
> moment).  

Many people favor a combination of Emacs with the ILISP package, and
your preferred implementation of Common Lisp (CLISP, in this case).  You
should at the very least be using an editor that is aware of Lisp
syntax; that knows how to match parenthesis, and preferably how to do
indentation.  Another option is the LispWorks personal edition
(http://www.lispworks.com/) which comes with an easy to install IDE.

> How do I then use what I've written?  Using the compile-file
> function to compile the above just gives me a load of "MYSTREAM is neither
> declared nor bound, it will be treated as if it were declared SPECIAL.",
> which I don't understand, and it doesn't work, bah!  Is compiling the only
> option?

COMPILE-FILE (and then LOAD) is an acceptable means of loading your file
into the running Lisp image; (load (compile-file "file")) is handy.  The
problem here is that your code is incorrect.

1. SETF cannot be used to create a new variable.  This is why you are
getting messages aboue MYSTREAM being "neither declared nor bound".  In
your case, it looks like you want to create a new lexical variable, and
an appropriate form to do that is LET.

(let ((mystream (open path :direction :input)))
  (print (read-line mystream))
  (close mystream))

SETF is only used to modify /existing/ variables (or places).

2. Common Lisp has a handy macro for doing what you want to do already;
plus it guarantees that the file will be closed!

(with-open-file (mystream path :direction :input)
  (print (read-line mystream)))

;; upon exit of the WITH-OPEN-FILE form, the stream is closed

3. A minor issue: the PROGN is unnecessary.  DEFUN implies a PROGN in
its body.

So:

(defun read-file (path)
  (with-open-file (in-stream path :direction :input)
    (print (read-line in-stream))))

I recommend that you start at http://www.cliki.net/ and
http://www.cliki.net/Document for more pointers.

-- 
; Matthew Danish <·······@andrew.cmu.edu>
; OpenPGP public key: C24B6010 on keyring.debian.org
; Signed or encrypted mail welcome.
; "There is no dark side of the moon really; matter of fact, it's all dark."
From: Tim Daly, Jr.
Subject: Re: Newbie cluelessness continued...
Date: 
Message-ID: <87r87y5ciy.fsf@tenkan.org>
"smeckler" <······················@hotmail.com> writes:

> Hello,
> 
> I'm getting started with CLISP and have run straight into trouble :(
> 
> OK, this code should get me the first line of a file, right?
> 
> (defun readfile(path)
>  (progn
>   (setf mystream (open path :direction :input))
>   (print (read-line mystream))
>   (close mystream)))

Speaking as a seasoned lisper (smirk), it's nicer if you write it more
like this:

(defun readfile (path)
  (with-open-file (mystream path :direction :input) 
    (print (read-line mystream))))

The form WITH-OPEN-FILE is like OPEN, but it will make sure that the
file gets closed no matter what.  It's one of the cooler things about
Lisp, IMHO.  It also lets you bind MYSTREAM, and gives you an implicit
PROGN, all in one swell foop.


> 
> First question - when I invoke this, it prints the line but also prints
> "*** - EVAL: variable L has no value".  What is that about?
> 

To be honest, I don't know.  Most implementations should complain
about the fact that you did a SETF of MYSTREAM without binding it,
unless of course you bound it with a DEFPARAMETER or DEFVAR somewhere.

> Second question - Can anyone give me some general tips on how to develop
> with CLISP (vague, I know).  Obviously I shouldn't be typing code like the
> above into the command line, but in an editor (notepad's fine for the
> moment).  

Notepad?!  Noooo!  

I use emacs with ilisp.  Are you working on losedoze?  If you are, and
you've not yet learned emacs, I would really recommend installing the
trial version of one of the commercial lisps - Corman, Lispworks, or
Allegro.  It's the same language, and it will give you a more friendly
environment to learn in.

> How do I then use what I've written?  Using the compile-file
> function to compile the above just gives me a load of "MYSTREAM is neither
> declared nor bound, it will be treated as if it were declared SPECIAL.",

Aha, so Clisp is a most implementation.  

Before you use a variable in your functions, you should introduce it
with LET or some other form.  Clisp will like you better for it.


> which I don't understand, and it doesn't work, bah!  Is compiling the only
> option?

No.  You can LOAD files too.

> cheers all...
> 
> p.s. Sorry about all the dumb questions but I don't have much time outside
> of work to spend on this, and so your assistance is really invaluable to me.
> 

Everyone has to start somewhere; there is no shame in being new.  Have fun!

-Tim


-- 
From: Mark Carter
Subject: Re: Newbie cluelessness continued...
Date: 
Message-ID: <d3c9c04.0304200457.6582c239@posting.google.com>
> Everyone has to start somewhere; there is no shame in being new.  Have fun!

OK, so what's wrong with this, then:

(defun read-file (arg-file-name)
  ( setq result () )
  (with-open-file (stream arg-file-name :direction :input)
		  (loop while (setq line (read-line stream nil)) do 
			(append result '(line))
			(print result) ;debug info
			;... more processing, ostensibly
			)
		  )
  result ;this returns the result
)

The idea is that you read the lines of a file into a list.  You can
then process the list. This should cut down on the nesting required.
The problem is that when I do:
(read-file "/autoexec.bat")
I get a whole bunch of NILs printed.

I'm sure that it must be something obvious, and that there are better
ways of doing it - but I'm new to lisp.
From: Tim Bradshaw
Subject: Re: Newbie cluelessness continued...
Date: 
Message-ID: <ey3n0ilnt2o.fsf@cley.com>
* Mark Carter wrote:
> (defun read-file (arg-file-name)
>   ( setq result () )
>   (with-open-file (stream arg-file-name :direction :input)
> 		  (loop while (setq line (read-line stream nil)) do 
> 			(append result '(line))
> 			(print result) ;debug info
> 			;... more processing, ostensibly
> 			)
> 		  )
>   result ;this returns the result
> )

APPEND is nondestructive, so this (a) doesn't ever change RESULT, and
if it did (using NCONC say) (b) copies it each time, and (c) in that
case would be quadratic in file length.

If you want to use basic tools:

  (with-open-file (...)
    (loop with r = '()
          for line = (read-line stream nil stream)
          until (eql line stream)
          do (push line r)
          finally (return (nreverse r))))

If you want to write idiomatic LOOP code:

  (with-open-file (...)
    (loop for line = (read-line stream nil stream)
          until (eql line stream)
          collect line))

--tim
From: Sascha Wilde
Subject: Re: Newbie cluelessness continued...
Date: 
Message-ID: <resd8b.6b1.ln@ID-133390.user.dfncis.de>
Tim Bradshaw <···@cley.com> wrote:

> * Mark Carter wrote:
> If you want to use basic tools:
>
>   (with-open-file (...)
>     (loop with r = '()
>           for line = (read-line stream nil stream)
>           until (eql line stream)
>           do (push line r)
>           finally (return (nreverse r))))

speaking of basic tools i would prefer using do rather when loop
(which IMHO is quite "unlispish") like:

(defun read-file (arg-file-name)
  (with-open-file (stream arg-file-name :direction :input)
    (do ((line nil (read-line stream nil stream))
	 (r nil (push line r)))
          ((eql line stream) (cdr (nreverse r))))))

just my two cent
-- 
Sascha Wilde
"Gimme about 10 seconds to think for a minute..."
From: Sascha Wilde
Subject: Re: Newbie cluelessness continued...
Date: 
Message-ID: <8vbg8b.hm.ln@ID-133390.user.dfncis.de>
Sascha Wilde <······@sha-bang.de> wrote:

> speaking of basic tools i would prefer using do rather when loop
> (which IMHO is quite "unlispish") like:
>
> (defun read-file (arg-file-name)
>   (with-open-file (stream arg-file-name :direction :input)
>     (do ((line nil (read-line stream nil stream))
> 	 (r nil (push line r)))
>           ((eql line stream) (cdr (nreverse r))))))

Having a second look on what I wrote, I think it might be not correct,
for the binding of r depends on line.  So I think do* would be the
right way:

    (do* ((line nil (read-line stream nil stream))
	 (r nil (push line r)))
          ((eql line stream) (nreverse (cdr r))))))

Being somewhat a cl-newbee, too, I'm not quite sure about that, but I
think the form should be correct now.

-- 
Sascha Wilde : "Do not dangle the mouse by its cable 
             : or throw the mouse at co-workers"
             : (sgi - Indigo2 Workstation Owner's Guide)
From: Rob Warnock
Subject: Re: Newbie cluelessness continued...
Date: 
Message-ID: <9dicnSpbQZDkXzajXTWc-w@speakeasy.net>
Sascha Wilde  <······@sha-bang.de> wrote:
+---------------
| Sascha Wilde <······@sha-bang.de> wrote:
| > ...i would prefer using do rather when loop...
| > (defun read-file (arg-file-name)
| >   (with-open-file (stream arg-file-name :direction :input)
| >     (do ((line nil (read-line stream nil stream))
| > 	 (r nil (push line r)))
| >           ((eql line stream) (cdr (nreverse r))))))
| 
| Having a second look on what I wrote, I think it might be not correct,
| for the binding of r depends on line.
+---------------

Not only that, but you're completely wasting the first pass through
the loop! [And thus pushing a redundant NIL on the result list, which
you have to get rid of later.]

+---------------
| So I think do* would be the right way:
| 
|     (do* ((line nil (read-line stream nil stream))
| 	 (r nil (push line r)))
|           ((eql line stream) (nreverse (cdr r))))))
+---------------

Again, still wasting the first pass through the loop. The way I always
used to code it [before turning to the dark side and learning to love LOOP]
was to use #=/## to duplicate the READ-LINE form, and also use a simple
CONS instead of PUSH (since you're NREVERSEing anyway -- but with no CDR):

	(defun read-file (arg-file-name)
	  (with-open-file (stream arg-file-name :direction :input)
	    (do ((line #1=(read-line stream nil nil) #1#)
		 (result nil (cons line result)))
		((not line) (nreverse result)))))


-Rob

p.s. I conventionally use NIL as the EOF marker with READ-LINE or
READ-CHAR since it's faster to test [and can't be returned by them],
reserving "stream" or some other unique constant [such as a read-time
gensym] for use with READ. Others prefer to use the same code pattern
all the time, to save thinking about it. As usual, YMMV...

-----
Rob Warnock, PP-ASEL-IA		<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: Sascha Wilde
Subject: Re: Newbie cluelessness continued...
Date: 
Message-ID: <2mkg8b.bv.ln@ID-133390.user.dfncis.de>
····@rpw3.org (Rob Warnock) wrote:

> Sascha Wilde  <······@sha-bang.de> wrote:
> +---------------
> | So I think do* would be the right way:
> | 
> |     (do* ((line nil (read-line stream nil stream))
> | 	 (r nil (push line r)))
> |           ((eql line stream) (nreverse (cdr r))))))
> +---------------

> Again, still wasting the first pass through the loop. The way I always
> used to code it [before turning to the dark side and learning to love LOOP]
> was to use #=/## to duplicate the READ-LINE form,

indeed this is much more elegant, and in fact exactly what I was
searching for...  :-)

> and also use a simple CONS instead of PUSH (since you're NREVERSEing
> anyway -- but with no CDR):
>
> 	(defun read-file (arg-file-name)
> 	  (with-open-file (stream arg-file-name :direction :input)
> 	    (do ((line #1=(read-line stream nil nil) #1#)
> 		 (result nil (cons line result)))
> 		((not line) (nreverse result)))))
[...]
> p.s. I conventionally use NIL as the EOF marker with READ-LINE or
> READ-CHAR since it's faster to test [and can't be returned by them],

I didn't changed any further code, for all I wanted to show was getting
rid of the loop macro, which I do not consider being the "dark side"
but also not a "basic tool" -- and I think it's non lisp syntax can be
quite irritating.

btw. I have to admit that I was wrong, concerning the need of do*,
line is only referenced in the modifier so do will do.
(I thought of canceling the article but was to slow...)
-- 
Sascha Wilde : "There are 10 types of people in the world. 
             : Those who understand binary and those who don't."
From: Kalle Olavi Niemitalo
Subject: Re: Newbie cluelessness continued...
Date: 
Message-ID: <87d6jhs1po.fsf@Astalo.kon.iki.fi>
············@ukmail.com (Mark Carter) writes:

> 		  (loop while (setq line (read-line stream nil)) do 
> 			(append result '(line))
> 			(print result) ;debug info
> 			;... more processing, ostensibly
> 			)

APPEND returns a new list that contains all the elements of the
lists given as arguments.  It does not modify the original lists.
Because your loop ignores the value returned by APPEND, the call
has no effect.

The second argument of APPEND is wrong too.  '(line) means a
list containing the symbol LINE.  It does not use the variable
LINE.  For that, you can do (list line) or `(,line).

Also note that using APPEND in this manner requires scanning and
copying the whole old list each time a line is added in it.  Here
are some more efficient methods:

 - Keep a pointer to the tail of the list.  Add new elements by
   SETF of CDR, or perhaps NCONC, and then update the pointer.
   You may have to handle the first element separately.

 - Push the elements in the front of the list.  Reverse the list
   after the loop.

 - Use the COLLECT keyword of LOOP.
From: Mark Carter
Subject: (Thanks) Re: Newbie cluelessness continued...
Date: 
Message-ID: <d3c9c04.0304201156.7c416baf@posting.google.com>
Many thanks to Kalle and Tim for the responses I received ... I've
been making schoolboy errors, for sure! I'm only a few hours in to
lisp, not counting the grinding tedium of setting up emacs, clisp, and
documentation (which sure could use some examples).

My approach has been one of "learning by doing", rather than trying to
absorb a wodge of text.

> ············@ukmail.com (Mark Carter) writes:
> 
> > 		  (loop while (setq line (read-line stream nil)) do 
> > 			(append result '(line))
> > 			(print result) ;debug info
> > 			;... more processing, ostensibly
> > 			)
> 
> APPEND returns a new list that contains all the elements of the
> lists given as arguments.  It does not modify the original lists.
> Because your loop ignores the value returned by APPEND, the call
> has no effect.

[snip]
From: Kenny Tilton
Subject: Re: Newbie cluelessness continued...
Date: 
Message-ID: <3EA2C460.4060609@nyc.rr.com>
Kalle Olavi Niemitalo wrote:
> ············@ukmail.com (Mark Carter) writes:
> 
> 
>>		  (loop while (setq line (read-line stream nil)) do 
>>			(append result '(line))
>>			(print result) ;debug info
>>			;... more processing, ostensibly
>>			)
> 
> The second argument of APPEND is wrong too.  '(line) means a
> list containing the symbol LINE.  It does not use the variable
> LINE.  For that, you can do (list line) or `(,line).

Right, and just to beat this point to death:

Lisp tutorials tend to do a lot of '(1 2 3) and '(red green blue) stuff, 
so newbies understandably think one makes a list with a form like: 
'(<what> <ever>). But lo and behold, the character ' (aka quote), well, 
quotes its argument:

(let ((line "dont quote me on this"))
    (print 'line)
    (print line))

produces:

line
"dont quote me on this"

and while (print '(line)) would print "(line)", (print (line))...

...well, since (line) is not quoted, Lisp will try to invoke the 
function named line, and produce a compiler or interpreter run-time error.

In your case, line is a local variable to which the input gets bound. 
you don't want to see a result like "(line line line line)", so you do 
not want to use quote, you want to use one of the alternatives kalle 
offered (and probably the more straightforward (list line)). The 
compiler will evaluate the unquoted form LINE, come up
with the text just read, and send that to LIST.


-- 

  kenny tilton
  clinisys, inc
  http://www.tilton-technology.com/
  ---------------------------------------------------------------
"Everything is a cell." -- Alan Kay
From: Kalle Olavi Niemitalo
Subject: Re: Newbie cluelessness continued...
Date: 
Message-ID: <87r87wj46w.fsf@Astalo.kon.iki.fi>
Kenny Tilton <·······@nyc.rr.com> writes:

> ...well, since (line) is not quoted, Lisp will try to invoke the
> function named line, and produce a compiler or interpreter run-time
> error.

Compiling the form must not signal an error; see the end of CLHS
section 3.2.2.3.  A warning might be OK though.
From: Kalle Olavi Niemitalo
Subject: Re: Newbie cluelessness continued...
Date: 
Message-ID: <87brz2jme6.fsf@Astalo.kon.iki.fi>
"smeckler" <······················@hotmail.com> writes:

> First question - when I invoke this, it prints the line but also prints
> "*** - EVAL: variable L has no value".  What is that about?

I don't get that error with Debian CLISP 2.30-6 on i386.

> "MYSTREAM is neither declared nor bound, it will be treated as
> if it were declared SPECIAL."

The function uses the free variable MYSTREAM.  The standard does
not say what this should mean.  CLIPS chooses to treat it as if
you had declared (DECLARE (SPECIAL MYSTREAM)) but warns you.
To avoid of the warning, use a lexical variable:

  (defun readfile (path)
    (progn
      (let ((mystream (open path :direction :input)))
        (print (read-line mystream))
        (close mystream))))

You can simplify this a little by removing the PROGN.  DEFUN has
an implicit PROGN so you don't need an explicit one.

  (defun readfile (path)
    (let ((mystream (open path :direction :input)))
      (print (read-line mystream))
      (close mystream)))

You could also use UNWIND-PROTECT or WITH-OPEN-FILE so that
the stream will be reliably closed even if there is an error.

  (defun readfile (path)
    (with-open-file (mystream path :direction :input)
      (print (read-line mystream))))
From: Henrik Motakef
Subject: Re: Newbie cluelessness continued...
Date: 
Message-ID: <87r87xexos.fsf@interim.henrik-motakef.de>
"smeckler" <······················@hotmail.com> writes:

> Second question - Can anyone give me some general tips on how to develop
> with CLISP (vague, I know).  Obviously I shouldn't be typing code like the
> above into the command line, but in an editor (notepad's fine for the
> moment).

On the editing part: Other than Notepad, Emacs or the Lispworks trial,
you could also use the Jabberwocky IDE (http://jabberwocky.sf.net). I
prefer Emacs myself, but from what I've seen it seems to work well
with CLISP on Windows.

Another thing: At least on Unix, the CLISP implementation of the
function ED is quite sensible, typing `(ed "somefile.lisp")' opens the
file in your standard editor (the one from the EDITOR environment
variable that is; I can think of at least three other ways to specify a
'standard editor'), I guess something like this will work on Windows
as well.

Regards
Henrik
From: Erann Gat
Subject: Re: Newbie cluelessness continued...
Date: 
Message-ID: <gat-2104030938310001@192.168.1.51>
In article <············@newsg1.svr.pol.co.uk>, "smeckler"
<······················@hotmail.com> wrote:

> Hello,
> 
> I'm getting started with CLISP and have run straight into trouble :(
> 
> OK, this code should get me the first line of a file, right?
> 
> (defun readfile(path)
>  (progn
>   (setf mystream (open path :direction :input))
>   (print (read-line mystream))
>   (close mystream)))
> 
> First question - when I invoke this, it prints the line but also prints
> "*** - EVAL: variable L has no value".  What is that about?

My guess would be that you did (readfile l) without defining l, or
something like that.  There's nothing in your code that would produce this
error.

E.
From: smeckler
Subject: Re: Newbie cluelessness continued...
Date: 
Message-ID: <b80j8c$nd4$1@news8.svr.pol.co.uk>
Thanks all for the comprehensive answers.  Also apologies for offending
sensibilities by mentioning 'notepad' :)  I'll check out the emacs for
windoze when I get a moment.