SECTION 5 PRIMITIVE FUNCTIONS AND PREDICATES 5.1 Primitive Functions car[x] car gives the first element of a list x, or the left element of a dotted pair x. car of NIL is always NIL. For all other nonlists, e.g., atoms, strings, arrays, and numbers, the value is undefined (and on some implementations may generate an error). cdr[x] cdr gives the rest of a list (all but the first element). This is also the right member of a dotted pair. cdr of NIL is always NIL. The value of cdr is undefined for other nonlists. caar[x] = car[car[x]] All 30 combinations of nested cars and cdrs up to 4 deep cadr[x] = car[cdr[x]] are included in the system. All are compiled cddddr[x] = open by the compiler. cdr[cdr[cdr[cdr[x]]]] cons[x;y] cons constructs a dotted pair of x and y. If y is a list, x becomes the first element of that list. To minimize drum accesses the following algorithm is used in INTERLISP-10, for finding a page on which to put the constructed INTERLISP word. cons[x;y] is placed 1) on the page with y if y is a list and there is room; otherwise 2) on the page with x if x is a list and there is room; otherwise 3) on the same page as the last cons if there is room; otherwise 4) on any page with a specified minimum of storage, presently 16 LISP words. conscount[] value is the number of conses since this INTERLISP was started up. rplacd[x;y] Places the pointer y in the decrement, i.e., 5.1 cdr, of the cell pointed to by x. Thus it physically changes the internal list structure of x, as opposed to cons which creates a new list element. The only way to get a circular list is by using rplacd to place a pointer to the beginning of a list in a spot at the end of the list. The value of rplacd is x. An attempt to rplacd NIL will cause an error, ATTEMPT TO RPLAC NIL, (except for rplacd[NIL;NIL]). An attempt to rplacd any other non-list will cause an error ARG NOT LIST. rplaca[x;y] similar to rplacd, but replaces the address pointer of x, i.e., car, with y. The value of rplaca is x. An attempt to rplaca NIL will cause an error, ATTEMPT TO RPLAC NIL, (except for rplaca[NIL;NIL]). An attempt to rplaca any other non-list will cause an error, ARG NOT LIST. Convention: Naming a function by prefixing an existing function name with f usually indicates that the new function is a fast version of the old, i.e., one which has the same definition but compiles open and runs without any "safety" error checks. frplacd[x;y] Has the same definition as rplacd but compiles open as one instruction. Note that no checks are made on x, so that a compiled frplacd can clobber NIL, producing strange and wondrous effects. frplaca[x;y] Similar to frplacd. quote[x] This is a function that prevents its arguments from being evaluated. Its value is x itself, 1 e.g., (QUOTE FOO) is FOO. kwote[x] (LIST (QUOTE QUOTE) x), if x=A, and y=B, then (KWOTE (CONS x y))= (QUOTE (A . B)). ------------------------------------------------------------------------ 1 Since giving quote more than one argument, e.g., (QUOTE EXPR (CONS X Y)), is almost always a parentheses error, and one that would otherwise go undetected, quote itself generates an error in this case, PARENTHESIS ERROR. 5.2 cond[c1;c2;...;ck] The conditional function of INTERLISP, cond, takes an indefinite number of arguments c1,c2, ... ck, called clauses. Each clause ci is a list (e1i ... eni) of n > 1 items, where the first element is the predicate, and the rest of the elements the consequents. The operation of cond can be paraphrased as IF e11 THEN e21 ... en1 ELSEIF e12 THEN e22 ... en2 ELSEIF e13 ... The clauses are considered in sequence as follows: the first expression e1i of the clause ci is evaluated and its value is classified as false (equal to NIL) or true (not equal to NIL). If the value of e1i is true, the expressions e2i ... eni that follow in clause ci are evaluated in sequence, and the value of the conditional is the value of eni, the last expression in the clause. In particular, if n=1, i.e., if there is only one expression in the clause ci, the value of the conditional is the value of e1i. (which is evaluated only once). If e1i is false, then the remainder of clause ci is ignored, and the next clause ci+1 is considered. If no e1i is true for any clause, the value of the conditional expression is NIL. selectq[x;y1;y2;...;yn;z] selects a form or sequence of forms based on the value of its first argument x. Each yi is a list of the form (si e1i e2i ... eki) where si is the selection key. The operation of selectq can be paraphrased as: IF x=s1 THEN e1i ... eki ELSEIF x=s2 THEN ... ELSE z. If si is an atom, the value of x is tested to see if it is eq to si (not evaluated). If so, the expressions e1i ... eki are evaluated in sequence, and the value of the selectq is the value of the last expression evaluated, i.e., eki. If si is a list, the value of x is compared with each element (not evaluated) of si, and if x is eq to any one of them, then e1i to eki are evaluated in turn as above. If yi is not selected in one of the two ways described, yi+1 is tested, etc., until all the y's have been tested. If none is selected, the value of the selectq is the value of z. z must be present. An example of the form of a selectq is: 5.3 [SELECTQ (CAR X) (Q (PRINT FOO) (FIE X)) ((A E I O U) (VOWEL X)) (COND ((NULL X) NIL) (T (QUOTE STOP] which has two cases, Q and (A E I O U) and a default condition which is a cond. selectq compiles open, and is therefore very fast; however, it will not work if the value of x is a list, a large integer, or floating point number, since selectq uses eq for all comparisons. prog1[x1;x2;...;xn] evaluates its arguments in order, that is, first x1, then x2, etc, and returns the value of its first argument x1, e.g., (PROG1 X (SETQ X Y)) sets x to y, and returns x's original value. progn[x1i;x2i;...;xn] progn evaluates each of its arguments in order, and returns the value of its last argument as its value. progn is used to specify more than one computation where the syntax allows only one, e.g., (SELECTQ ... (PROGN ...)) allows evaluation of several expressions as the default condition for a selectq. prog[args;e1;e2;...;en] This function allows the user to write an ALGOL-like program containing INTERLISP expressions (forms) to be executed. The first argument, args, is a list of local variables (must be NIL if no variables are used). Each atom in args is treated as the name of a local variable and bound to NIL. args can also contain lists of the form (atom form). In this case, atom is the name of the variable and is bound to the value of form. The evaluation takes place before any of the bindings are performed, e.g., (PROG ((X Y) (Y X)) ...) will bind x to the value of y and y to the (original) value of x. The rest of the prog is a sequence of non-atomic statements (forms) and atomic symbols used as labels for go. The forms are evaluated sequentially; the labels serve only as markers. The two special functions go and return alter this flow of control as described below. The value of the prog is usually specified by the 5.4 function return. If no return is executed, i.e., if the prog "falls off the end," the value of the prog is NIL. go[x] go is the function used to cause a transfer in a prog. (GO L) will cause the program to continue at the label L. A go can be used at any level in a prog. If the label is not found, go will search higher progs within the same function, e.g., (PROG -- A -- (PROG -- (GO A))). If the label is not found in the function in which the prog appears, an error is generated, UNDEFINED OR ILLEGAL GO. return[x] A return is the normal exit for a prog. Its argument is evaluated and is the value of the prog in which it appears. If a go or return is executed in an interpreted function which is not a prog, the go or return will be executed in the last interpreted prog entered if any, otherwise cause an error. go or return inside of a compiled function that is not a prog is not allowed, and will cause an error at compile time. As a corollary, go or return in a functional argument, e.g., to sort, will not work compiled. Also, since nlsetq's and ersetq's compile as separate functions, a go or return cannot be used inside of a compiled nlsetq or ersetq if the corresponding prog is outside, i.e., above, the nlsetq or ersetq. set[x;y] This function sets x to y. Its value is y. If x is not a literal atom, causes an error, ARG NOT LITATOM. If x is NIL, causes an error, ATTEMPT TO SET NIL. Note that set is a normal lambda-spread function, i.e., its arguments are evaluated before it is called. Thus, if the value of x is c, and the value of y is b, then set[x;y] would result in c having value b, and b being returned as the value of set. setq[x;y] An nlambda version of set: the first argument is 5.5 2 not evaluated, the second is. Thus if the value of X is C and the value of Y is B, (SETQ X Y) would result in X (not C) being set to B, and B being returned. If x is not a literal atom, an error is generated, ARG NOT LITATOM. If x is NIL, the error ATTEMPT TO SET NIL is generated. setqq[x;y] Like setq except that neither argument is evaluated, e.g., (SETQQ X (A B C)) sets x to (A B C). gettopval[atm] returns top level value of atm from its value cell (even if NOBIND), regardless of any intervening bindings. Interpreted, generates an error, ARG NOT LITATOM, if atm is not a literal atom. Compiles open without any error checks. settopval[atm;val] Sets top level value of atm, regardless of any intervening bindings, i.e., stores val in value cell of atm. Value is val. Interpreted, generates an error ATTEMPT TO SET NIL, if atm=NIL, or ARG NOT LITATOM, if atm is not a literal atom. Compiles open without any error checks. rpaq[x;y] An nlambda function like setq, except always works on top level value of x, i.e., on the value cell. rpaqq[x;y] An nlambda function like setqq for top level values. rpaq and rpaqq are used by prettydef (Section 14). Both rpaq and rpaqq generate errors if x is not a literal atom. Both are affected by the value of dfnflg (Section 8). If dfnflg = ALLPROP (and the value of x is other than NOBIND), instead of setting x, the corresponding value is stored on the property list of x under the property VALUE. Both are undoable. ------------------------------------------------------------------------ 2 Since setq is an nlambda, neither argument is evaluated during the calling process. However, setq itself calls eval on its second argument. Note that as a result, typing (SETQ var form) and SETQ(var form) to lispx is equivalent: in both cases var is not evaluated, and form is. 5.6 Changing and Restoring System State In INTERLISP, a computation can be interrupted/aborted at any point due to an error, or more forcefully, because a control-D was typed, causing return to the top level. This situation creates problems for programs that need to perform a computation with the system in a "different state", e.g., different radix, input file, readtable, etc. or simply a different value for some global variable, e.g., helpflag, dfnflg, etc., but want to "protect" the calling environment, i.e., be able to restore the state when the computation has completed. While errors can be 3 "caught" by errorsets, control-D cannot. Thus the system may be left in its changed state as a result of the computation being aborted. The following functions address this problem: resetlst[resetx] nlambda, nospread. resetx is a list of forms. resetlst sets up an errorset so that any reset operations performed by resetsave (see below) are restored when the evaluation of resetx has been completed (or an error occurs, or a control-D is typed). If no error occurs, the value of resetlst is the value of the last form on resetx, otherwise resetlst generates an error (after performing the necessary restorations). resetlst compiles open. resetsave[resetx] nlambda, nospread function for use under a 4 resetlst. If car of resetx is atomic, resets the top level value of car of resetx to the value of cadr of resetx, e.g., (RESETSAVE LISPXHISTORY EDITHISTORY) resets the value of lispxhistory to be edithistory and provides for the original value of lispxhistory to be restored when the resetlst completes operation, (or an error occurs, or a control-D is typed). If car of resetx is not atomic, it is a form that is evaluated. If cdr of resetsave is NIL, e.g., (RESETSAVE (RADIX 8)), the form must return as its value its "former state", so that ------------------------------------------------------------------------ 3 i.e., not conveniently. The program could of course redefine control-D as a userinterrupt, check for it, reenable it, and call reset or something similar. 4 resetsave can be called when not under a resetlst. In this case, the restoration will be performed at the next RESET, i.e., control-D or control-C reenter. In other words, there is an "implicit" resetlst at the top level in evalqt. 5.7 the effect of evaluating the form can be reversed, and the system state can be restored, by applying car of the form to the value of the form, e.g., (RESETSAVE (RADIX 8)) performs (RADIX 8), and provides for radix to be reset to its original value when the resetlst completes by applying radix to the value returned by (RADIX 8). For functions which do not return their "previous setting", the restoring expression can be specified as the value of the second argument to resetsave, which in this case is evaluated before the first argument, e.g., 5 [RESETSAVE(SETBRK --)(LIST(QUOTE SETBRK)(GETBRK]. will restore the break characters by applying setbrk to the value returned by (GETBRK), which was computed before the (SETBRK --) expression was evaluated. (RESETSAVE NIL form) is permissible. It simply specifies that the value of form be treated as a restoration expression, e.g., (RESETSAVE NIL (LIST (QUOTE CLOSEF) FILE)) will cause file to be closed when the resetlst that the resetsave is under completes (or an error occurs or a control-D is typed). resetsave compiles open. Its value is not a "useful" quantity. resetvar[var;new-value;form] Nlambda function. Simplified form of resetlst and resetsave for resetting and restoring global variables. Equivalent to (RESETLST (RESETSAVE var new-value) form), e.g., (RESETVAR LISPXHISTORY EDITHISTORY (FOO)) resets lispxhistory to the value of edithistory while evaluating (FOO). resetvar compiles open. If no error occurs, its value is the value of form. resetform[form1;form2] Nlambda function. Simplified form of resetlst and resetsave for resetting a system state when the corresponding function returns as its value the "previous setting." Equivalent to (RESETLST (RESETSAVE form1) form2), e.g., (RESETFROM (RADIX 8) (FOO)). resetform compiles open. If no error occurs, its value is the value returned by form2. ------------------------------------------------------------------------ 5 Note that the restoration expression is still "evaluated" by applying its car to its cdr. 5.8 For some applications, the restoration operation must be different depending on whether the computation completed successfully or was aborted by an error or control-D. To facilitate this, while the restoration operation is being performed, the value of resetstate will be bound to NIL, ERROR, or RESET, depending on whether the exit was normal, due to an error, or reset (i.e., control-D, or in INTERLISP-10, control-C followed by reenter). For example, (RESETLST (RESETSAVE (INFILE X) (LIST '[LAMBDA (FL) (AND (EQ RESETSTATE 'RESET) (CLOSEF FL) (DELFILE FL] X)) . forms) will cause X to be closed and deleted only if a control-D was typed during the execution of forms. For convenience in specifying complicated restoring expressions, the variable oldvalue is bound to the value of the saving expression. For example, 6 (RESETLST (RESETSAVE (INPUT FL) '(AND RESETSAVE (INPUT OLDVALUE))) . forms) will restore the primary input file if an error or control-D occurs. In addition, the function resetundo, in conjunction with resetlst and resetsave, provides a way of specifying that the system be restored to its prior state by undoing the side effects of the computations performed under the resetlst. Undoing and resetundo are described in Section 22. 5.2 Predicates and Logical Connectives atom[x] is T if x is an atom; NIL otherwise. litatom[x] is T if x is a literal atom, i.e., an atom and not a number, NIL otherwise. numberp[x] is x if x is a number, NIL otherwise. Convention: Functions that end in p are usually predicates, i.e., they test for some condition. ------------------------------------------------------------------------ 6 Without using oldvalue, the user would have to write (RESETLST (SETQ TEM (INPUT FL)) (RESETSAVE NIL (LIST '(LAMBDA (FL) (AND RESETSTATE (INPUT FL))) TEM)) forms) 5.9 7 stringp[x] is x if x is a string, NIL otherwise. arrayp[x] is x if x is an array, NIL otherwise. listp[x] is x if x is a list-structure, i.e., one created by one or more conses; NIL otherwise. Note that arrays and strings are not atoms, but are also not lists, i.e., both atom and listp will return NIL when given an array or a string. nlistp[x] not[listp[x]] eq[x;y] The value of eq is T, if x and y are pointers to the same structure in memory, and NIL otherwise. eq is compiled open by the compiler. Its value is not guaranteed T for equal numbers which are not small integers. See eqp. neq[x;y] The value of neq is T, if x is not eq to y, and NIL otherwise. null[x] eq[x;NIL] not[x] same as null, that is eq[x;NIL]. eqp[x;y] The value of eqp is T if x and y are eq, i.e., pointers to the same structure in memory, or if 8 x and y are numbers and are equal in value. Its value is NIL otherwise. equal[x;y] The value of equal is T (1) if x and y are eq, i.e., pointers to the same structure in memory; or (2) eqp, i.e., numbers with equal value; or (3) strequal, i.e., strings containing the same sequence of characters; or (4) lists and car of ------------------------------------------------------------------------ 7 For other string functions, see Section 10. 8 For more discussion of eqp and other number functions, see Section 13. 5.10 x is equal to car of y, and cdr of x is equal to 9 cdr of y. The value of equal is NIL otherwise. Note that x and y do not have to be eq. equaln[x;y;depth] like equal, except if depth of car recursion plus depth of cdr recursion exceeds depth, assumes equality and does not search further along that chain, e.g., equaln[(((A)) B);(((Z)) B);2]=T. For use with (possibly) circular structures. and[x1;x2;...;xn] Takes an indefinite number of arguments (including 0). If all of its arguments have non-null value, its value is the value of its last argument, otherwise NIL. e.g., and[x;member[x;y]] will have as its value either NIL or a tail of y. and[]=T. Evaluation stops at the first argument whose value is NIL. or[x1;x2;...;xn] Takes an indefinite number of arguments (including 0). Its value is that of the first argument whose value is not NIL, otherwise NIL if all arguments have value NIL. e.g., or[x;numberp[y]] has its value x, y, or NIL. or[]=NIL. Evaluation stops at the first argument whose value is not NIL. every[everyx;everyfn1;everyfn2] Is T if the result of applying everyfn1 to each element in everyx is true, otherwise NIL. e.g., every[(X Y Z); ATOM]=T. every operates by computing 10 everyfn1[car[everyx]]. If this yields NIL, every immediately returns NIL. Otherwise, every computes everyfn2[everyx], or cdr[everyx] if everyfn2=NIL, and uses this as the "new" everyx, and the process continues, e.g., every[x;ATOM;CDDR] is true if every other element of x is atomic. every compiles open. ------------------------------------------------------------------------ 9 A loose description of equal might be to say that x and y are equal if they print out the same way. 10 Actually, everyfn1[car[everyx];everyx] is computed, so for example everyfn1 can look at the next element on everyx if necessary. 5.11 some[somex;somefn1;somefn2] value is the tail of somex beginning with the first element that satisfies somefn1, i.e., for which somefn1 applied to that element is true. Value is NIL if no such element exists. e.g., some[x;(LAMBDA (Z) (EQUAL Z Y))] is equivalent to member[y;x]. some operates analogously to every. At each stage, somefn1[car[somex];somex] is computed, and if this is not NIL, somex is returned as the value of some. Otherwise, somefn2[somex] is computed, or cdr[somex] if somefn2=NIL, and used for the next somex. some compiles open. notany[somex;somefn1,somefn2] same as not[some[somex;somefn1;somefn2]] notevery[everyx;everyfn1;everyfn2] not[every[everyx;everyfn1;everyfn2]] memb[x;y] Determines if x is a member of the list y, i.e., if there is an element of y eg to x. If so, its value is the tail of the list y starting with that element. If not, its value is NIL. fmemb[x;y] Fast version of memb that compiles open as a five instruction loop, terminating on a NULL check. Interpreted, fmemb gives an error, BAD ARGUMENT - FMEMB, if y ends in a non-list other than NIL. member[x;y] Identical to memb except that it uses equal instead of eq to check membership of x in y. The reason for the existence of both memb and member is that eq compiles as one instruction but equal requires a function call, and is therefore considerably more expensive. Wherever possible, the user should write (and use) functions that use eq instead of equal. tailp[x;y] Is x, if x is a tail of y, i.e., x is eq to some 11 number of cdrs > 0 1 of y, we say x is a proper tail.> of y, NIL otherwise. ------------------------------------------------------------------------ 11 If x is eq to some number of cdrs 5.12 assoc[key;alst] alst is a list of lists (usually dotted pairs). The value of assoc is the first sublist of alst whose car is eq to key. If such a list is not found, the value is NIL. Example: assoc[B;((A . 1) (B . 2) (C . 3))] = (B . 2). fassoc[key;alst] Fast version of assoc that compiles open as a 6 instruction loop, terminating on a NULL check. Interpreted, fassoc gives an error if alst ends in a non-list other than NIL, BAD ARGUMENT - FASSOC. sassoc[key;alst] Same as assoc but uses equal instead of eq. putassoc[key;val;alst] Searches alst for an element car of which is eq to key. If one is found, cdr is replaced (using rplacd) with val. If no such element is found, cons[prop;val] is added at the end of alst. Value is ?. If alst is NIL, generates an error, ATTEMPT TO RPLAC NIL. Otherwise, if alst is not a list, generates an error, ARG NOT LIST. listget[lst;prop] Similar to getprop (Section 7) but works on lists using property list format. Searches lst two elements at a time, i.e., by cddr, looking for an element eq to prop. If one is found, returns the next element of lst, otherwise NIL. Generates an ARG NOT LIST error if lst is non- NIL and not a list. listput[lst;prop;val] Similar to putprop. Searches lst by cddr looking for prop. If prop is found, replaces the next element of lst with val. Otherwise, prop and val are added to the end of lst. Value is ?. Generates an ARG NOT LIST error if lst is not a list. listget1[lst;prop] Like listget but searches lst one cdr at a time, i.e., looks at each element. listput1[lst;prop;val] Like listput except searches lst one cdr at a time. 5.13