charlie (05/06/83)
I promised that when I completed a course and project in AI using Lisp that I would post to the net some notes for novices. Here it is. Thanks to all in net land who gave me advice. This collection of notes should help a novice Lisp programmer to get well started using Franz Lisp under UNIX 4.1BSD on the VAX. 1) The first thing you must do is get some literature on the language itself. The new paperback 'LISP' by Patrick Winston is very good and quite compatible with Franz. See the appendix of this guide for a recommended reading list. Of course you should try to obtain a copy of "The FRANZ LISP Manual" which is about 1/2 inch thick. Mine is dated Sept 1981. It tells about the Liszt compiler, the Tracer and most of the functions but it has almost no examples. It is strictly for reference. The usual manual pages that are on line are nice but woefully inadequate for this large and complex system. 2) Use your favorite editor to create a pgm.l file. The .l means lisp. My favorite editor is vi and there are lisp functions that can invoke vi from within a running lisp program. There is a lisp structure editor that can be invoked from within lisp but it seems like good advice for a novice to avoid using it. 3) Start up lisp as follows, % is assumed to be the Unix prompt %lisp ->(load 'foo.l) The .l is not really needed but the single ' is. Later when you use the Liszt compiler it will make a foo.o file and if you merely (load 'foo) it will first try to get foo.o then foo.l and finally foo. 4) For subsequent editing from within lisp just do this -> (vil foo) It will invoke vi on the file foo.l and upon exiting vi it will load it into the lisp interpreter. This function is undocumented, thanks to some kind soul on the Usenet for telling me about it. Note that this function does not need the ' before the argument! 5) Getting parenthesis to balance is tricky but here are two very useful things to know: [ ] automatically provides the correct number of balancing right parentheses. Also, in vi the % command will move the cursor to the paren or bracket that matches the one you currently have the cursor on. It works for both left and right parens. 6) (exit) carriage return to leave the lisp interpreter. 7) Comments appear after a semicolon on any lisp program line. Be careful not to comment out a line that causes right paren unbalance in what is left! 8) You can freeze a session and restart later by (dumplisp filename) and just typing filename after the Unix prompt. This takes about half a megabyte of disk, a fair amount of time and is therefore not to be used lightly. - 1 - 9) When your program runs, it will probably stop with some kind of error message and you will see the prompt <1>: additional errors cause deeper prompts <2>: <3>: etc. You can go up a level with cntrl-Z on my system, maybe cntrl-D on yours. If the error is correctable because you typed an atom wrong you can do a (return atom-spelled-right) i.e. spell it correctly and the lisp interpreter will take off and continue running. If it was a function name that was spelled wrong do a (return 'correct-func-name). The forced by putting the function (break) in your code, you will find the functions (showstack) and (baktrace) to be useful for debugging. To get it started after one of your (break) instructions just enter (return t). 10) At first you will be amazed at how many functions seem to be built into the interpreter and you will wonder if there is some function or command that will give you a big list of all the currently loaded functions. I haven't found one! However you can use the pretty printer function (pp atom-or-func) to show if one exists. For example (pp sort) will return t machine coded, which means yes there is a function sort and it is compiled so it can't be pretty printed. This is one of those undocumented ones, more later. Also, at any break or error you can examine lists and functions easily by just typing -> (pp func) or (pp atom) where atom is a variable name that is currently bound to a list. 11) The most useful debugging tool for me has been the Joseph Lister Trace package herewith called the tracer. I have not had quick success with the stepper or the debugger. The most common thing to do is enter -> (trace (func-name break)) which will cause a break when that function is invoked. It will show the arguments on entry to the function and will show the result as the function exits. You may access the arguments with (arg 1) or (arg 2) etc. For example: T> (pp (arg 2)) will pretty print the 2nd argument. the T> is the trace prompt. To get going again enter (tracereturn) or cntrl-Z. If you have a typescript going and cntl-z is the EOF on your system you better use (tracereturn) or the script will terminate. There are many other ways to get the tracer invoked but the one shown is sufficient. (untrace func-name) or a simple (untrace) to untrace everything will turn it off. If you do a (vil func) of a traced function it will no longer be traced when you come back from the edit. I'm not sure what happens. 12) If you create a big data structure in the interpreter, or make alterations with the structure editor or if you just want to extract several pieces of a large program, then you can use the following to pretty print to a file ->(pp (F foo.l) prove smurf ok)) - 2 - This will pretty print the functions prove smurf and foo out to file foo.l. The capital F is needed. 13) There are times when you wish lisp had some kind of history mechanism. Well - ho ho - it does, again undocumented as far as I can tell unless you read the source code. * is bound to the last result you got back ** to the next to last *** to the last but two + is bound to the last thing you typed ++ next to last +++ to the 3rd from the last Needless to say these 'handles' are transitory and you should do ->(setq it *) and (pp it) if you want to hang onto something to play with for a while. 14) On my system and probably on yours can be found a wealth of lisp programs and functions in source code in /usr/lib/lisp. I must admit that as a novice I find most of them to be too complex but they are becoming more interesting as I progress. Beyond the Winston book there are some pretty hairy examples for complex problems in the paperback Techniques of Artificial Intelligence by Stuart Shapiro. 15) The basic building block is defun which defines a function. If you need 'local variables' you need a prog inside the defun (defun foo (arg1 arg2) ; arguments passed into function foo (prog (local1 local2) (setq local1 '(a b c)) . . (return local2))) An alternative to defun is def in which the arguments are declared a bit differently as follows: (def foo (lambda (arg1 arg2) ... body ... )) ; note that the right paren for the lambda is here A useful function is the undocumented sort function. It takes two arguments, a list of things to be sorted and a function of two args that returns t or nil depending on whether to sort them or not. Example: (sort '(1 2 4 3 7 6) '(lambda (one two) (greaterp one two))) which should return the list: (7 6 4 3 2 1). A very useful macro is the (repeat .. while .... until .... result). It not only allows initialized local variables, it provides looping control and can return a result. The result and one of the while or until keywords is optional. See appendix 2. - 3 - 16) The liszt compiler can produce code that runs typically over ten times faster than the interpreter. It is a bit tricky to get things that include macros to compile however. There are a few things that help. In your program put (declare special variable) for all the variables that are global. Put all macros within an (eval-when (load compile eval) ......) function list. The .... may comprise several pages worth of your program! Then %liszt -x pgm.l > out. Much information is contained in the out file which you should look at. Successful compilation does not mean it will run however. Run time errors such as 'internal bad memory reference , suggest you (reset)' can occur. They may be understandable such as from a (member x y) where y is an atom and not a list or they may have something to do with macros that weren't compiled. The -x option produces a file that can be input to lxref for a cross reference which can be valuable for understanding a big program or even to find functions that arn't even referenced anymore! _A_p_p_e_n_d_i_x _A - _R_e_a_d_i_n_g _l_i_s_t 1) LISP by Patrick Henry Winston. Addison-Wesley Publishing 1981. About $14. This is a very good introduction. Many examples and very readable. Solutions to the exercises are given. 2) Techniques of Artificial Intelligence by Stuart C. Shapiro D. Van Nostrand Company. Paperback. Not in the Franz dialect but has complete examples of non-trivial problems. 3) Artificial Intelligence Programming by Charniak, Riesbeck and McDermott. Lawrence Erlbaum Assoc. Publishers. 1980. Hardcover with lots of text and pieces of lisp code embedded in the text. Not Franz dialect. 4) The Little Lisper by Daniel Friedman. Science Research Associates. 1974. This 8 1/2 by 11 size thin paperback is a question and answer text. You cover up the right half of the page 'till you get the answer. It didn't hold my attention. 5) BYTE magazine August 1979. The LISP issue. 6) BYTE magazine September 1981. The Artificial Intelligence issue. 7) Scientific American magazine Feb March and April 1983 in the Metamagical Themas column by Douglas Hofstadter on the "LISP genie". - 4 - _A_p_p_e_n_d_i_x _2 _S_o_u_r_c_e _o_f _r_e_p_e_a_t _m_a_c_r_o Originally from the Shapiro book but modified for Franz with local variables and a result key word by Mark Swartwout. ; repeat is a handy looping function. See the example below. ; The general form is (repeat (local val )"F1" "F2" ... "Fn") ; Local variables occur in the list after the repeat as pairs ; with the first atom being initialized to the 2nd item of the pair. ; Each argument is a form or a keyword i.e. while, until, or result. ; The forms are evaluated one after the other cyclicly until the form ; following an occurence of while evaluates to nil or the form after ; an until evaluates to non-nil. repeat then returns the value of this ; last form or if the keyword result is present, of the form after result. ; (def repeat (macro (call) (repeathelp (cdr call) (gensym 'l) (gensym 'v)))) (def repeathelp (lambda (bodi lbl vbl) (cons 'prog (cond [(null (car bodi)) (cons (list vbl) (cons lbl (repeathelp1 (cdr bodi) lbl vbl (list 'return vbl))))] [t (cons (cons vbl (repeatvars (car bodi))) (cons (cons 'setq (car bodi)) (cons lbl (repeathelp1 (cdr bodi) lbl vbl (list 'return vbl)))))])))) (def repeatvars (lambda (vars-n-vals) (cond [(null vars-n-vals)nil] [t (cons (car vars-n-vals) (repeatvars (cddr vars-n-vals)))]))) (def repeathelp1 (lambda (bodi lbl vbl tail) (cond ((null bodi) (list (list 'go lbl) 'exit tail)) ((eq (car bodi) 'while) (cons (list 'cond (list (list 'not (list 'setq vbl (cadr bodi))) (list 'go 'exit))) (repeathelp1 (cddr bodi) lbl vbl tail))) ((eq (car bodi) 'until) (cons (list 'cond (list (list 'setq vbl (cadr bodi)) (list 'go 'exit))) (repeathelp1 (cddr bodi) lbl vbl tail))) ((eq (car bodi) 'result) (repeathelp1 (cddr bodi) lbl vbl (list 'return (cadr bodi)))) - 5 - (t (cons (car bodi) (repeathelp1 (cdr bodi) lbl vbl tail)))))) ; ; An example of repeat ; ;(def rtest ; (lambda nil ; (repeat ; (x 15 y 15) ; local variables x and y are both set to 15. ; (print (list 'x x)) ; (terpri) ; (setq x (sub1 x)) ; while ; stop and return nil ; (greaterp x 0) ; when x is not greater than 0. ; (setq y (add1 y)) ; until ; stop and return t ; (greaterp y 30) ; when y is greater than 30 ; (print (list 'y y)) ; (terpri)))) ; ; This is what rtest expands to. ; ;(def rtest ; (lambda nil ; (prog (v00001 x y) ; (setq x 15 y 15) ; l00000 (print (list 'x x)) ; (terpri) ; (setq x (sub1 x)) ; (cond ((not (setq v00001 (greaterp x 0))) (return v00001))) ; (setq y (add1 y)) ; (cond ((setq v00001 (greaterp y 30)) (return v00001))) ; (print (list 'y y)) ; (terpri) ; (go l00000)))) ;
tim (05/06/83)
Relay-Version:version B 2.10 gamma 4/3/83; site mhuxt.UUCP Message-ID:<5172@unc.UUCP> Date:Fri, 6-May-83 16:19:54 EDT I wish I'd had something like that when I got started. There are of course a number of things that could be added to it, but most important, I think, is that vi has a "lisp" mode which provides automatic indentation of list structures. It's not perfect -- it isn't specific to Franz, so it doesn't know about semicolon comments and such -- but it sure helps. To use it, just say ":set lisp" in vi. (Note: it is possible to compile vi without lisp mode to save space. If this was done on your system, I'm sorry to have wasted your time.) Another handy vi mode is "showmatch". When this is on, entering a close parenthesis causes the cursor to momentarily bounce to the matching open parenthesis, if it's on the screen. Again, it's not perfect -- it doesn't know about square brackets -- but it is handy. To use it, say ":set showmatch" or ":se sm" in vi. You can set more than one option at once, for instance ":set lisp sm". Finally, it is possible to set these options on the command line, so you can use an alias to vi your Lisp programs with the modes turned on. alias vl vi '+:set lisp sm' \!* is my alias. It works with csh only, of course. Saying "vl foo.l" starts up vi on the file foo.l with lisp and sm set. (Unfortunately, you start at the end of the file instead of the beginning when you do this, but that's no big thing.) I hope this has served to help rather than confuse. Does anyone have Gosling Emacs "electric-lisp-mode" working with Franz Lisp, by the way? Tim Maroney
rogerh (05/07/83)
Yes, we have the Gosling Emacs electric-lisp-mode working. I usually have one window running Lisp, another editing the relevant function definition file(s). S'wonderful. There are a couple problems: the formatting, while better than the Franz Lisp pretty-printer, is not perfect; and it gets real confused about the end of a function. My workaround for the second is Never begin a line with a close-paren. I have a couple questions: Does the "CMU toplevel" really exist? I found the source, or at least something that must be the source, but it don't load. Has anyone messed with funargs? It seems, on cursory examination, that the tools are there to write a closure function -- a function to close a functional form so that any free variables of the form are evaluated in the environment extant at the point of application of the closure function (whew!) -- but I can't find one in the manual. Roger Hayes University of Arizona arizona!rogerh PS: We have Opus 36 of Franz Lisp; I can't find a version number on our electric-lisp.ml . rh.
tim (05/09/83)
I don't see how Franz Lisp, with its shallow binding, could efficiently support funargs and closures. However, I could be wrong. Can anyone provide a clear explanation of evalframe descriptors as described in the functions eval and evalframe? You can call me a dummy if you like, but please provide the answers too. Tim Maroney duke!unc!tim
george (05/12/83)
"10) At first you will be amazed at how many functions seem to be built into the interpreter and you will wonder if there is some function or command that will give you a big list of all the currently loaded functions. I haven't found one!" The function "oblist" returns a list of interned atoms. Given this, it should be straight forward to write a function that returns a list of atom names for functions. George Rosenberg duke!mcnc!idis!george decvax!idis!george