From: Harag
Subject: NEWBIE: code crit neaded...its not looking lisp like
Date: 
Message-ID: <bo3ksu$58h$1@ctb-nnrp2.saix.net>
To start with I will openly admit that I come from a vb back ground so that
is a set back already (lol) but I would really appreciate some crit of this
piece of code. I have read enough lisp about lisp (but have not
practiced/used lisp enough) and seen enough code to know that this function
could be a lot more lisp/functional like.

the fist thing you would notice, beside all the lets, is that I am using
strings instead of lists because every time I read anything on lisp they
cons'ing is slow = bad (but optomization from the word go is bad to they
tell me!!!), but I have this feeling that what I'm doing is even worse ...
it looks like rape to me...

O the other thing is I might be over doing the streams thing or am I
underdoing it should I abandon the strings for more streams?

the function gets the following from a file :
        first-rec: 000 | last-rec: 010 | first-line: 000 | last-line: 021 |
free-list: 022 | rec-size: 22 |
loops through it and breaks it up into a couple of variables ... I started
having doubts before I got round to trying to use the variables/strings

(defun read-head (stream)
  ;;forcing read to beginning of file
  (file-position stream 1)
  (let (
        (headstrm (make-string-input-stream (read-line stream)))
        (first "")
        (last "")
        (lastline "")
        (free "")
        (size "")
        (count 0)
        )

    (do
 ((instr (read-char headstrm nil :eof) (read-char headstrm nil :eof)))
 ((equal instr :eof) first)
         (if (equal instr #\|)
             (setf count (+ count 1))
             (case count
               (0 (setf first (concatenate 'string first (string instr))))
               (1 (setf last (concatenate 'string last (string instr))))
               (2 (setf lastline (concatenate 'string lastline (string
instr))))
               (3 (setf free (concatenate 'string free (string instr))))
               (4 (setf size (concatenate 'string size (string instr))))
               )
           )
        )

    )
)

From: james anderson
Subject: Re: NEWBIE: code crit neaded...its not looking lisp like
Date: 
Message-ID: <3FA55FED.407C93A0@setf.de>
Harag wrote:
> ...
> 
> the function gets the following from a file :
>         first-rec: 000 | last-rec: 010 | first-line: 000 | last-line: 021 |
> free-list: 022 | rec-size: 22 |
> loops through it and breaks it up into a couple of variables ... I started
> having doubts before I got round to trying to use the variables/strings
> 
> (defun read-head (stream)
>   ;;forcing read to beginning of file
>   (file-position stream 1)

 i suspect, at least, that this is intended to be '0' instead of '1'.
 one must be aware that positioning and indexing in lisp is zero-based

> [not sure why the remainder was trying to do what it was trying to do]

read up on read tables. it is often possible to set the character syntax
attributes such the files with simple delimiter syntax can be read directly.

(defParameter *record-readtable* (copy-readtable))

(set-syntax-from-char #\: #\space *record-readtable*)
(set-syntax-from-char #\| #\space *record-readtable*)

(defun read-record (stream)
  (let ((line (read-line stream nil nil))
        (alist nil)
        (*readtable* *record-readtable*))
    (when line
      (with-input-from-string (stream line)
        (do* ((key (read stream) (read stream nil nil))
              (value (when key (read stream)) (when key (read stream))))
            ((null key) (reverse alist))
          (push (cons key value) alist))))))


(with-input-from-string (stream
"first-rec: 000 | last-rec: 010 | first-line: 000 | last-line: 021 |
free-list: 022 | rec-size: 22
first-rec: 100 | last-rec: 110 | first-line: 100 | last-line: 121 | free-list:
122 | rec-size: 122
first-rec: 200 | last-rec: 210 | first-line: 200 | last-line: 221 | free-list:
222 | rec-size: 222
")
  (loop (unless (print (read-record stream)) (return))))


==>

? 
((FIRST-REC . 0) (LAST-REC . 10) (FIRST-LINE . 0) (LAST-LINE . 21) (FREE-LIST
. 22) (REC-SIZE . 22)) 
((FIRST-REC . 100) (LAST-REC . 110) (FIRST-LINE . 100) (LAST-LINE . 121)
(FREE-LIST . 122) (REC-SIZE . 122)) 
((FIRST-REC . 200) (LAST-REC . 210) (FIRST-LINE . 200) (LAST-LINE . 221)
(FREE-LIST . 222) (REC-SIZE . 222)) 
NIL
From: Harag
Subject: Re: NEWBIE: code crit neaded...its not looking lisp like
Date: 
Message-ID: <bo3rj7$9ua$1@ctb-nnrp2.saix.net>
Well your rewrite says more than a thousand words....but let me try and
translate just to make sure I've learnt the right lessons...

> (defParameter *record-readtable* (copy-readtable))
>
> (set-syntax-from-char #\: #\space *record-readtable*)
> (set-syntax-from-char #\| #\space *record-readtable*)


After some franting reading I must agree that read tables are the way to go
because you get to use the richness of the lisps read by setting the
'delimiters' to be equal to 'space' you get the reader to read the line one
'field' (lisp object?) at a time surcomventing all that character reading
stuff....this makes your version a lot simpler and faster too I susspect...

O and by using the read table I think you are endorsing the fact that I use
the line as stream...?

>
> (defun read-record (stream)
>   (let ((line (read-line stream nil nil))
>         (alist nil)
>         (*readtable* *record-readtable*))
>     (when line
>       (with-input-from-string (stream line)
>         (do* ((key (read stream) (read stream nil nil))
>               (value (when key (read stream)) (when key (read stream))))
>             ((null key) (reverse alist))
>           (push (cons key value) alist))))))

The main difference between your version and mine when it comese to
'lispness' is that by simply using a stack you do a way with all the ugly
'lets' (after doing some re-reading I redescovered the fact that push is
equavilant to setf and thus the lets in a sense) and the case statement and
you have a single structure(the stack / list) to return from the function.
What you get in the bargain is a more flexable and thus more re-usable
version to because the record format (sequence and or field count) can now
be changed without making changes to the function.

This looks to me to be a intuative 'lispish'  function, the use of cons and
the stack (list) is what lisp is about correct? However if one should
optimise for speed later on would a hashtable be better?

>
> (with-input-from-string (stream
> "first-rec: 000 | last-rec: 010 | first-line: 000 | last-line: 021 |
> free-list: 022 | rec-size: 22
> first-rec: 100 | last-rec: 110 | first-line: 100 | last-line: 121 |
free-list:
> 122 | rec-size: 122
> first-rec: 200 | last-rec: 210 | first-line: 200 | last-line: 221 |
free-list:
> 222 | rec-size: 222
> ")
>   (loop (unless (print (read-record stream)) (return))))
>

In summary I think the following observations would be true. Fistly lists
gives you the opportunity to work with groups of
data/fields/objecst/thingies in a simple and effective way and this should
be used. Secondly using lists gives you a jump start because of the rich
list manipulation functionality lisp has and the fact that you can
plunder/use lisps internals to do your work.

Everything I have read so far has tried to convey these facts but yet when I
tried my hand at it I had missed it some how...maybe I have read to far
before trying my hand at it....thus the confusion about to cons or not
to...I think in my heart I was still not convinced that I should write the
program simple and quick and then look at optimising ...its hard when you
come from a specing to death paradigm...

Thanks for your help James.

Harag

"spelling fairy ... O why has thou abandoned me....!!!!"
From: james anderson
Subject: Re: NEWBIE: code crit neaded...its not looking lisp like
Date: 
Message-ID: <3FA579D0.BD6C3546@setf.de>
Harag wrote:
> ...this makes your version a lot simpler and faster too I susspect...
> 
no comment on the speed issue.

> O and by using the read table I think you are endorsing the fact that I use
> the line as stream...?

there's too little information to decide much here either. i've no clear idea
what's in the file. if it is, in fact, either just a single header line
followed by some other syntax, or if the lines are all uniform, then buffering
the line makes little sense. the intermediate buffer is an easy way to handle
lines which contain a varying number of entries. in which case, if space
performance mattered, one could then reuse a single adjustable buffer rather
than using read-line.

> 
> ...
> The main difference between your version and mine when it comese to
> 'lispness' is that by simply using a stack you do a way with all the ugly
> 'lets' (after doing some re-reading I redescovered the fact that push is
> equavilant to setf and thus the lets in a sense) and the case statement and
> you have a single structure(the stack / list) to return from the function.
> What you get in the bargain is a more flexable and thus more re-usable
> version to because the record format (sequence and or field count) can now
> be changed without making changes to the function.

for reading that particular header, yes, translating it into an alist would
likely make sense. given more information it might have been more appropriate
to map a function directly across the header contents without building a list,
or set variables to the values, or collect the values in a plist and call a
function with the contents. (there's too little information to say)

> 
> This looks to me to be a intuitive 'lispish'  function, the use of cons and
> the stack (list) is what lisp is about correct? However if one should
> optimise for speed later on would a hashtable be better?

it would depend on what the application needed to do with the values.

> 
> ...
> In summary I think the following observations would be true. Fistly lists
> gives you the opportunity to work with groups of
> data/fields/objecst/thingies in a simple and effective way and this should
> be used. Secondly using lists gives you a jump start because of the rich
> list manipulation functionality lisp has and the fact that you can
> plunder/use lisps internals to do your work.

yes and yes. (except, they're not "internals".)


...
From: Harag
Subject: Re: NEWBIE: code crit neaded...its not looking lisp like
Date: 
Message-ID: <bo3vln$ck5$1@ctb-nnrp2.saix.net>
> the line makes little sense. the intermediate buffer is an easy way to
handle
> lines which contain a varying number of entries. in which case, if space
> performance mattered, one could then reuse a single adjustable buffer
rather
> than using read-line.

so what your saying is work with the stream oe atleast dont set variables
unnecessary?


> for reading that particular header, yes, translating it into an alist
would
> likely make sense. given more information it might have been more
appropriate
> to map a function directly across the header contents without building a
list,

what do you mean by mapping a function directly accross the contents?


> > In summary I think the following observations would be true. Fistly
lists
> > gives you the opportunity to work with groups of
> > data/fields/objecst/thingies in a simple and effective way and this
should
> > be used. Secondly using lists gives you a jump start because of the rich
> > list manipulation functionality lisp has and the fact that you can
> > plunder/use lisps internals to do your work.
>
> yes and yes. (except, they're not "internals".)
>
at my 'lisp level' everything is still internals(balck box like) its like
scraping paint off an old car layer for layer...lol :o} but I will get there
in the end...
From: james anderson
Subject: Re: NEWBIE: code crit neaded...its not looking lisp like
Date: 
Message-ID: <3FA5947B.932B4B2B@setf.de>
Harag wrote:
> 
> ...
> 
> > for reading that particular header, yes, translating it into an alist
> would
> > likely make sense. given more information it might have been more
> appropriate
> > to map a function directly across the header contents without building a
> list,
> 
> what do you mean by mapping a function directly accross the contents?
> 
(defun total-records (stream)
  (let ((totals nil)
        (*readtable* *record-readtable*))
  (flet ((interpret-entry (key value) (incf (getf totals key 0) value)))
    (do* ((key (read stream) (read stream nil nil))
          (value (when key (read stream)) (when key (read stream))))
         ((null key) totals)
      (interpret-entry key value)))))

(with-input-from-string (stream
"first-rec: 000 | last-rec: 010 | first-line: 000 | last-line: 021 |
free-list: 022 | rec-size: 22
first-rec: 100 | last-rec: 110 | first-line: 100 | last-line: 121 | free-list:
122 | rec-size: 122
first-rec: 200 | last-rec: 210 | first-line: 200 | last-line: 221 | free-list:
222 | rec-size: 222
")
  (total-records stream))

==>

(REC-SIZE 366 FREE-LIST 366 LAST-LINE 363 FIRST-LINE 300 LAST-REC 330
FIRST-REC 300)
From: Harag
Subject: Re: NEWBIE: code crit neaded...its not looking lisp like
Date: 
Message-ID: <bo57n4$4ce$1@ctb-nnrp2.saix.net>
> > > to map a function directly across the header contents without building
a
> > list,


> (defun total-records (stream)
>   (let ((totals nil)
>         (*readtable* *record-readtable*))
>   (flet ((interpret-entry (key value) (incf (getf totals key 0) value)))
>     (do* ((key (read stream) (read stream nil nil))
>           (value (when key (read stream)) (when key (read stream))))
>          ((null key) totals)
>       (interpret-entry key value)))))
>

Ok so you are declaring a function and building a propertylist with the
minimal setting variables...

Is the property list faster or just cleaner or both?

Whats the difference (performance wise, I wont argue that it does not look
and feel cleaner)  between passing a value into a function and setting a
variable and just using it, are'nt both allocating the same memory?

Can you pass a flet function around as an object? I understand that the flet
functions dont see each other but if you passed it could you pass it as a
variable?

What kind of penalty does the flet incur or does it translate to a non
function call on compile. The hyperspec says talks about blocks and scope a
lot but in the end for me that only means that there is a lot more work to
be done because of return-form and all the events that go with it?

Sorry the more I learn the more questions I have....

Regards
Harag
From: Henrik Motakef
Subject: Re: NEWBIE: code crit neaded...its not looking lisp like
Date: 
Message-ID: <86wuaicyfd.fsf@pokey.internal.henrik-motakef.de>
"Harag" <·········@psychedelic.co.za> writes:

> the fist thing you would notice, beside all the lets, is that I am using
> strings instead of lists because every time I read anything on lisp they
> cons'ing is slow = bad (but optomization from the word go is bad to they
> tell me!!!), but I have this feeling that what I'm doing is even worse ...
> it looks like rape to me...

"Consing" is not about using the CONS function, or even lists; the
word is mostly a historical coincident. It is about allocating memory/
copying stuff when you don't have to in general.

Additionally, "avoid consing!" is something you should worry about
/after/ your code works, when the profiler tells you that it is an
issue. Especially when you are still learning Lisp, it shouldn't be
your top priority, IMHO. "First make it work, then make it fast", as
they say.

> O the other thing is I might be over doing the streams thing or am I
> underdoing it should I abandon the strings for more streams?

String streams can be a very nice tool for building strings,
escpecially they can be more efficient than using CONCATENATE a
lot. Unless cleverly optimized away by your compiler, your code will
create a new string for every character read, store it in the relevant
variable and leave the old string for garbage collection. In other
words, it will cons a lot, even if it doesn't use cons cells :-)

On the other hand, you never return any of the read strings, so they
are needlessly allocated anyway ;-)

Alternativly, you could use adjustable strings and append the
characters using VECTOR-PUSH-EXTEND.

Some other random notes:

You have a string, then read a line from it, only to create a string
input stream of this line. Scince you examine every character anyway,
you could just read from the original stream until you hit a #\Newline.

Or you could use FIND to locate the #\|s in the header line (as a
string), and use either SUBSEQ or displaced arrays to extract the fields.
You could also use SPLIT-SEQUENCE (not a part of ANSI Common Lisp,
it's available from cliki) to split the line at #\| into a list.

For real kicks (but probably little practical gain), you could also
try to make the reader convert "first-rec: 000 |" to (setf first-rec
000), although it could be tricky because "first-rec:" doesn't
conveniently start with some special character to attach a reader
macro to.

Instead of (setf count (+ count 1)), you could just use (incf
count). Or at least (setf count (1+ count)).

Oh, and placing closing parens on a line on their own is often
considered poor style, but that is a matter of taste of course.
From: Harag
Subject: Re: NEWBIE: code crit neaded...its not looking lisp like
Date: 
Message-ID: <bo3t5s$auq$1@ctb-nnrp2.saix.net>
"Henrik Motakef" <············@henrik-motakef.de> wrote in message
···················@pokey.internal.henrik-motakef.de...

> "Consing" is not about using the CONS function, or even lists; the
> word is mostly a historical coincident. It is about allocating memory/
> copying stuff when you don't have to in general.
>

Ok that clears up the consing issue for once and for all.

> Additionally, "avoid consing!" is something you should worry about
> /after/ your code works, when the profiler tells you that it is an
> issue. Especially when you are still learning Lisp, it shouldn't be
> your top priority, IMHO. "First make it work, then make it fast", as
> they say.

Thats what every body keeps on telling me but my experience in vb was always
to rather re-write a quick hack that to try and hack-optimise it because it
was quicker that way, changing type etc of variables alone could kill you,
and maybe thats why I'm reluctatnt to do the quick and dirty to see if it
works thing. I also come from a spec-to-death world....all excuses I
know...;o}

>
> > O the other thing is I might be over doing the streams thing or am I
> > underdoing it should I abandon the strings for more streams?
>
> String streams can be a very nice tool for building strings,
> escpecially they can be more efficient than using CONCATENATE a
> lot. Unless cleverly optimized away by your compiler, your code will
> create a new string for every character read, store it in the relevant
> variable and leave the old string for garbage collection. In other
> words, it will cons a lot, even if it doesn't use cons cells :-)
>

mmm your right of course aout the settign vars of course in a perfect world
I should have something like the following:

    do
        (read into stack)
    test loop from stack var

to avoid setting vars as much as possible


> On the other hand, you never return any of the read strings, so they
> are needlessly allocated anyway ;-)

    by that stage I had abondoned hope because it was looking to ugly ... so
I never even tried to pass them back out

>
> Alternativly, you could use adjustable strings and append the
> characters using VECTOR-PUSH-EXTEND.
>
aaaah an array ...something familiar ...;o}

> Some other random notes:
>
> You have a string, then read a line from it, only to create a string
> input stream of this line. Scince you examine every character anyway,
> you could just read from the original stream until you hit a #\Newline.
>

mmmm...ok

> Or you could use FIND to locate the #\|s in the header line (as a
> string), and use either SUBSEQ or displaced arrays to extract the fields.
> You could also use SPLIT-SEQUENCE (not a part of ANSI Common Lisp,
> it's available from cliki) to split the line at #\| into a list.
>

> For real kicks (but probably little practical gain), you could also
> try to make the reader convert "first-rec: 000 |" to (setf first-rec
> 000), although it could be tricky because "first-rec:" doesn't
> conveniently start with some special character to attach a reader
> macro to.

lol  ... the string I was using was just a test case it can still be fiddled
to do something like that

>
> Instead of (setf count (+ count 1)), you could just use (incf
> count). Or at least (setf count (1+ count)).

ok I suspected something like it but never got round to finding the function

>
> Oh, and placing closing parens on a line on their own is often
> considered poor style, but that is a matter of taste of course.
>
its a C# habbit ... all the brackets still makes my head hurt...and yes I
know i'm suppose to read the indentation and not the brackets but
still...eish



thanks for your input it is really appreciated.
From: Pascal Bourguignon
Subject: Re: NEWBIE: code crit neaded...its not looking lisp like
Date: 
Message-ID: <87ism21m8d.fsf@thalassa.informatimago.com>
"Harag" <·········@psychedelic.co.za> writes:

> To start with I will openly admit that I come from a vb back ground so that
> is a set back already (lol) but I would really appreciate some crit of this
> piece of code. I have read enough lisp about lisp (but have not
> practiced/used lisp enough) and seen enough code to know that this function
> could be a lot more lisp/functional like.
> 
> the fist thing you would notice, beside all the lets, is that I am using
> strings instead of lists because every time I read anything on lisp they
> cons'ing is slow = bad (but optomization from the word go is bad to they
> tell me!!!), but I have this feeling that what I'm doing is even worse ...
> it looks like rape to me...

1- Breathing is slow too, what a loss of time. Let's stop breathing as well.

2- As  you said, this is a  question of optimization, that  is this is
   something  to be concerned  with only  when your  customers complains
   about your programs being slow.


In any  case, don't worry too  much.  Practice. When  you'll have been
writting lisp program for one year, you'll know more and have a better
feeling about what's right and what's wrong.

 
> O the other thing is I might be over doing the streams thing or am I
> underdoing it should I abandon the strings for more streams?
> 
> the function gets the following from a file :
>         first-rec: 000 | last-rec: 010 | first-line: 000 | last-line: 021 |
> free-list: 022 | rec-size: 22 |
> loops through it and breaks it up into a couple of variables ... I started
> having doubts before I got round to trying to use the variables/strings
> 
> (defun read-head (stream)
>   ;;forcing read to beginning of file
>   (file-position stream 1)

The first byte is number 0,  at least on POSIX systems. CLHS is rather
vague  about the  definition of  a file-position  ot  allow conforming
implementations to work on more sophisticated (extinct?) file systems.

[43]> (progn 
  (with-open-file (out "/tmp/test" :direction :output :if-exists :supersede 
                       :if-does-not-exist :create) 
    (format out "hello Word!~%")
    (file-position out 1)
    (format out "H"))
  (with-open-file (in "/tmp/test" :direction :input)
    (format t  "~A~%" (read-line in))) )
hHllo Word!
NIL


>   (let (
>         (headstrm (make-string-input-stream (read-line stream)))
>         (first "")
>         (last "")
>         (lastline "")
>         (free "")
>         (size "")
>         (count 0)
>         )
> 
>     (do
>  ((instr (read-char headstrm nil :eof) (read-char headstrm nil :eof)))
>  ((equal instr :eof) first)
>          (if (equal instr #\|)

To parse a string, you can  access the characters inside it with:
(char string index) or (elt string index).


In my explications, I wrote records as:

        | field 1 | field 2 | field 3 |

just as a graphical illustration. Take it as ASCII art.

Of  course, you  could  define such  a  file format,  but since  we're
speaking of fixed-length records, think about a punch card:

000000000011111111112222222222333333333344444444445555555555666666666677
012345678901234567890123456789012345678901234567890123456789012345678901
ref   |author name            |yea|title                               |

0000003Celko, Joe              1999Data & Databases: conseps in practice
0000002Celko, Joe              2000SQL for smarties: advanced SQL progra
0000001Wood, Derick            1993Data Structures, Algorithms, and Perf

There is no need for  separator, since it's fixed-size: the fields are
defined by character positions. Then  you can easily extract or insert
the fields in a buffer with the subseq and replace functions:

(defun make-and-fill-record  (ref obj)
  (let ((record (make-string 80 :initial-element (character " "))))
    (replace record (format nil "~7,'0D" ref)              :start1  0 :end1 7)
    (replace record (getf obj :author)                     :start1  7 :end1 30)
    (replace record (format nil "~4,'0D" (getf obj :year)) :start1 30 :end1 34)
    (replace record (getf obj :title)                      :start1 34 :end1 72)
    record))

[57]> (make-and-fill-record 1 (gethash 1 data))
"0000001Wood, Derick           1993Data Structures, Algorithms, and Perfo        "
[58]> (make-and-fill-record 2 (gethash 2 data))
"0000002Celko, Joe             2000SQL for smarties: advanced SQL program        "
[59]> (make-and-fill-record 3 (gethash 3 data))
"0000003Celko, Joe             1999Data & Databases: conseps in practice         "

Then:

(defun scan-record (record)
  (list :ref    (parse-integer   (subseq record  0 7))
        :author (string-trim " " (subseq record  7 30))
        :year   (parse-integer   (subseq record 30 34))
        :title  (string-trim " " (subseq record 34 72))))

[60]> (scan-record (make-and-fill-record 1 (gethash 1 data)))
(:REF 1 :AUTHOR "Wood, Derick" :YEAR 1993 
 :TITLE "Data Structures, Algorithms, and Perfo")
[65]> (scan-record (make-and-fill-record 2 (gethash 2 data)))
(:REF 2 :AUTHOR "Celko, Joe" :YEAR 2000 
 :TITLE "SQL for smarties: advanced SQL program")
[66]> (scan-record (make-and-fill-record 3 (gethash 3 data)))
(:REF 3 :AUTHOR "Celko, Joe" :YEAR 1999
 :TITLE "Data & Databases: conseps in practice")


Now, the interesting fact is that all our records are exactly 80 bytes
long.  So  when we store them  in a file, we  can find them  with a so
called random  access instead of  a sequential access: given  a record
number n, we can seek to the position of the nth record and read it:

    (defun read-record (f n)
        (file-position (recfile-stream f) (+ (recfile-header-size f)
                                             (* n (recfile-record-size f))))
        (let ((record (make-string (recfile-record-size f))))
            (read-sequence (recfile-stream f) record)
            record))

and similarly to write a record.

Of  course, the  length 80  is  just an  example, showing  one of  the
consequences:  truncation, limited  space available  for data,  and to
feel hungry for those two century bytes :-)



On  the  other hand,  you  can  very well  use  bigger  records and  a
free-form format inside them.  Then you  can just store in them a lisp
sexp:

    (defun make-and-fill-sexp-record (sexp)
        (let ((record (make-string 1024 :initial-element (character " ")))
              (buffer (format nil "~S" sexp)))
          (when (< (length record) (length buffer))
            (error "data too big to put in a record."))
          (replace record buffer :start1 0 :end1 (length record))))


[67]> (MAKE-AND-FILL-SEXP-RECORD (gethash 1 data))
"(:AUTHOR \"Wood, Derick\" :YEAR 1993 :TITLE \"Data Structures, Algorithms, and Performance\"
 :ISBN \"0-201-52148-2\")                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                " ;; <<-- ends here.

Note the numerous spaces up to 1000 characters after the sexp printed form.
Then you can use read-from-string to recover the sexp from the record:

[76]> (read-from-string (MAKE-AND-FILL-SEXP-RECORD (gethash 1 data)))

(:AUTHOR "Wood, Derick" :YEAR 1993 :TITLE "Data Structures, Algorithms, and Performance"
 :ISBN "0-201-52148-2") ;
112
[77]> 



-- 
__Pascal_Bourguignon__
http://www.informatimago.com/
From: Harag
Subject: Re: NEWBIE: code crit neaded...its not looking lisp like
Date: 
Message-ID: <bo5599$37b$1@ctb-nnrp2.saix.net>
> You're misinterpreting.  First, consing is not particularly slow (in
> fact, it's usually very fast indeed); it's just that if you minimise
> the amount of garbage you create while doing your thing, the garbage
> collector won't have to run as often.  Also, the term "consing" is
> generally used to mean any kind of memory allocation, not just for
> cons cells -- it doesn't cost less to cons a new string than a new
> cons cell (in fact, it almost certainly costs more).

Ye your right I was misunderstanding the whole consing issue...


> There's no reason to be storing all that text and vertical bars and
> stuff in the file header.  If you're going to do that, just store a
> Lisp form and use READ on it!  [But I think Pascal probably intended to
> have a bunch of fixed-width binary values there]

Yes the header is anything but optimal but the excersize was to try some
text IO stuff....;o}

>
> You're returning FIRST and throwing all the other things you just read
> away!

That I know and even stated ...

"loops through it and breaks it up into a couple of variables ... I started
having doubts before I got round to trying to use the variables/strings"

...but I will put a better effort together next time and try to be a bit
more clear about what I'm after..

From all the comments I have learnt a whole lot of knew things about Lisp
functionality and and style which is great.

Thanks
Harag
From: Jock Cooper
Subject: Re: NEWBIE: code crit neaded...its not looking lisp like
Date: 
Message-ID: <m3n0bd1c0v.fsf@jcooper02.sagepub.com>
"Harag" <·········@psychedelic.co.za> writes:

> To start with I will openly admit that I come from a vb back ground so that
> is a set back already (lol) but I would really appreciate some crit of this
> piece of code. I have read enough lisp about lisp (but have not
> practiced/used lisp enough) and seen enough code to know that this function
> could be a lot more lisp/functional like.
> 
> the fist thing you would notice, beside all the lets, is that I am using
> strings instead of lists because every time I read anything on lisp they
> cons'ing is slow = bad (but optomization from the word go is bad to they
> tell me!!!), but I have this feeling that what I'm doing is even worse ...
> it looks like rape to me...
> 
> O the other thing is I might be over doing the streams thing or am I
> underdoing it should I abandon the strings for more streams?
> 
> the function gets the following from a file :
>         first-rec: 000 | last-rec: 010 | first-line: 000 | last-line: 021 |
> free-list: 022 | rec-size: 22 |
> loops through it and breaks it up into a couple of variables ... I started
> having doubts before I got round to trying to use the variables/strings
> 
> (defun read-head (stream)
>   ;;forcing read to beginning of file
>   (file-position stream 1)
>   (let (
>         (headstrm (make-string-input-stream (read-line stream)))
>         (first "")
>         (last "")
>         (lastline "")
>         (free "")
>         (size "")
>         (count 0)
>         )
> 
>     (do
>  ((instr (read-char headstrm nil :eof) (read-char headstrm nil :eof)))
>  ((equal instr :eof) first)
>          (if (equal instr #\|)
>              (setf count (+ count 1))
>              (case count
>                (0 (setf first (concatenate 'string first (string instr))))
>                (1 (setf last (concatenate 'string last (string instr))))
>                (2 (setf lastline (concatenate 'string lastline (string
> instr))))
>                (3 (setf free (concatenate 'string free (string instr))))
>                (4 (setf size (concatenate 'string size (string instr))))
>                )
>            )
>         )
> 
>     )
> )

I would write it more this way:

(defun split-on (str separator)
  (loop for start  = 0 then (1+ end)
	  for end  = (position separator str :start start)
	  for substr = (subseq str start end)
	  collect substr into result
	  when (null end) do (return result)))
(defun strip-sp (str)
  (string-trim #(#\space) str))
(defun intern-kw (str)
  (intern (string-upcase (strip-sp str)) :keyword))

(defun read-head (stream)
  (let* ((the-line (read-line stream))
	 (parts  (split-on the-line #\|))
	 (fields (make-hash-table)))
    (loop for (key value) in (mapcar #'(lambda (val) (split-on val #\:)) parts)
	  when value
	  do (setf (gethash (intern-kw key) fields)
		   (parse-integer (strip-sp value))))
    (flet ((print-field (field) ;eg
	     (format t "~a = ~a~%" field (gethash field fields))))
      (mapcar #'print-field '(:first-rec :last-rec :first-line :last-line 
                              :free-list :rec-size))))
  (values))

The fields are split at |, then each is split again at : .. the left side is 
used as a key into a hash table.  This has the advantage that the file field order and
names can change without breaking the parse code.  

A simpler scheme would be to use DESTRUCTURING-BIND like so:

(defun read-head (stream)
  (let* ((the-line (read-line stream))
	 (parts  (split-on the-line #\|)))
    (destructuring-bind (first-rec last-rec first-line last-line 
                         free-list rec-size)
	(mapcar #'strip-sp
		(delete nil (mapcar #'second
				    (mapcar #'(lambda (val) (split-on val #\:)) parts))))
      (format t "last-rec = ~a~%" last-rec))) ;eg
  (values))

But changes to the file's field order or names would require change to the 
DESTRUCTURING-BIND line..

On a side note:

you can shorthand your DO loop a little if you don't like typing the code twice:

(do ((instr #1=(read-char headstrm nil :eof) #1#))