From: Wolfgang Mederle
Subject: CL beginner's first attempts
Date: 
Message-ID: <gg7hu9.0rh.ln@DS9.mederle.de>
I am trying to learn CL in self-study, and below you can see my first
attempt to write a real application. I could need some help with it, as
you will clearly see as soon as you try to run it.

The purpose of the program is to help creating a phrase structure tree
for LaTeX using the bundle package. It creates a file one can later
insert into a LaTeX file. The desired output on paper would be something
like: 

   S
  /\
NP  VP
|   |
N   V
|   |
Jo  sleeps.

In LaTeX (this is what the program is supposed to create except the
indentation) this looks like:

,---------[ file ]
| \begin{bundle}{S}
|   \chunk{
|     \begin{bundle}{NP}
|       \chunk{
|         \begin{bundle}{N}
|           \chunk{Jo}
|         \end{bundle}
|        }
|     \end{bundle}
|   }
|   \chunk{
|     \begin{bundle}{VP}
|       \chunk{
|         \begin{bundle}{V}
|           \chunk{sleeps.}
|         \end{bundle}
|       }
|     \end{bundle}
|   }
| \end{bundle}
`---------

Now, here's the actual code (I translated the doc strings into English
to hopefully make it easier for you):

(defun lies-dateiname ()
  "Main function. Invokes creation of outfile or displays help."
  (format t "~%Name of outfile (or ? for help): ")
  (let ((dateiname (read-line)))
  (if (equalp "?" dateiname) (hilfe) (ausgabedatei dateiname))))

(defun hilfe ()
  (format t "~%Das Programm erzeugt einen Strukturbaum, der in LaTeX-Skripten verwendet
      werden kann. Voraussetzung im LaTeX-Skript:~%
      \\usepackage{epic,eepic,ecltree}~%
      [More German text snipped]
      links nach rechts.~%")
  (lies-dateiname))

(defun ausgabedatei (dateiname)
  "Creates outfile and starts tree generation."
  (with-open-file (aus dateiname :direction :output :if-exists :rename)
  (knoten aus)))

(defun knoten (dateistrom &optional (ober-knoten nil))
  "asks for node and branches."
  (cond ((eq nil ober-knoten)
		 (format t "~&node (\"0\" for end): "))
		(t (format t "~&node for ~a:" ober-knoten)))
  (let ((node (read-line)))
	(unless (equalp node "0")
	  (format t "~&Number of branches (\"0\" for terminal symbol): ")
	  (let ((branch (read)))
		(machknoten dateistrom node branch)))))

(defun machknoten (dateistrom knoten ast)
  "Writing of node and branch information."
  (format dateistrom "~&\\begin\{bundle\}\{~a\}" knoten)
  (cond ((= ast 1)
		 (format t "~&type of terminal:")
		 (let (termt (read-line))
		   (format t "~&value of terminal:")
		   (let (termv (read-line))
			 (format dateistrom "~&\\chunk\{~%\\begin\{bundle\}\{~a\}~%\\chunk\{~a\}~%\\end\{bundle\}~%\}~%\\end\{bundle\}" termt termv))))
		((= ast 0)
		 (format t "type of terminal:")
		 (let (termv (read-line))
		   (format dateistrom "\\chunk\{~a\}~%\\end\{bundle\}" termv)))
		((> ast 1)
		 (loop for zaehler from 0 below ast do
			   (format dateistrom "\\chunk\{~%")
			   (knoten dateistrom knoten)
			   (format dateistrom "\}~%"))
		 (format dateistrom "\\end\{bundle\}~%"))))


END CODE

I think the code has some misconceptions, and I would appreciate very
much if you could point out some of the mistakes I made. I wrote a
working solution to this problem in Perl, and I thought it would be a
good exercise to translate it into CL, but I am some stuck right now. If
you want to take a look at the Perl version, it is at
http://sudo.cis.uni-muenchen.de/~reboot/baumstrukt

-- 
Wolfgang Mederle
················@stud.uni-muenchen.de
http://www.mederle.de/
ICQ# 1435333

From: Dr. Edmund Weitz
Subject: Re: CL beginner's first attempts
Date: 
Message-ID: <m3wv03jofk.fsf@bird.agharta.de>
Wolfgang Mederle <················@stud.uni-muenchen.de> writes:

> I am trying to learn CL in self-study, and below you can see my first
> attempt to write a real application. I could need some help with it, as
> you will clearly see as soon as you try to run it.
> 
> The purpose of the program is to help creating a phrase structure tree
> for LaTeX using the bundle package. It creates a file one can later
> insert into a LaTeX file. The desired output on paper would be something
> like: 
> 
>    S
>   /\
> NP  VP
> |   |
> N   V
> |   |
> Jo  sleeps.
> 
> In LaTeX (this is what the program is supposed to create except the
> indentation) this looks like:
> 
> ,---------[ file ]
> | \begin{bundle}{S}
> |   \chunk{
> |     \begin{bundle}{NP}
> |       \chunk{
> |         \begin{bundle}{N}
> |           \chunk{Jo}
> |         \end{bundle}
> |        }
> |     \end{bundle}
> |   }
> |   \chunk{
> |     \begin{bundle}{VP}
> |       \chunk{
> |         \begin{bundle}{V}
> |           \chunk{sleeps.}
> |         \end{bundle}
> |       }
> |     \end{bundle}
> |   }
> | \end{bundle}
> `---------
> 
> Now, here's the actual code (I translated the doc strings into English
> to hopefully make it easier for you):
> 
> (defun lies-dateiname ()
>   "Main function. Invokes creation of outfile or displays help."
>   (format t "~%Name of outfile (or ? for help): ")
>   (let ((dateiname (read-line)))
>   (if (equalp "?" dateiname) (hilfe) (ausgabedatei dateiname))))
> 
> (defun hilfe ()
>   (format t "~%Das Programm erzeugt einen Strukturbaum, der in LaTeX-Skripten verwendet
>       werden kann. Voraussetzung im LaTeX-Skript:~%
>       \\usepackage{epic,eepic,ecltree}~%
>       [More German text snipped]
>       links nach rechts.~%")
>   (lies-dateiname))
> 
> (defun ausgabedatei (dateiname)
>   "Creates outfile and starts tree generation."
>   (with-open-file (aus dateiname :direction :output :if-exists :rename)
>   (knoten aus)))
> 
> (defun knoten (dateistrom &optional (ober-knoten nil))
>   "asks for node and branches."
>   (cond ((eq nil ober-knoten)
> 		 (format t "~&node (\"0\" for end): "))
> 		(t (format t "~&node for ~a:" ober-knoten)))
>   (let ((node (read-line)))
> 	(unless (equalp node "0")
> 	  (format t "~&Number of branches (\"0\" for terminal symbol): ")
> 	  (let ((branch (read)))
> 		(machknoten dateistrom node branch)))))
> 
> (defun machknoten (dateistrom knoten ast)
>   "Writing of node and branch information."
>   (format dateistrom "~&\\begin\{bundle\}\{~a\}" knoten)
>   (cond ((= ast 1)
> 		 (format t "~&type of terminal:")
> 		 (let (termt (read-line))
> 		   (format t "~&value of terminal:")
> 		   (let (termv (read-line))
> 			 (format dateistrom "~&\\chunk\{~%\\begin\{bundle\}\{~a\}~%\\chunk\{~a\}~%\\end\{bundle\}~%\}~%\\end\{bundle\}" termt termv))))
> 		((= ast 0)
> 		 (format t "type of terminal:")
> 		 (let (termv (read-line))
> 		   (format dateistrom "\\chunk\{~a\}~%\\end\{bundle\}" termv)))
> 		((> ast 1)
> 		 (loop for zaehler from 0 below ast do
> 			   (format dateistrom "\\chunk\{~%")
> 			   (knoten dateistrom knoten)
> 			   (format dateistrom "\}~%"))
> 		 (format dateistrom "\\end\{bundle\}~%"))))
> 
> 
> END CODE
> 
> I think the code has some misconceptions, and I would appreciate very
> much if you could point out some of the mistakes I made. I wrote a
> working solution to this problem in Perl, and I thought it would be a
> good exercise to translate it into CL, but I am some stuck right now. If
> you want to take a look at the Perl version, it is at
> http://sudo.cis.uni-muenchen.de/~reboot/baumstrukt
> 
> -- 
> Wolfgang Mederle
> ················@stud.uni-muenchen.de
> http://www.mederle.de/
> ICQ# 1435333

I think your problem in the code above is that you've omitted a level
of parentheses in your three last LET statements: It should be

  (LET ((TERMV (READ-LINE))) ...

instead of

  (LET (TERMV (READ-LINE)) ...

The latter is just another way of writing

  (LET (TERMV READ-LINE) ...

which is obviously not what you want. As this is not a syntax error,
you usually don't get a warning here if you're using the
interpreter.[1] If you compile the code, you _will_ get a meaningful
warning about an unused variable READ-LINE in most implementations,
though.

Also, your function MACHKNOTEN makes a distinction between two cases -
(= AST 0) and (= AST 1) - that can be treated the same if you
re-arrange your code.

Apart from that I think you shouldn't try learning Lisp by adapting
programs from other languages like Perl line by line. It usually pays
to re-think the problem in a 'Lispier' way. In this case I'd suggest
to represent your structures in list form (as opposed to lines of
text). This could be something like that:

  (defun print-tree (node &optional (indent 0))
    (cond
     ((atom node)
      node)
     (t
      (format nil "~%~V,0T\\begin{bundle}{~A}~{~A~}~%~V,0T\\end{bundle}"
              indent
              (car node)
              (mapcar #'(lambda (chunk)
                          (format nil "~%~V,0T\\chunk{~A}"
                                  (+ indent 2)
                                  (print-tree chunk (+ indent 4))))
                      (cdr node))
              indent))))
  
  (defun tree-to-file (node file)
    (with-open-file (stream file :direction :output :if-exists :rename)
      (princ (print-tree node) stream)))

Try

  (TREE-TO-FILE '(S NP VP ADVP) "foo")
  (TREE-TO-FILE '(S (NP DET N) (VP V (NP DET N))) "foo")

and check if this fits your needs. Then try to improve it (error
checking, better indentation control, optional chunk labels, ...).

One last remark: Maybe it's just the TAB characters, but you should
properly indent your code - makes it easier for you and other people
to understand. As you seem to be using Gnus, you already have the best
Lisp editor (Emacs) available, and you only need to invoke
INDENT-REGION (and maybe UNTABIFY).

HTH,
Edi.


[1] Implementations that (partly) compile their code in the REPL
    already, will warn earlier. This is at least true for CMUCL and
    might also be the case for MCL and CormanLisp - I haven't checked.
From: Wolfgang Mederle
Subject: Re: CL beginner's first attempts
Date: 
Message-ID: <dkvju9.fug.ln@DS9.mederle.de>
Edmund Weitz wrote:
> I think your problem in the code above is that you've omitted a level
> of parentheses in your three last LET statements: It should be
> 
>   (LET ((TERMV (READ-LINE))) ...

Arrrrgh ... I knew there was some stupid mistake somewhere.

> If you compile the code, you _will_ get a meaningful
> warning about an unused variable READ-LINE in most implementations,
> though.

I must admit that I got this error but didn't see what was wrong.

> Apart from that I think you shouldn't try learning Lisp by adapting
> programs from other languages like Perl line by line. It usually pays
> to re-think the problem in a 'Lispier' way. 

I agree, but in this case the Perl code I had written was as lispy as
Perl code can get, functional and recursive, and thus I should have
used CL in the first place.

> In this case I'd suggest
> to represent your structures in list form (as opposed to lines of
> text). This could be something like that:

[snip]

>   (TREE-TO-FILE '(S NP VP ADVP) "foo")
>   (TREE-TO-FILE '(S (NP DET N) (VP V (NP DET N))) "foo")

Thanks for the suggestion, which I will consider for an improved version
of the program. Like, have a routine that asks questions about nodes and
branches and creates a list for the output routine.

> One last remark: Maybe it's just the TAB characters, but you should
> properly indent your code - makes it easier for you and other people
> to understand. 

Oops, sorry. For me it looked perfect. I'm using GNU Emacs 21 and ILISP
from CVS.

> As you seem to be using Gnus, you already have the best
> Lisp editor (Emacs) available, and you only need to invoke
> INDENT-REGION (and maybe UNTABIFY).

(setq-default indent-tabs-mode nil)
I put this in my .emacs now and hope it will do the trick.

> HTH,

Sure does. Thank you.

-- 
Wolfgang Mederle
················@stud.uni-muenchen.de
http://www.mederle.de/
ICQ# 1435333