From: Mirko
Subject: read columns from a file - how to use read-from-string?
Date: 
Message-ID: <9be48a23-e6d2-494f-b072-1949ce5925e3@p4g2000vba.googlegroups.com>
This thread
http://groups.google.com/group/comp.lang.lisp/browse_frm/thread/2bde165d6e368cae/5e9cc62893c3c3e2?lnk=gst&q=read+columns#
discussed reading columns from a file (among other things, ruby
included)

The lisp ideas used `read' to advance from field to field in a file.

I have a slightly different file structure (in tecplot format), where
the file has two headers that I first have to parse in order to parse
the columns.  So, I have to read lines, and then decide what to do.
(There is another solution that I discuss later on).
Here is my code so far:
  (with-open-file (stream file :direction :input
			  :if-does-not-exist :error)
    (let (header vars c-vars data)
      (loop for line = (read-line stream nil 'eof)
	 until (eq line 'eof)
	 do (cond
	      ((string= "#" (subseq line 0 1)) (push line header))
	      ((string= "VARIABLES" (subseq line 0 9))
	       (setf vars (split-sequence #\, (subseq line 12)))
	       (setf c-vars (length vars))
	       (setf data (make-array c-vars)))
 ...

The first two parts of `cond' will deal with the header, and after
that, I was thinking of having a `t' clause with code that will parse
the successively rad lines and store the contents.
I tried this for line parsing at the repl and got an infinite loop:
(let ((start 0) item)
	   (do ()
	       ((eq item 'end))
	     (multiple-value-bind (item start)
		 (read-from-string "1 2 3 4" nil 'end :start start)
	       (print start))))

The goal is to reset the `start' keyword in read-from-string using the
second value it returns during the previous read.  But, (on sbcl at
least), `start' does not get redefined in the call to `read-from-
string'.  I guess the multiple-value-bind evaluates it during
compilation and it is not changed.

So, (although a workaround is described below), I wonder how one would
do this?

As for the workaround, after reading the header, I exit the line-
reading loop, and then revert to using read to read column item by
column item all the way to the end.

Thanks,

Mirko

From: Thomas A. Russ
Subject: Re: read columns from a file - how to use read-from-string?
Date: 
Message-ID: <ymik54m47s1.fsf@blackcat.isi.edu>
Mirko <·············@gmail.com> writes:

