From: frantisek stafek
Subject: Several NEWBIE questions...
Date: 
Message-ID: <9sluik$8ai$1@news.vol.cz>
Hello,
    in my free time, as a hobby, I've just started to play with
Common Lisp, on Windows 2000, latest trial Xanalys Lispworks.
I started learning by using CL in little scripts/one time
utilities for which I till now used PERL.

My little program takes mailfile with approx 300 mails
from posted WWW form, and output shoud be comma
separated file with form variables filled in cells. Each
row one mail.

Just at the beginning it looks like:

(defun process-line (outstream one-line)
    ;just stub for now
  (format outstream "~A~%" one-line))

(defun mails2csv (mailfile &optional
                           (csvfile *STANDARD-OUTPUT*)
                           (csv-separator #\;)
                           (message-separator "___separator_zpravy___")
                           (csv-quote #\"))
  (let ((fields-to-print '("datum:" "Subject:" "prijmeni:" "jmeno:" "mesto:"
"adresa:" "telefon:" "email:" "otazka_1:" "otazka_2:" "otazka_3:"
"otazka_4:")); fields actually printed
        (month-numbers (make-hash-table)) ;for conversion of "Date:" field
in format "Mon, 08 Oct 2001 14:42:03 +0200"
    (fields (make-hash-table)) ;all fields (variables) of one message
        )
    (setf
     (gethash "Sep" month-numbers) 9
     (gethash "Oct" month-numbers) 10
     (gethash "Nov" month-numbers) 11
     (gethash "Dec" month-numbers) 12)
    (with-open-file (outstream csvfile :direction :output)
      (with-open-file (instream mailfile :direction :input)
        (do
            ((line (read-line instream) (read-line instream nil 'eof)))
            ((eq line 'eof) "end of file.")
          (process-line outstream line))))))

My beginner questions are:

1)I started actually with CLISP and notepad. Then I switched to Lispworks.
Is there some way to automatically reformat the existing pasted code with
right indentation of forms and lists (especially my long line with
fields-to-print list)?
Should I write something using CL pretty-printer?

2)Variables fields-to-print and month-nubers are just parameters. Should
I put them out of function as global variables, in defvar form? is this the
right style, or should I somehow "encapsulate" them, so that they don't
clobber
the toplevel? Via package or via let form surrounding the main defun?
(probably
big nonsense :-)

3) There is a setf form, which initializes the month-numbers hash.
Is it the usual way to assign values in hash via repeated call of gethash
with one all the time same parameter - the actual hash, or should I
do something like this:

(mapcar #'(lambda(key value)
    (setf (gethash key month-numbers) value))
    '(("Sep" 9) ("Oct" 10) ("Nov" 11) ("Dec" 12)))

Should I write some macro for this, or is there something for
hashes I missed in CLHS?

4)Should I for this purpose use assoc or property lists?

5)For debugging purposes I'd like to use terminal output instead for output
file. My idea was, that the output file path would be optional parameter of
the converting function mails2csv. If not specified, terminal output would
be
used. But it seems that *STANDARD-OUTPUT* stream could not be used
as parameter for with-open-file, and the code should be probably
more comlicated to enable this simple debugging feature.

Or am I missing something?

Thank you for any valueable help.
        Frantisek Stafek
From: JP Massar
Subject: Re: Several NEWBIE questions...
Date: 
Message-ID: <3beea1a2.32221409@news>
On Sun, 11 Nov 2001 13:35:16 +0100, "frantisek stafek"
<·······@noise.cz> wrote:

 
>
>My beginner questions are:
>
>1)I started actually with CLISP and notepad. Then I switched to Lispworks.
>Is there some way to automatically reformat the existing pasted code with
>right indentation of forms and lists (especially my long line with
>fields-to-print list)?
>Should I write something using CL pretty-printer?

If you are using the Xanalysis editor, the command invoked by
CONTROL-META-q (hold down CONTROL, ALT and q simulataneously) indents
the next form.  So put the cursor (caret) on the first paren of your
function definition, and do the above command.

It is quite awkward to write Lisp code without an editor that can (at
the very least) indent Lisp forms in a reasonable way.

>
>2)Variables fields-to-print and month-nubers are just parameters. Should
>I put them out of function as global variables, in defvar form? is this the
>right style, or should I somehow "encapsulate" them, so that they don't
>clobber
>the toplevel? Via package or via let form surrounding the main defun?
>(probably
>big nonsense :-)

The simplest approach is to make them globals, using defvar.
Once your program is working, worry if you want about encapsulating
this data so that it is not visible to the world.

>
>3) There is a setf form, which initializes the month-numbers hash.
>Is it the usual way to assign values in hash via repeated call of gethash
>with one all the time same parameter - the actual hash, or should I
>do something like this:
>
>(mapcar #'(lambda(key value)
>    (setf (gethash key month-numbers) value))
>    '(("Sep" 9) ("Oct" 10) ("Nov" 11) ("Dec" 12)))
>
>Should I write some macro for this, or is there something for
>hashes I missed in CLHS?
>

Lisp gives you a lot of alternatives.  LOOP, DO, MAPC, MAPCAR, even
recursion, to name some.

Since you aren't using the result of the MAPCAR, it is building up a
list of results needlessly, so you could use MAPC here, or do
something like

(defconstant *month-strings* '("Jan Feb Mar"))

(defparameter *month-codes*
  (let ((hash-table (make-hash-table :test #'equalp)))
    (do ((j 1 (1+ j)) (month-list *month-strings* (cdr month-list)))
        ((null month-list) hash-table)
      (setf (gethash (first month-list) hash-table) j))))


>4)Should I for this purpose use assoc or property lists?
>

Whatever you feel comfortable with.  Again, Lisp gives you lots of
alternatives that are easy to use.  You aren't really concerned with
memory usage or speed here, so anything reasonable that gets the job
done is probably all right.

A good thing to do is to create an abstraction so that if you decide
to change the data structures involved, your code will still work
simply by changing the interface functions which do the actual
accessing of the data structure.


>5)For debugging purposes I'd like to use terminal output instead for output
>file. My idea was, that the output file path would be optional parameter of
>the converting function mails2csv. If not specified, terminal output would
>be
>used. But it seems that *STANDARD-OUTPUT* stream could not be used
>as parameter for with-open-file, and the code should be probably
>more comlicated to enable this simple debugging feature.
>
>Or am I missing something?
>
 
You could use a BROADCAST STREAM to direct the output both to your
file and optionally to *STANDARD-OUTPUT*.  See MAKE-BROADCAST-STREAM,
etc.  

You could encapsulate the code that writes to the stream in an FLET,
and write code that either does a WITH-OPEN-FILE and then calls your
FLET, or just calls your FLET with *STANDARD-OUTPUT* if your output
file path optional parameter is not specified.

Hope this helps.