{FnDef {FnName COND} {FnArgs CLAUSE{SUB 1} CLAUSE{SUB 2} {ellipsis} CLAUSE{SUB K}} {Type NOSPREAD NLAMBDA} {Text The conditional function of Interlisp, {fn COND}, takes an indefinite number of arguments, called {index COND clause}clauses. Each {arg CLAUSE{sub i}} is a list of the form {lisp ({arg P{sub i}} {arg C{sub i1}} {ellipsis} {arg C{sub iN}})}, where {arg P{sub i}} is the predicate, and {arg C{sub i1}} {ellipsis} {arg C{sub iN}} are the consequents. The operation of {fn COND} can be paraphrased as: IF {arg P{sub 1}} THEN {arg C{sub 11}} {ellipsis} {arg C{sub 1N}} ELSEIF {arg P{sub 2}} THEN {arg C{sub 21}} {ellipsis} {arg C{sub 2N}} ELSEIF {arg P{sub 3}} {ellipsis} The clauses are considered in sequence as follows: the predicate {arg P{sub 1}} of the clause {arg CLAUSE{sub i}} is evaluated. If the value of {arg P{sub 1}} is "true" (non-{lisp NIL}), the consequents {arg {arg C{sub i1}} {ellipsis} {arg C{sub iN}}} are evaluated in order, and the value of the {fn COND} is the value of {arg C{sub iN}}, the last expression in the clause. If {arg P{sub 1}} is "false" ({fn EQ} to {lisp NIL}), then the remainder of {arg CLAUSE{sub i}} is ignored, and the next clause, {arg CLAUSE{sub i+1}}, is considered. If no {arg P{sub i}} is true for {it any} clause, the value of the {fn COND} is {lisp NIL}. Note: If a clause has no consequents, and has the form {lisp ({arg P{sub i}})}, then if {arg P{sub i}} evaluates to non-{lisp NIL}, it is returned as the value of the {lisp COND}. It is only evaluated once. Example: {lispcode ← (DEFINEQ (DOUBLE (X) (COND ((NUMBERP X) (PLUS X X)) ((STRINGP X) (CONCAT X X)) ((ATOM X) (PACK* X X)) (T (PRINT "unknown") X) ((HORRIBLE-ERROR))] (DOUBLE) ← (DOUBLE 5) 10 ← (DOUBLE "FOO") "FOOFOO" ← (DOUBLE 'BAR) BARBAR ← (DOUBLE '(A B C)) "unknown" (A B C)} A few points about this example: Notice that {lisp 5} is both a number and an atom, but it is "caught" by the {fn NUMBERP} clause before the {fn ATOM} clause. Also notice the predicate {lisp T}, which is always true. This is the normal way to indicate a {fn COND} clause which will always be executed (if none of the preceeding clauses are true). {lisp (HORRIBLE-ERROR)} will never be executed. Note: The {lisp IF} statement ({PageRef Tag IFstatement}) provides an easier and more readable way of coding conditional expressions than {fn COND}. }} {FnDef {FnName AND} {FnArgs X{SUB 1} X{SUB 2} {ellipsis} X{SUB N}} {Type NOSPREAD NLAMBDA} {Text Takes an indefinite number of arguments (including zero), that are evaluated in order. If any argument evaluates to {lisp NIL}, {fn AND} immediately returns {lisp NIL} (without evaluating the remaining arguments). If all of the arguments evaluate to non-{lisp NIL}, the value of the last argument is returned. {lisp (AND) => T}. }} {FnDef {FnName OR} {FnArgs X{SUB 1} X{SUB 2} {ellipsis} X{SUB N}} {Type NOSPREAD NLAMBDA} {Text Takes an indefinite number of arguments (including zero), that are evaluated in order. If any argument is non-{lisp NIL}, the value of that argument is returned by {fn OR} (without evaluating the remaining arguments). If all of the arguments evaluate to {lisp NIL}, {lisp NIL} is returned. {lisp (OR) => NIL}. }} {fn AND} and {fn OR} can be used as simple logical connectives, but note that they may not evaluate all of their arguments. This makes a difference if the evaluation of some of the arguments causes side-effects. Another result of this implementation of {fn AND} and {fn OR} is that they can be used as simple conditional statements. For example: {lisp (AND (LISTP {arg X}) (CDR {arg X}))} returns the value of {lisp (CDR {arg X})} if {arg X} is a list cell, otherwise it returns {lisp NIL} without evaluating {lisp (CDR {arg X})}. In general, this use of {fn AND} and {fn OR} should be avoided in favor of more explicit conditional statements in order to make programs more readable. {Begin Note} SELECT de-documented. It doesn't seem to be used in system files (EXCEPT as a PROG tag). Def moved to Stuff.SELECT. Ron says: SELECTxx fns should be conceptually like (SEL FN X . CLAUSES), where FN is wrapped around each select key (one atom or one list) so (SELECTQ ...) <==> (SEL 'QUOTE ...) (SELECTC ...) <==> (SEL 'CONSTANT ...) (SELCHARQ ...) <==> (SEL 'CHARCODE ...) so far so good...but unfortunately SELECT has somewhat different semantics, so it doesn't fit into this model. In particular, (SELECT X ... ( (A B C) ...) ...) evaluates A, B, and C individually, rather than doing something to (A B C). Also, compiled SELECT doesn't do the right thing if infix operators are in selection keys or rest of clauses. {End Note} {FnDef {FnName SELECTQ} {FnArgs X CLAUSE{SUB 1} CLAUSE{SUB 2} {ellipsis} CLAUSE{SUB K} DEFAULT} {Type NOSPREAD NLAMBDA} {Text Selects a form or sequence of forms based on the value of its first argument {arg X}. Each clause {arg CLAUSE{sub i}} is a list of the form {lisp ({arg S{sub i}} {arg C{sub i1}} {ellipsis} {arg C{sub iN}})} where {arg S{sub i}} is the selection key. The operation of {fn SELECTQ} can be paraphrased as: IF {arg X} = {arg S{sub 1}} THEN {arg C{sub 11}} {ellipsis} {arg C{sub 1N}} ELSEIF {arg X} = {arg S{sub 2}} THEN {ellipsis} ELSE {arg DEFAULT}. If {arg S{sub i}} is an atom, the value of {arg X} is tested to see if it is {fn EQ} to {arg S{sub i}} (which is not evaluated). If so, the expressions {arg C{sub i1}} {ellipsis} {arg C{sub iN}} are evaluated in sequence, and the value of the {fn SELECTQ} is the value of the last expression evaluated, i.e., {arg C{sub iN}}. If {arg S{sub i}} is a list, the value of {arg X} is compared with each element (not evaluated) of {arg S{sub i}}, and if {arg X} is {fn EQ} to any one of them, then {arg C{sub i1}} {ellipsis} {arg C{sub iN}} are evaluated as above. If {arg CLAUSE{sub i}} is not selected in one of the two ways described, {arg CLAUSE{sub i+1}} is tested, etc., until all the clauses have been tested. If none is selected, {arg DEFAULT} is evaluated, and its value is returned as the value of the {fn SELECTQ}. {arg DEFAULT} must be present. An example of the form of a {fn SELECTQ} is: {lispcode [SELECTQ MONTH (FEBRUARY (if (LEAPYEARP) then 29 else 28)) ((APRIL JUNE SEPTEMBER NOVEMBER) 30) 31]} If the value of {lisp MONTH} is the litatom {lisp FEBRUARY}, the {fn SELECTQ} returns 28 or 29 (depending on {lisp (LEAPYEARP)}); otherwise if {lisp MONTH} is {lisp APRIL}, {lisp JUNE}, {lisp SEPTEMBER}, or {lisp NOVEMBER}, the {fn SELECTQ} returns 30; otherwise it returns 31. {fn SELECTQ} compiles open, and is therefore very fast; however, it will not work if the value of {arg X} is a list, a large integer, or floating point number, since {fn SELECTQ} uses {fn EQ} for all comparisons. }} Note: The function {fn SELCHARQ} ({PageRef Fn SELCHARQ}) is a version of {fn SELECTQ} that recognizes {fn CHARCODE} litatoms. {FnDef {FnName SELECTC} {FnArgs X CLAUSE{SUB 1} CLAUSE{SUB 2} {ellipsis} CLAUSE{SUB K} DEFAULT} {Type NOSPREAD NLAMBDA} {Text "{fn SELECTQ}-on-Constant." Similar to {fn SELECTQ} except that the selection keys are evaluated, and the result used as a {fn SELECTQ}-style selection key. {fn SELECTC} is compiled as a {fn SELECTQ}, with the selection keys evaluated at compile-time. Therefore, the selection keys act like compile-time constants (see {PageRef Fn CONSTANT}). For example: {lispcode [SELECTC NUM ( (for X from 1 to 9 collect (TIMES X X)) "SQUARE" ) "HIP"]} compiles as: {lispcode [SELECTQ NUM ( (1 4 9 16 25 36 49 64 81) "SQUARE" ) "HIP"]} }} {FnDef {FnName PROG1} {FnArgs X{SUB 1} X{SUB 2} {ellipsis} X{SUB N}} {Type NOSPREAD NLAMBDA} {Text Evaluates its arguments in order, and returns the value of its first argument {arg X{sub 1}}. For example, {lisp (PROG1 X (SETQ X Y))} sets {lisp X} to {lisp Y}, and returns {lisp X}'s original value. }} {FnDef {FnName PROG2} {FnArgs X{SUB 1} X{SUB 2} {ellipsis} X{SUB N}} {Text Similar to {fn PROG1}. Evaluates its arguments in order, and returns the value of its second argument {arg X{sub 2}}. {note arg! takes advantage of fact that evaluator evals ALL args given to a spread fn ... even excess ones.} }} {FnDef {FnName PROGN} {FnArgs X{SUB 1} X{SUB 2} {ellipsis} X{SUB N}} {Type NOSPREAD NLAMBDA} {Text {fn PROGN} evaluates each of its arguments in order, and returns the value of its last argument. {fn PROGN} is used to specify more than one computation where the syntax allows only one, e.g., {lisp (SELECTQ {ellipsis} (PROGN {ellipsis}))} allows evaluation of several expressions as the default condition for a {lisp SELECTQ}. }} {FnDef {FnName PROG} {FnArgs VARLST E{SUB 1} E{SUB 2} {ellipsis} E{SUB N}} {Type NOSPREAD NLAMBDA} {Text This function allows the user to write an ALGOL-like program containing Interlisp expressions (forms) to be executed. The first argument, {arg VARLST}, is a list of local variables{index local variables} (must be {lisp NIL} if no variables are used). Each atom in {arg VARLST} is treated as the name of a local variable and bound to {lisp NIL}. {arg VARLST} can also contain lists of the form {lisp (atom form)}. In this case, {lisp atom} is the name of the variable and is bound to the value of {lisp form}. The evaluation takes place before any of the bindings are performed, e.g., {lisp (PROG ((X Y) (Y X)) {ellipsis})} will bind local variable {lisp X} to the value of {lisp Y} (evaluated {it outside} the {fn PROG}) and local variable {lisp Y} to the value of {lisp X} (outside the {fn PROG}). An attempt to use anything other than a literal atom as a {fn PROG} variable will cause an error, {index ARG NOT LITATOM Error}{lisp ARG NOT LITATOM}. An attempt to use {lisp NIL} or {lisp T} as a {fn PROG} variable will cause an error, {index ATTEMPT TO BIND NIL OR T Error}{lisp ATTEMPT TO BIND NIL OR T}. The rest of the {fn PROG} is a sequence of non-atomic statements (forms) and litatoms (labels). The forms are evaluated sequentially;{index PROG label} the labels serve only as markers. The two special functions {fn GO} and {fn RETURN} alter this flow of control as described below. The value of the {fn PROG} is usually specified by the function {fn RETURN}. If no {fn RETURN} is executed before the {fn PROG} "falls off the end," the value of the {fn PROG} is {lisp NIL}. {Note Example!!} }} {FnDef {FnName GO} {FnArgs X} {Type NOSPREAD NLAMBDA} {Text {fn GO} is used to cause a transfer in a {fn PROG}. {lisp (GO L)} will cause the {fn PROG} to evaluate forms starting at the label {lisp L} ({fn GO} does not evaluate its argument). A {fn GO} can be used at any level in a {fn PROG}. If the label is not found, {fn GO} will search higher progs {it within the same function,} e.g., {lisp (PROG {ellipsis} A {ellipsis} (PROG {ellipsis} (GO A)))}. If the label is not found in the function in which the {fn PROG} appears, an error is generated, {lisp UNDEFINED OR ILLEGAL GO}.{index UNDEFINED OR ILLEGAL GO Error} }} {FnDef {FnName RETURN} {FnArgs X} {Text A {fn RETURN} is the normal exit for a {fn PROG}. Its argument is evaluated and is immediately returned the value of the {fn PROG} in which it appears. }} Note: If a {fn GO} or {fn RETURN} is executed in an interpreted function which is not a {fn PROG}, the {fn GO} or {fn RETURN} will be executed in the last interpreted {fn PROG} entered if any, otherwise cause an error.{index ILLEGAL RETURN Error}{index UNDEFINED OR ILLEGAL GO Error} {fn GO} or {fn RETURN} inside of a compiled function that is not a {fn PROG} is not allowed, and will cause an error at compile time. As a corollary, {fn GO} or {fn RETURN} in a functional argument, e.g., to {fn SORT}, will not work compiled. Also, since {fn NLSETQ}'s and {fn ERSETQ}'s compile as {it separate} functions, a {fn GO} or {fn RETURN} {it cannot} be used inside of a compiled {index NLSETQ FN}{fn NLSETQ} or {index ERSETQ FN}{fn ERSETQ} if the corresponding {fn PROG} is outside, i.e., above, the {fn NLSETQ} or {fn ERSETQ}. {Note Is the above also true for ERRORSET in general (on which NLSETQ and ERSETQ are based)? ---mjs <-- No! ERRORSET doesn't create a seperate function ... however, it seems that GO and RETURN won't work under a compiled call to ERRORSET anyway (compiles OK, but gives undef or illegal GO error on execution). Heres the story: it seems that GO (and RETURN too) will not work if inside a function called (via fn call) from a compiled function containing a PROG. If the "function" is actually a macro, so that it is compiled open, or is one of the specially-compiled fns (PROG, COND, etc.), then it works. The important thing is the function call from a compiled function containing a PROG. This is not immediately observable by the random user. Should this explaination be documented?? (perhaps in compiler section??)}