F.G.H. 6/12/85 LispCourse #36: More on Variable Binding; Control Structures in Lisp LispCourse #36: More on Variable Binding; Control Structures in Lisp NLAMBDA Functions Recall that NLAMBDA functions are treated specially by the Lisp interpreter. In particular, when evaluating a function call containing an NLAMBDA function, the interpreter does not evaluate the arguments before the function is applied. To define an NLAMBDA function, just use the keyword NLAMBDA instead of LAMBDA in the function definition. For example: 1_ (DEFINEQ (E (NLAMBDA (FileName) (* * Call TEdit on the named file) (TEDIT FileName)))) (E) 2_ (E OUTLINE01.TED) {PROCESS}#32,12345 (Starts up a TEdit) 3_ (SETQQ File OUTLINE01.TED) OUTLINE01.TED 4_ (E File) {DSK}File not found Contrast the preceeding NLAMBDA with an analogous LAMBDA function: 5_ (DEFINEQ (EX (LAMBDA (FileName) (* * Call TEdit on the named file) (TEDIT FileName)))) (EX) 6_ (EX 'OUTLINE01.TED) {PROCESS}#32,12337 (Starts up a TEdit) 7_ (SETQQ File OUTLINE01.TED) OUTLINE01.TED 8_ (EX File) {PROCESS}#32,12678 (Starts up a TEdit) The NLAMBDA version of this function has the advantage that user/programmer does not have to QUOTE the file name argument when calling the function since the argument is not evaluated. I.e., compare events 2 and 6 in the preceding examples. However, the NLAMBDA version has the disadvantage that it is less easily integrated into the workings of the Lisp Exec. For example, you cannot set a variable to the name of the desired file and then use this variable to refer to the file when calling the NLAMBDA function. This is shown in the comparison between events 4 and 8 above. The bottom line is that NLAMBDA function have their uses, especially when writing functions that interface with the user, but they also have their drawbacks in terms of flexibility. For example: LISTFILES is an NLAMBDA function. It is nice to be able to type: (LISTFILES {DSK}HOMEWORK34) without worrying about the QUOTE. On the other hand, you can do: (FOR File in ListOfFiles DO (LISTFILES File)) to print out each file in a list of files. You would have to type (FOR File in ListOfFiles DO (APPLY 'LISTFILES (LIST File))), which is more than a bit clumsy. The other place that NLAMBDA function are very handy is when you want to write functions that evaluate a bunch of SExpressions in a non-standard order. For example, the following function evaluates the first of two SExpressions only if the first evaluates to non-NIL: (DEFINEQ (FirstOnlyIfSecond (NLAMBDA (SExpr1 SExpr2) (COND ((EVAL SExpr2) (EVAL SExpr1)))))) Note that COND, AND, OR, PROG, etc. are all standard Interlisp functions that use this trick to change the order in which a set of SExpressions are evaluated. Spread and NoSpread Functions The Spread/NoSpread distinction is a second classification of functions in Interlisp that is orthogonal to the LAMBDA/NLAMBDA distinction. So far, we have been looking only at spread functions. A spread function is one whose parameter list is in fact a LISTP. Thus, a spread function definition has the form (LAMBDA List SExpr1 ... SExprN) or the form (NLAMBDA List SExpr1 ... SExprN). When APPLYing spread functions, the Interlisp interpreter matches each parameter in this parameter list with the corresponding argument in the function call. Note that the effect of this scheme is that each spread function has a fixed number of parameters. If there are more arguments then parameters, the arguments are just ignored. In contrast, a nospread function has a parameter "list" consisting of a single LITATOM. Thus, a nospread function definition has the form (LAMBDA Litatom SExpr1 ... SExprN) or the form (NLAMBDA Litatom SExpr1 ... SExprN). When APPLYing a nospread function, Interlisp sets up this LITATOM to point to the list of all arguments in the function call. Using the LITATOM as a reference (as described below), you can access this list from within the function. This scheme allows a single parameter to refer to an arbitrarily long list of arguments. Thus, nospread functions have no fixed number of parameters. The manner in which one can reference the arguments of a nospread function differs for LAMBDA and NLAMBDA functions. NLAMBDA nospread functions Interlisp simply sets the LITATOM that is the parameter "list" to the list of arguments (unevaluated). You can then get to any element of the this list using CAR, CDR, LAST, etc. For example, the following function calls TEdit for each of an arbitrary number of files: 10_(DEFINEQ (TEDs (NLAMBDA Files (FOR File in Files DO (TEDIT File))))) (TEDs) 11_(TEDs {DSK}FOO) NIL (Starts TEdit on a single file {DSK}FOO) 12_ (TEDs {DSK}BAR {ERIS}BAZ {DSK}ARG) NIL (Starts 3 TEdits on three files as specified) 13_ (TEDs A B C D E F G H J K) NIL (Starts 10 TEdits on ten files as specified) LAMBDA nospread functions LAMBDA nospread functions are a bit more complicated. The LITATOM that is the parameter "list" is bound to the number of arguments being passed in the current function call. The arguments can be accessed individually using the functions ARG and SETARG. (ARG Litatom M) In a LAMBDA nospread function whose parameter specification is the LITATOM Litatom, ARG returns the Mth argument of the current function call. ARG is itself an NLAMBDA function that doesn't evaluate its argument but DOES evaluate its second argument. (SETARG Litatom M Value) In a LAMBDA nospread function whose parameter specification is the LITATOM Litatom, SETARG sets the Mth argument of the current function call to Value. SETARG is itself an NLAMBDA function that doesn't evaluate its argument but DOES evaluate its second and third arguments. For example, the following (nonsense) function collects the results of evaluating an arbitrary number of SExprs, where each result is made into a string: 14_(DEFINEQ (ResultsAsStrings (LAMBDA SExprs (FOR Index FROM 1 to SExprs COLLECT (MKSTRING (ARG SExprs Index)))))) (ResultsAsStrings) 15_ (ResultsAsStrings (PLUS 1 2) 'ABC (REVERSE '(A B C))) ("3" "ABC" "(C B A)") 16_ (ResultsAsStrings (PLUS 2 3) '(PLUS 2 3) (EVAL (PLUS 2 3))) ("5" "(PLUS 2 3)" "5") What are nospread functions good for? Nospread functions are handy whenever you want to write a function that handles an arbitrary number of arguments. COND, PROG, AND, OR, and PLUS are all implemented as nospread functions since they all handle an arbitrary number of arguments. Another example might be the print function, LC.Print, from Homework #35. LC.Print was defined as a spread function as follows: (DEFINEQ (LC.Print (LAMBDA (#Items Thing1 Thing2 Thing3 Thing4 Thing5) (FOR Thing IN (LIST Thing1 Thing2 Thing3 Thing4 Thing5) AS Ctr FROM 1 TO #Items DO (PRIN1 Thing)) (TERPRI)))) This version of LC.Print could handle at most 5 things to be printed and required a sixth argument specifying how many actual arguments there were if there were less than 5. But LC.Print should have been defined as a nospread function as follows: (DEFINEQ (LC.Print (LAMBDA Things (FOR Index FROM 1 TO Things DO (PRIN1 (ARG Things Index))) (TERPRI)))) Note that this nospread version of LC.Print can print an arbitrary number of things and does not require an explicit parameter specifying how many arguments there are. Summary of 4 function types Since Spread/Nospread and LAMBDA/NLAMBDA are orthogonol distinctions, there are a total of 4 different kinds of functions in Interlisp: LAMBDA spread LAMBDA nospread NLAMBDA spread NLAMBDA nospread You can find out the type of an arbitrary function in Interlisp using the ?= facility in the Lisp Exec. In the Lisp Exec, just type a "(", the function name, a space, the characters "?=" and a carriage return. Once the return is typed, the ?= will be erased and on the next line there will be a print out the parameter "list" of the named function. Following the parameter list will be a indication of the type of the function as follows: LAMBDA spread -- Blank LAMBDA nospread -- {L*} NLAMBDA spread -- {NL} NLAMBDA nospread -- {NL*} Examples: LAMBDA spread JDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDODO7?UUUUUUUUUUUVDUUUUUUUUUUUWO];DO]w;UUUUUUUUUUUVD~]~߶UUUUUUUUUUUW~ߺתO~ߺתDO~߽UUUUUUUUUUUVD}UUUUUUUUUUUW7?着ODOUUUUUUUUUUUVDUUUUUUUUUUUWLDLD! LD @DL8DLDLDDqd!Ld!DLT!DT!L!LL!DLDqDLDL@D8 >|#D`B @$HLAB @$HDL8 B~ D c 0~ c 0ǹL c ǹ@DLٿfy@DLDLDODDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD LAMBDA nospread IDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDOO7?UUUUUUUUUUUVUUUUUUUUUUUWO];O]w;UUUUUUUUUUUV~]~߶UUUUUUUUUUUW~ߺתO~ߺתO~߽UUUUUUUUUUUV}UUUUUUUUUUUW7?着OOUUUUUUUUUUUVUUUUUUUUUUUWLL LD@L8pLLDqd!Ld!LT!T!L!LL!LDqLL@8p<D"LA"XqcL8p"d <@ @LDA @ L8p @@LL``0B*L o>|BË @L n;0C "@* 30B @    30B @L 36B " @@L 3B  @`LLODDDDDDDDDDDDDDDDDDDDDDDDDDDD NLAMBDA spread IDDDDDDDDDDDDDDDDDDDDDDDDDDDD@Gꪪ >UUUUUUUUUUUUGUUUUUUUUUUUUGwꪪw{C=ꪪ{=;UUUUUUUUUUUUG߀w{~߷UUUUUUUUUUUUG~߷ꪪw~߷ꪪw~߷{UUUUUUUUUUUUG}~;{UUUUUUUUUUUUG >}~?{ꪪꪪUUUUUUUUUUUUGUUUUUUUUUUUUGGPG 0 @0>|GGG G G 0 G~"B$G3`10"C$3`10C$|10BG`10G3`10Bd3`10Bd~0B'G`@G8p 0 GGDDDDDDDDDDDDDDDDDDDDDDDDDDDD@DDDDDDDDDDDDDDDDDDDDDDDDDDDD@ NLAMBDA nospread IDDDDDDDDDDDDDDDDDDDDDDDDDDDD@Gꪪ >UUUUUUUUUUUUGUUUUUUUUUUUUGwꪪw{C=ꪪ{=;UUUUUUUUUUUUG߀w{~߷UUUUUUUUUUUUG~߷ꪪw~߷ꪪw~߷{UUUUUUUUUUUUG}~;{UUUUUUUUUUUUG >}~?{ꪪꪪUUUUUUUUUUUUGUUUUUUUUUUUUGGPG 0 @0>|GGG!Â|G G@`0G0Z ? 6 5 /' 7 9 9. 9/ 93 9 92 026 1x 1?   ?O     ?O     ,  } > ? 6 5 / . -" / 7 9: 9 9A 9 0&2r , AJ @6 + 64 5 /. / / * 1 1I + 6 5 / 5 = 02 : : : : AJ   :           9 9Z ) )   )   )   : < ) BMOBJ.GETFN2 TIMESROMAN < ) BMOBJ.GETFN2 TIMESROMAN < ) BMOBJ.GETFN2 TIMESROMAN < ) BMOBJ.GETFN2 TIMESROMAN () 4  7658888"88288!88 +97787 +7777;DTDD ;C88'84 87/7 "2B( 3 A5 = ==%%\=< $|$$=A pA[ ;: * ;) E ;  ( # A  o ,e @ ",=_ ! K ; !   / . - -   5   f " 3 !e             7* O  %    .  5 /+ /% /  .' .& .  / ! G   =    %  '  +        #  m            )        ,        !! :  /    (  |  U     }   /& / /! /( (    1     @ ) -  E  !  !8  D  h   /& / . -   . / / . . /( / f   . . -   . . - . ., . !d ( L 5 c '&< <"      @  X  "     ,      .   !                      $  [  % F < $ $                 % $ $ $1 $ $ $& $ $ $ $ A =< > %P %;       <L < )  / / .$ .    / / . . -$ . . !               & <? <   _ V   z  !5555555<   X  = V   $  5565555"55555&.<7 < <a <                     +    G # <f !p(<G<p(4 3F E EIEvEBEEz