> I have a slightly different file structure (in tecplot format), where
> the file has two headers that I first have to parse in order to parse
> the columns.  So, I have to read lines, and then decide what to do.
> (There is another solution that I discuss later on).
> Here is my code so far:
>   (with-open-file (stream file :direction :input
> 			  :if-does-not-exist :error)
>     (let (header vars c-vars data)
>       (loop for line = (read-line stream nil 'eof)
> 	 until (eq line 'eof)
> 	 do (cond
> 	      ((string= "#" (subseq line 0 1)) (push line header))
> 	      ((string= "VARIABLES" (subseq line 0 9))
> 	       (setf vars (split-sequence #\, (subseq line 12)))
> 	       (setf c-vars (length vars))
> 	       (setf data (make-array c-vars)))
>  ...
> 
> The first two parts of `cond' will deal with the header, and after

I would use some more specialized functions for doing the matching.
SUBSEQ needlessly creates a new string for doing the comparison.

For the first one I would use something like

   (eql #\# (char line 0))   ;; or CHAR=

For the second I would do something like

   (null (mismatch "VARIABLES" line :end2 9))

Also, you should do some testing of the length of the line that is being
read, since calling SUBSEQ (or any other accessor) with out-of-bounds
indices will generate an error.  You can easily get that information by
adding a
    as length = (length line)
iteration clause after the "for" clause in your LOOP.


> that, I was thinking of having a `t' clause with code that will parse
> the successively rad lines and store the contents.
> I tried this for line parsing at the repl and got an infinite loop:
> (let ((start 0) item)
> 	   (do ()
> 	       ((eq item 'end))
> 	     (multiple-value-bind (item start)
> 		 (read-from-string "1 2 3 4" nil 'end :start start)
> 	       (print start))))
> 
> The goal is to reset the `start' keyword in read-from-string using the
> second value it returns during the previous read.  But, (on sbcl at
> least), `start' does not get redefined in the call to `read-from-
> string'.  I guess the multiple-value-bind evaluates it during
> compilation and it is not changed.

Nope.  That is not the problem.

The problem is that multiple-value-BIND introduces a NEW binding of the
variables.  It works like having another LET form establishing a new set
of variables with that name.

The form you want is multiple-value-SETQ instead.  You would discover
that ITEM never changes outside the body of the MULTIPLE-VALUE-BIND
either.

Didactic digression.  You could define MULTIPLE-VALUE-BIND as follows:

  (defmacro multiple-value-bind ((&rest vars) value-form &body body)
    `(let (,@vars)
        (multiple-value-setq (,@vars) ,value-form)
        ,@body))


-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Peder O. Klingenberg
Subject: Re: read columns from a file - how to use read-from-string?
Date: 
Message-ID: <ks8wl25raa.fsf@netfonds.no>
Mirko <·············@gmail.com> writes:

> I tried this for line parsing at the repl and got an infinite loop:
> (let ((start 0) item)
> 	   (do ()
> 	       ((eq item 'end))
> 	     (multiple-value-bind (item start)
> 		 (read-from-string "1 2 3 4" nil 'end :start start)
> 	       (print start))))

The m-v-b creates new bindings, only visible in the body of the m-v-b.
That binding is discarded before the next round in the iteration.  So
each round in the iteration, start is 0 and item is nil.

Try this instead:

         (let ((start 0) item)
 	   (do ()
 	       ((eq item 'end))
 	     (multiple-value-setq (item start)
 		 (read-from-string "1 2 3 4" nil 'end :start start))
	     (print start)))

This actually updates the value of the outer bindings, which is what
you want.

> The goal is to reset the `start' keyword in read-from-string using the
> second value it returns during the previous read.  But, (on sbcl at
> least), `start' does not get redefined in the call to `read-from-
> string'.  I guess the multiple-value-bind evaluates it during
> compilation and it is not changed.

Wrong guess.  Read up on the difference between binding and
assignment.  I think there's a good essay on the subject floating
about (by Ron Garrett?) but I don't have a reference, sorry.

...Peder...
-- 
Sl�v uten dop.
From: gugamilare
Subject: Re: read columns from a file - how to use read-from-string?
Date: 
Message-ID: <a863bcb2-ee1f-4149-8f6c-873f17569e3e@e24g2000vbe.googlegroups.com>
On 12 maio, 11:28, ·····@news.klingenberg.no (Peder O. Klingenberg)
wrote:
> The m-v-b creates new bindings, only visible in the body of the m-v-b.
> That binding is discarded before the next round in the iteration.  So
> each round in the iteration, start is 0 and item is nil.
>
> Try this instead:
>
>          (let ((start 0) item)
>            (do ()
>                ((eq item 'end))
>              (multiple-value-setq (item start)
>                  (read-from-string "1 2 3 4" nil 'end :start start))
>              (print start)))

(setf (values item start) (read-from-string "1 2 3 4" nil 'end :start
start)) would do as well.
From: Mirko
Subject: Re: read columns from a file - how to use read-from-string?
Date: 
Message-ID: <0099d4fe-ef1d-4f4d-9d36-678813229154@r34g2000vbi.googlegroups.com>
On May 12, 11:04 am, gugamilare <··········@gmail.com> wrote:
> On 12 maio, 11:28, ·····@news.klingenberg.no (Peder O. Klingenberg)
> wrote:
>
> > The m-v-b creates new bindings, only visible in the body of the m-v-b.
> > That binding is discarded before the next round in the iteration.  So
> > each round in the iteration, start is 0 and item is nil.
>
> > Try this instead:
>
> >          (let ((start 0) item)
> >            (do ()
> >                ((eq item 'end))
> >              (multiple-value-setq (item start)
> >                  (read-from-string "1 2 3 4" nil 'end :start start))
> >              (print start)))
>
> (setf (values item start) (read-from-string "1 2 3 4" nil 'end :start
> start)) would do as well.

Thanks to both suggestions.  I was not aware of either possibility.

Mirko