From: Erik Winkels
Subject: Getting past distractions quickly
Date: 
Message-ID: <87sn8mjnph.fsf@xs4all.nl>
I hope this message will be helpful for other newcomers to Lisp.

Yesterday I was playing with my Lisp SDL bindings and its OpenGL
subsystem when I wanted to go beyond typing in quads and triagles by
hand.  I had some Elite (the game) VRML models lying around and since
it is a pretty easy fileformat[1], I decided to write a little
function that would tranform a model into a Lisp object.

However, since I'm still pretty firmly stuck in the Perl / C mindset,
it quickly became a pattern-matching mess in Lisp and too much code
than it really should be.  I won't include the dysfunctional function
here, but trust me on this.

However, I recalled some newsgroup postings on the subject of simply
using the Lisp reader (Reader) for such jobs.  That solved my problem
in less than fifteen minutes (two minutes if you're a pro I guess).

The result:

    (defun load-adder (adder-file)
      (let ((vrml "("))
        (with-open-file (file adder-file :if-does-not-exist :error)
          (do ((char (read-char file nil) (read-char file nil)))
              ((equal "EOF" (peek-char nil file nil "EOF")))
            (cond ((or (equal char #\{)
                       (equal char #\[)) (setf char #\( ))
                  ((or (equal char #\})
                       (equal char #\])) (setf char #\) ))
                  ((equal char #\#) (setf char #\space))
                  ((equal char #\,) (setf char #\space)))
            (setf vrml (concatenate 'string vrml (string char)))))
        (setf vrml (concatenate 'string vrml ")"))
        (read-from-string vrml)))

What it does: It starts a string VRML with an open parenthesis, since
the VRML-file doesn't.  The Reader will not parse it otherwise.  Then
it reads in the file character-by-character and replaces those
characters that proved to be troublesome to the Reader with either a
space or an open or close parenthesis; depending on the character's
function in the VRML file.

Once the file is read I close the string with another parenthesis and
put it through the Reader with read-from-string.  That gives me a Lisp
object with all the language's tools at my disposal.

It's still not pretty (the object), but I wanted to get past an hurdle
quickly to continue with the fun stuff.

(The code isn't an example of good Lisp style, especially the read-char
and peek-char declarations, and the use of read-from-string without
any safeguards.)


Regards,
Erik.

[1]  http://www.witchspace.free-online.co.uk/arcships.html
-- 
"You are trapped in a maze of screens and ssh sessions all alike. It
 is dark, and you are likely to log off the wrong account."  -- Nep

From: Tim Moore
Subject: Re: Getting past distractions quickly
Date: 
Message-ID: <a3c9dh$it0$0@216.39.145.192>
On 31 Jan 2002 20:59:54 +0100, Erik Winkels <·······@xs4all.nl> wrote:
>I hope this message will be helpful for other newcomers to Lisp.
>
Sometimes you've gotta do what you've gotta do to make progress, and
this is a good example of that.  You can do more or less the same
thing with changes to *read-table*, but in your case you solved your
problem with a minimum of hacking, so why bother...

One note though:  instead or repeatedly concatenating a character onto
the end of a string, it's much easier and faster to use an adjustable
vector with a fill pointer:

(let ((vrml (make-array '(16) 
			:element-type 'character 
			:adjustable t
			:fill-pointer 1
			:initial-element #\()))
  ...
  (vector-push-extend char vrml))

>Yesterday I was playing with my Lisp SDL bindings and its OpenGL
>subsystem when I wanted to go beyond typing in quads and triagles by
>hand.  I had some Elite (the game) VRML models lying around and since
>it is a pretty easy fileformat[1], I decided to write a little
>function that would tranform a model into a Lisp object.
>
...
>However, I recalled some newsgroup postings on the subject of simply
>using the Lisp reader (Reader) for such jobs.  That solved my problem
>in less than fifteen minutes (two minutes if you're a pro I guess).
>
>The result:
>
>    (defun load-adder (adder-file)
>      (let ((vrml "("))
>        (with-open-file (file adder-file :if-does-not-exist :error)
>          (do ((char (read-char file nil) (read-char file nil)))
>              ((equal "EOF" (peek-char nil file nil "EOF")))
>            (cond ((or (equal char #\{)
>                       (equal char #\[)) (setf char #\( ))
>                  ((or (equal char #\})
>                       (equal char #\])) (setf char #\) ))
>                  ((equal char #\#) (setf char #\space))
>                  ((equal char #\,) (setf char #\space)))
>            (setf vrml (concatenate 'string vrml (string char)))))
>        (setf vrml (concatenate 'string vrml ")"))
>        (read-from-string vrml)))
>
>What it does: It starts a string VRML with an open parenthesis, since
>the VRML-file doesn't.  The Reader will not parse it otherwise.  Then
>it reads in the file character-by-character and replaces those
>characters that proved to be troublesome to the Reader with either a
>space or an open or close parenthesis; depending on the character's
>function in the VRML file.
>
>Once the file is read I close the string with another parenthesis and
>put it through the Reader with read-from-string.  That gives me a Lisp
>object with all the language's tools at my disposal.
From: Kenny Tilton
Subject: Re: Getting past distractions quickly
Date: 
Message-ID: <3C59BCBA.87FDE667@nyc.rr.com>
Tim Moore wrote:
> 
> On 31 Jan 2002 20:59:54 +0100, Erik Winkels <·······@xs4all.nl> wrote:
> >I hope this message will be helpful for other newcomers to Lisp.
> >
> Sometimes you've gotta do what you've gotta do to make progress, and
> this is a good example of that.  You can do more or less the same
> thing with changes to *read-table*, but in your case you solved your
> problem with a minimum of hacking, so why bother...

agreed, but I am pretty weak on the reader so as I read Erik's article i
was looking forward to enhancements the gurus might offer. i am also
salivating as usual over the combo of Lisp and OpenGL. :)

kenny
clinisys
From: Raymond Wiker
Subject: Re: Getting past distractions quickly
Date: 
Message-ID: <86u1t2gp8x.fsf@raw.grenland.fast.no>
······@sea-tmoore-l.dotcast.com (Tim Moore) writes:

> On 31 Jan 2002 20:59:54 +0100, Erik Winkels <·······@xs4all.nl> wrote:
> >I hope this message will be helpful for other newcomers to Lisp.
> >
> Sometimes you've gotta do what you've gotta do to make progress, and
> this is a good example of that.  You can do more or less the same
> thing with changes to *read-table*, but in your case you solved your
> problem with a minimum of hacking, so why bother...
> 
> One note though:  instead or repeatedly concatenating a character onto
> the end of a string, it's much easier and faster to use an adjustable
> vector with a fill pointer:
> 
> (let ((vrml (make-array '(16) 
> 			:element-type 'character 
> 			:adjustable t
> 			:fill-pointer 1
> 			:initial-element #\()))
>   ...
>   (vector-push-extend char vrml))

        I'd use "with-output-to-string"...

        //Raymond        

-- 
Raymond Wiker                        Mail:  ·············@fast.no
Senior Software Engineer             Web:   http://www.fast.no/
Fast Search & Transfer ASA           Phone: +47 23 01 11 60
P.O. Box 1677 Vika                   Fax:   +47 35 54 87 99
NO-0120 Oslo, NORWAY                 Mob:   +47 48 01 11 60

Try FAST Search: http://alltheweb.com/
From: Tim Moore
Subject: Re: Getting past distractions quickly
Date: 
Message-ID: <a3ci8c$576$0@216.39.145.192>
On 31 Jan 2002 22:54:54 +0100, Raymond Wiker <·············@fast.no> wrote:
>······@sea-tmoore-l.dotcast.com (Tim Moore) writes:
>
>> One note though:  instead or repeatedly concatenating a character onto
>> the end of a string, it's much easier and faster to use an adjustable
>> vector with a fill pointer:
>> 
>> (let ((vrml (make-array '(16) 
>> 			:element-type 'character 
>> 			:adjustable t
>> 			:fill-pointer 1
>> 			:initial-element #\()))
>>   ...
>>   (vector-push-extend char vrml))
>
>        I'd use "with-output-to-string"...

Different strokes for different folks.  I'd personally avoid the
stream machinery when simply accumulating characters into a string
like this, but with-output-to-string is reasonable and quickly becomes
a effective solution in more complex situations.

Tim
From: Kaz Kylheku
Subject: Re: Getting past distractions quickly
Date: 
Message-ID: <cal68.6837$2x2.220649@news3.calgary.shaw.ca>
In article <··············@xs4all.nl>, Erik Winkels wrote:
>The result:
>
>    (defun load-adder (adder-file)
>      (let ((vrml "("))
>        (with-open-file (file adder-file :if-does-not-exist :error)
>          (do ((char (read-char file nil) (read-char file nil)))
>              ((equal "EOF" (peek-char nil file nil "EOF")))
>            (cond ((or (equal char #\{)
>                       (equal char #\[)) (setf char #\( ))
>                  ((or (equal char #\})
>                       (equal char #\])) (setf char #\) ))
>                  ((equal char #\#) (setf char #\space))
>                  ((equal char #\,) (setf char #\space)))
>            (setf vrml (concatenate 'string vrml (string char)))))
>        (setf vrml (concatenate 'string vrml ")"))
>        (read-from-string vrml)))

The problem here is that you are reading twice. Or rather, it's not a
problem, it just seems strange. You build up a printed Lisp representation
and then read-from-string. Is it that hard to construct the object
more directly? But hey it works, I'm just talking about chrome plating.

Also, whenever you find yourself doing 
(setf x (concatenate 'string x new-char)) you may might want to consider
the with-output-to-string macro, which gives you a temporary string-stream
that you can write-char into. That form will spit out that string when
the form is finished evaluating. Matter of style, I guess. I don't
want to push anything on anyone!

  (read-from-string 
    (with-output-to-string (stream)
      (... parsing loop extracting chars to c .... 
        (write-char c stream) ...)))

>What it does: It starts a string VRML with an open parenthesis, since
>the VRML-file doesn't.  The Reader will not parse it otherwise.  Then
>it reads in the file character-by-character and replaces those
>characters that proved to be troublesome to the Reader with either a
>space or an open or close parenthesis; depending on the character's
>function in the VRML file.

Perhaps adjusting the read table could help the reader; it's worth
investigating. You can temporarily change the read table, through
a dynamic binding, then in that dynamic context do read operations
through the customized table. I'm not very experienced with this, but
I'm aware of it as a potential solution that I can investigate more
deeply when a compatible problem comes up. :)

>(The code isn't an example of good Lisp style, especially the read-char
>and peek-char declarations, and the use of read-from-string without
>any safeguards.)

What safeguards do you need? If there is a reading problem, it will
signal a condition, which you cans deal with elsewhere.  You don't have
to contain the error; that's again an example of being stuck in the
aforementioned Perl and C mindset.  In Lisp we have conditions.

One thing you can do is learn to use restarts, which are a beautiful
feature of Lisp. Instead of worrying how to contain every possible error
that can happen somehwere, try to think whether there is a rational way to
continue processing if an error happens, should someone decide to do so.
If you can think of that, you can provide a restart.

With restarts you can implement your real responsibility, which is
to know that something can go wrong, and to indicate to the rest of
the program that you know how to recover from it, if that is possible.
Containing the error is not your concern when it's not your fault.
In this case, the error is a matter between the program which called
your function, and the erroneous data file it asked you to process.
Perhaps the program even *expects* the file to be erroneous, but wants
to you to extract as much from it as you can, in which case the last
thing you want to do is to smooth over errors, or to just report that
you cannot read the input and bail. The best you can do is provide a
protocol to continue extracting. And so in Lisp we have a way to
do the best thing.

Restarts are nice for interactive development too. Your debugger knows
that restarts are available and can give you a menu of them to select
interactively. Restarts allow you to explore the ability of your code
to keep going in the face of errors. As you explore that ability, you
can refine its response, to gradually develop a piece of software that
will shock users with its ability to deal with exceptional situations.
From: Erik Winkels
Subject: Re: Getting past distractions quickly
Date: 
Message-ID: <878zadb8v9.fsf@xs4all.nl>
···@accton.shaw.ca (Kaz Kylheku) writes:
> 
> The problem here is that you are reading twice. Or rather, it's not
> a problem, it just seems strange.

I didn't feel too good about it either, but I couldn't find a better
solution with a quick peek in the CLHS.  And since I was in "I want
this problem out of the way"-mode I was happy once I got the object
into Lisp.

I'll certainly look and see if it can be read in one go once I get
home tonight.


> > (The code isn't an example of good Lisp style, especially the
> > read-char and peek-char declarations, and the use of
> > read-from-string without any safeguards.)
>
> What safeguards do you need? If there is a reading problem, it will
> signal a condition, which you cans deal with elsewhere.

This was my worry:

http://groups.google.com/groups?hl=en&selm=sfwzp3344a7.fsf%40world.std.com

I don't not know enough about the matter to say anything more about
it, but I thought I'd just mention it so other newcomers wouldn't
start running production code on webservers with read-from-string.


Thanks for the rest of your response.  Very interesting!