{Begin SubSec The IF Statement} {Title The IF Statement} {Text {Tag IFStatement} {index IF-THEN-ELSE statements} The {lisp IF} statement provides a way of way of specifying conditional expressions that is much easier and readable than using the {fn COND} function directly. CLISP translates expressions employing {lisp IF}, {lisp THEN}, {lisp ELSEIF}, or {lisp ELSE} into equivalent {fn COND} expressions. In general, statements of the form: {lispcode (IF {arg AAA} THEN {arg BBB} ELSEIF {arg CCC} THEN {arg DDD} ELSE {arg EEE})} are translated to: {lispcode (COND ({arg AAA} {arg BBB}) ({arg CCC} {arg DDD}) (T {arg EEE}) )} The segment between {lisp IF} or {lisp ELSEIF} and the next {lisp THEN} corresponds to the predicate of a {lisp COND} clause, and the segment between {lisp THEN} and the next {lisp ELSE} or {lisp ELSEIF} as the consequent(s). {lisp ELSE} is the same as {lisp ELSEIF T THEN}. These words are spelling corrected using the spelling list {var CLISPIFWORDSPLST}.{index CLISPIFWORDSPLST Var} Lower case versions ({lisp if}, {lisp then}, {lisp elseif}, {lisp else}) may also be used. If there is nothing following a {lisp THEN}, or {lisp THEN} is omitted entirely, then the resulting {fn COND} clause has a predicate but no consequent. For example, {lisp (IF X THEN ELSEIF {ellipsis})} and {lisp (IF X ELSEIF {ellipsis})} both translate to {lisp (COND (X) {ellipsis})}, which means that if {lisp X} is not {lisp NIL}, it is returned as the value of the {fn COND}. CLISP considers {lisp IF}, {lisp THEN}, {lisp ELSE}, and {lisp ELSEIF} to have lower precedence than all infix and prefix operators, as well as Interlisp forms, so it is sometimes possible to omit parentheses around predicate or consequent forms. For example, {lisp (IF FOO X Y THEN {ellipsis})} is equivalent to {lisp (IF (FOO X Y) THEN {ellipsis})}, and {lisp (IF X THEN FOO X Y ELSE {ellipsis})} as equivalent to {lisp (IF X THEN (FOO X Y) ELSE {ellipsis})}. Essentially, CLISP determines whether the segment between {lisp THEN} and the next {lisp ELSE} or {lisp ELSEIF} corresponds to one form or several and acts accordingly, occasionally interacting with the user to resolve ambiguous cases. Note that if {lisp FOO} is bound as a variable, {lisp (IF FOO THEN {ellipsis})} is translated as {lisp (COND (FOO {ellipsis}))}, so if a call to the {it function} {lisp FOO} is desired, use {lisp (IF (FOO) THEN {ellipsis})}. {note actually, in this last example: if FOO bound -> assume FOO is var if FOO unbound,def -> assume FOO is fn if FOO unbound,undef -> ask user "=(FOO)?"} }{End SubSec The IF Statement} {Begin SubSec The Iterative Statement} {Title The Iterative Statement} {Text {Tag IterativeStatement} {index *BEGIN* iterative statements} The iterative statement (i.s.) in its various forms permits the user to specify complicated iterative statements in a straightforward and visible manner. Rather than the user having to perform the mental transformations to an equivalent Interlisp form using {fn PROG}, {fn MAPC}, {fn MAPCAR}, etc., the system does it for him. The goal was to provide a robust and tolerant facility which could "make sense" out of a wide class of iterative statements. Accordingly, the user should not feel obliged to read and understand in detail the description of each operator given below in order to use iterative statements. {index *BEGIN* i.s.oprs} An iterative statement is a form consisting of a number of special words (known as i.s. operators or i.s.oprs), followed by operands. Many i.s.oprs ({lisp FOR}, {lisp DO}, {lisp WHILE}, etc.) are similar to iterative statements in other programming languages; other i.s.oprs ({lisp COLLECT}, {lisp JOIN}, {lisp IN}, etc.) specify useful operations in a Lisp environment. Lower case versions of i.s.oprs ({lisp do}, {lisp collect}, etc.) can also be used. Here are some examples of iterative statements: {lispcode ← (for X from 1 to 5 do (PRINT 'FOO)) FOO FOO FOO FOO FOO NIL ← (for X from 2 to 10 by 2 collect (TIMES X X)) (4 16 36 64 100) ← (for X in '(A B 1 C 6.5 NIL (45)) count (NUMBERP X)) 2} {note other examples!!} Iterative statements are implemented through CLISP, which translates the form into the appropriate {fn PROG}, {fn MAPCAR}, etc. Iterative statement forms are translated using all CLISP declarations in effect (standard/fast/undoable/ etc.); see {PageRef Tag CLISPDeclarations}. Misspelled i.s.oprs are recognized and corrected using the spelling list {var CLISPFORWORDSPLST}.{index CLISPFORWORDSPLST Var}{index spelling lists} The order of appearance of operators is never important; CLISP scans the entire statement before it begins to construct the equivalent Interlisp form. New i.s.oprs can be defined as described on {PageRef Fn I.S.OPR}. If the user defines a function by the same name as an i.s.opr ({lisp WHILE}, {lisp TO}, etc.), the i.s.opr will no longer have the CLISP interpretation when it appears as {fn CAR} of a form, although it will continue to be treated as an i.s.opr if it appears in the interior of an iterative statement. To alert the user, a warning message is printed, e.g., {lisp (WHILE DEFINED, THEREFORE DISABLED IN CLISP)}.{index DEFINED, THEREFORE DISABLED IN CLISP Error} {note does this also happen if you redefine IF or ELSEIF??--yes!} {Begin SubSec I.s.types} {Title I.s.types} {Text {index *PRIMARY* i.s.type} {Tag i.s.type} The following i.s.oprs are examples of a certain kind of iterative statement operator called an i.s.type. The i.s.type specifies what is to be done at each iteration. Its operand is called the "body" of the iterative statement. Each iterative statement must have one and only one i.s.type. {Def {Type (I.S. Operator)} {Name DO} {Args FORM} {NoParens} {Text Specifies what is to be done at each iteration. {lisp DO} with no other operator specifies an infinite loop. If some explicit or implicit terminating condition is specified, the value of the i.s. is {lisp NIL}. Translates to {fn MAPC} or {fn MAP} whenever possible. }} {Def {Type (I.S. Operator)} {Name COLLECT} {Args FORM} {NoParens} {Text Specifies that the value of {arg FORM} at each iteration is to be collected in a list, which is returned as the value of the i.s. when it terminates. Translates to {fn MAPCAR}, {fn MAPLIST} or {fn SUBSET} whenever possible. When {lisp COLLECT} translates to a {fn PROG} (e.g., if {lisp UNTIL}, {lisp WHILE}, etc. appear in the i.s.), the translation employs an open {fn TCONC} using two pointers similar to that used by the compiler for compiling {fn MAPCAR}. To disable this translation, perform {lisp (CLDISABLE 'FCOLLECT)}.{index CLDISABLE Fn} }} {Def {Type (I.S. Operator)} {Name JOIN} {Args FORM} {NoParens} {Text Similar to {lisp COLLECT}, except that the values of {arg FORM} at each iteration are {fn NCONC}ed. Translates to {fn MAPCONC} or {fn MAPCON} whenever possible. {fn /NCONC}, {fn /MAPCONC}, and {fn /MAPCON} are used when the CLISP declaration {lisp UNDOABLE} is in effect. }} {Def {Type (I.S. Operator)} {Name SUM} {Args FORM} {NoParens} {Text Specifies that the values of {arg FORM} at each iteration be added together and returned as the value of the i.s., e.g., {lisp (FOR I FROM 1 TO 5 SUM I↑2)} is equal to {lisp 1+4+9+16+25}. {fn IPLUS}, {fn FPLUS}, or {fn PLUS} will be used in the translation depending on the CLISP declarations in effect. }} {Def {Type (I.S. Operator)} {Name COUNT} {Args FORM} {NoParens} {Text Counts the number of times that {arg FORM} is true, and returns that count as its value. }} {Def {Type (I.S. Operator)} {Name ALWAYS} {Args FORM} {NoParens} {Text Returns {lisp T} if the value of {arg FORM} is non-{lisp NIL} for all iterations. (Note: returns {lisp NIL} as soon as the value of {arg FORM} is {lisp NIL}). }} {Def {Type (I.S. Operator)} {Name NEVER} {Args FORM} {NoParens} {Text Similar to {lisp ALWAYS}, except returns {lisp T} if the value of {arg FORM} is {it never} true. (Note: returns {lisp NIL} as soon as the value of {arg FORM} is non-{lisp NIL}). }} The following i.s.types explicitly refer to the iteration variable (i.v.) of the iterative statement, which is a variable set at each iteration. This is explained below under {lisp FOR}. {Def {Type (I.S. Operator)} {Name THEREIS} {Args FORM} {NoParens} {Text Returns the first value of the i.v. for which {arg FORM} is non-{lisp NIL}, e.g., {lisp (FOR X IN Y THEREIS (NUMBERP X))} returns the first number in {lisp Y}. (Note: returns the value of the i.v. as soon as the value of {arg FORM} is non-{lisp NIL}). }} {Def {Type (I.S. Operator)} {Name LARGEST} {Args FORM} {NoParens} } {Def {Type (I.S. Operator)} {Name SMALLEST} {Args FORM} {NoParens} {Text Returns the value of the i.v. that provides the largest/smallest value of {arg FORM}. {lisp $$EXTREME}{index $$EXTREME Var} is always bound to the current greatest/smallest value, {lisp $$VAL}{index $$VAL Var} to the value of the i.v. from which it came. }} }{End SubSec I.s.types} {Begin SubSec Iteration Variable I.s.oprs} {Title Iteration Variable I.s.oprs} {Text {Def {Type (I.S. Operator)} {Name FOR} {Args VAR} {NoParens} {Text Specifies the iteration variable (i.v.) which is used in conjunction with {lisp IN}, {lisp ON}, {lisp FROM}, {lisp TO}, and {lisp BY}. The variable is rebound within the i.s., so the value of the variable outside the i.s. is not effected. Example: {lispcode ← (SETQ X 55) 55 ← (for X from 1 to 5 collect (TIMES X X)) (1 4 9 16 25) ← X 55} }} {Def {Type (I.S. Operator)} {Name FOR} {Args VARS} {NoParens} {Text {arg VARS} a list of variables, e.g., {lisp (FOR (X Y Z) IN {ellipsis})}. The first variable is the i.v., the rest are dummy variables. See {lisp BIND} below. }} {Def {Type (I.S. Operator)} {Name FOR OLD} {Args VAR} {NoParens} {Text Similar to {lisp FOR}, except that {arg VAR} is {it not} rebound within the i.s., so the value of the i.v. outside of the i.s. is changed. Example: {lispcode ← (SETQ X 55) 55 ← (for old X from 1 to 5 collect (TIMES X X)) (1 4 9 16 25) ← X 6} }} {Def {Type (I.S. Operator)} {Name BIND} {Args VAR} {NoParens} } {Def {Type (I.S. Operator)} {Name BIND} {Args VARS} {NoParens} {Text Used to specify dummy variables, which are bound locally within the i.s. }} Note: {lisp FOR}, {lisp FOR OLD}, and {lisp BIND} variables can be initialized by using the form {lisp {arg VAR}←{arg FORM}}: {lispcode (FOR OLD (X←{arg FORM}) BIND (Y←{arg FORM}) {ellipsis})} {Def {Type (I.S. Operator)} {Name IN} {Args FORM} {NoParens} {Text Specifies that the i.s. is to iterate down a list with the i.v. being reset to the corresponding element at each iteration. For example, {lisp (FOR X IN Y DO {ellipsis})} corresponds to {lisp (MAPC Y (FUNCTION (LAMBDA (X) {ellipsis})))}. If no i.v. has been specified, a dummy is supplied, e.g., {lisp (IN Y COLLECT CADR)} is equivalent to {lisp (MAPCAR Y (FUNCTION CADR))}. }} {Def {Type (I.S. Operator)} {Name ON} {Args FORM} {NoParens} {Text Same as {lisp IN} except that the i.v. is reset to the corresponding {it tail} at each iteration. Thus {lisp IN} corresponds to {fn MAPC}, {fn MAPCAR}, and {fn MAPCONC}, while {lisp ON} corresponds to {fn MAP}, {fn MAPLIST}, and {fn MAPCON}. }} Note: for both {lisp IN} and {lisp ON}, {arg FORM} is evaluated before the main part of the i.s. is entered, i.e. {it outside} of the scope of any of the bound variables of the i.s. For example, {lisp (FOR X BIND (Y←'(1 2 3)) IN Y {ellipsis})} will map down the list which is the value of {lisp Y} evaluated {it outside} of the i.s., {it not} {lisp (1 2 3)}. {Def {Type (I.S. Operator)} {Name IN OLD} {Args VAR} {NoParens} {Text {index IN (I.S. Operator)}{index OLD (I.S. Operator)}Specifies that the i.s. is to iterate down {arg VAR}, with {arg VAR} itself being reset to the corresponding tail at each iteration, e.g., after {lisp (FOR X IN OLD L DO {ellipsis} UNTIL {ellipsis})} finishes, {lisp L} will be some tail of its original value. }} {Def {Type (I.S. Operator)} {Name IN OLD} {PrintName {lisp IN OLD ({arg VAR}←{arg FORM})}} {Text {index OLD (I.S. Operator)}Same as {lisp IN OLD {arg VAR}}, except {arg VAR} is first set to value of {arg FORM}. }} {Def {Type (I.S. Operator)} {Name ON OLD} {Args VAR} {NoParens} {Text {index ON (I.S. Operator)}{index OLD (I.S. Operator)}Same as {lisp IN OLD {arg VAR}} except the i.v. is reset to the current value of {arg VAR} at each iteration, instead of to {lisp (CAR {arg VAR})}. }} {Def {Type (I.S. Operator)} {Name IN OLD} {PrintName {lisp ON OLD ({arg VAR}←{arg FORM})}} {Text {index ON (I.S. Operator)}{index OLD (I.S. Operator)}Same as {lisp ON OLD {arg VAR}}, except {arg VAR} is first set to value of {arg FORM}. }} {Def {Type (I.S. Operator)} {Name INSIDE} {Args FORM} {NoParens} {Text Similar to {lisp IN}, except treats first non-list, non-{lisp NIL} tail as the last element of the iteration, e.g., {lisp INSIDE '(A B C D . E)} iterates five times with the i.v. set to {lisp E} on the last iteration. {lisp INSIDE 'A} is equivalent to {lisp INSIDE '(A)}, which will iterate once. }} {Def {Type (I.S. Operator)} {Name FROM} {Args FORM} {NoParens} {Text Used to specify an initial value for a numerical i.v. The i.v. is automatically incremented by 1 after each iteration (unless {index BY (I.S. Operator)}{lisp BY} is specified). If no i.v. has been specified, a dummy i.v. is supplied and initialized, e.g., {lisp (FROM 2 TO 5 COLLECT SQRT)} returns {lisp (1.414 1.732 2.0 2.236)}. {note need a simpler example....} }} {Def {Type (I.S. Operator)} {Name TO} {Args FORM} {NoParens} {Text Used to specify the final value for a numerical i.v. If {index FROM (I.S. Operator)}{lisp FROM} is not specified, the i.v. is initialized to 1. If no i.v. has been specified, a dummy i.v. is supplied and initialized. If {index BY (I.S. Operator)}{lisp BY} is not specified, the i.v. is automatically incremented by 1 after each iteration.{foot except when both the operands to {lisp TO} and {lisp FROM} are numbers, and {lisp TO}'s operand is less than {lisp FROM}'s operand, e.g., {lisp FROM 10 TO 1}, in which case the i.v. is decremented by 1 after each iteration. In this case, the i.s. terminates when the i.v. becomes {it less} than the value of {arg FORM}. }{comment endfootnote} When the i.v. is definitely being {it incremented}, i.e., either {lisp BY} is not specified, or its operand is a positive number, the i.s. terminates when the i.v. exceeds the value of {arg FORM} e.g., {lisp (FOR X FROM 1 TO 10 --)} is equivalent to {lisp (FOR X FROM 1 UNTIL (X GT 10) --)}. Similarly, when the i.v. is definitely being decremented the i.s. terminates when the i.v. becomes {it less} than the value of {arg FORM} (see description of {lisp BY}). Note: {arg FORM} is evaluated only once, when the i.s. is first entered, and its value bound to a temporary variable against which the i.v. is checked each interation. If the user wishes to specify an i.s. in which the value of the boundary condition is recomputed each iteration, he should use {lisp WHILE} or {lisp UNTIL} instead of {lisp TO}. }} {Def {Type (I.S. Operator)} {Name BY} {PrintName {lisp BY {arg FORM} {rm (with {lisp IN}/{lisp ON})}}} {Text If {index IN (I.S. Operator)}{lisp IN} or {index ON (I.S. Operator)}{lisp ON} have been specified, the value of {arg FORM} determines the {it tail} for the next iteration, which in turn determines the value for the i.v. as described earlier, i.e., the new i.v. is {fn CAR} of the tail for {lisp IN}, the tail itself for {lisp ON}. In conjunction with {lisp IN}, the user can refer to the current tail within {arg FORM} by using the i.v. or the operand for {lisp IN}/{lisp ON}, e.g., {lisp (FOR Z IN L BY (CDDR {emphasize Z}) {ellipsis})} or {lisp (FOR Z IN L BY (CDDR {emphasize L}) {ellipsis})}. At translation time, the name of the internal variable which holds the value of the current tail is substituted for the i.v. throughout {arg FORM}. For example, {lisp (FOR X IN Y BY (CDR (MEMB 'FOO (CDR X))) COLLECT X)} specifies that after each iteration, {fn CDR} of the current tail is to be searched for the atom {lisp FOO}, and ({fn CDR} of) this latter tail to be used for the next iteration. }} {Def {Type (I.S. Operator)} {Name BY} {PrintName {lisp BY {arg FORM} {rm (without {lisp IN}/{lisp ON})}}} {Text If {lisp IN} or {lisp ON} have not been used, {lisp BY} specifies how the i.v. itself is reset at each iteration. If {index FROM (I.S. Operator)}{lisp FROM} or {index TO (I.S. Operator)}{lisp TO} have been specified, the i.v. is known to be numerical, so the new i.v. is computed by adding the value of {arg FORM} (which is reevaluated each iteration) to the current value of the i.v., e.g., {lisp (FOR N FROM 1 TO 10 BY 2 COLLECT N)} makes a list of the first five odd numbers. If {arg FORM} is a positive number,{foot {arg FORM} itself, not its value, which in general CLISP would have no way of knowing in advance. }{comment endfootnote} the i.s. terminates when the value of the i.v. {it exceeds} the value of {lisp TO}'s operand. If {arg FORM} is a negative number, the i.s. terminates when the value of the i.v. becomes {it less} than {lisp TO}'s operand, e.g., {lisp (FOR I FROM N TO M BY -2 UNTIL (I LT M) ...)}. Otherwise, the terminating condition for each iteration depends on the value of {arg FORM} for that iteration: if {arg FORM}<0, the test is whether the i.v. is less than {lisp TO}'s operand, if {arg FORM}>0 the test is whether the i.v. exceeds {lisp TO}'s operand, otherwise if {arg FORM}=0, the i.s. terminates unconditionally. If {index FROM (I.S. Operator)}{lisp FROM} or {index TO (I.S. Operator)}{lisp TO} have not been specified and {arg FORM} is not a number, the i.v. is simply reset to the value of {arg FORM} after each iteration, e.g., {lisp (FOR I FROM N BY M ...)} is equivalent to {lisp (FOR I←N BY (IPLUS I M) ...)}. {note addition is done with IPLUS, FPLUS, or PLUS, according to current CLISP declarations} }} {Def {Type (I.S. Operator)} {Name AS} {Args VAR} {NoParens} {Text Used to specify an iterative statement involving more than one iterative variable, e.g., {lisp (FOR X IN Y AS U IN V DO --)} corresponds to {fn MAP2C}. The i.s. terminates when any of the terminating conditions are met, e.g., {lisp (FOR X IN Y AS I FROM 1 TO 10 COLLECT X)} makes a list of the first ten elements of {lisp Y}, or however many elements there are on {lisp Y} if less than 10. The operand to {index AS (I.S. Operator)}{lisp AS}, {arg VAR}, specifies the new i.v. For the remainder of the i.s., or until another {lisp AS} is encountered, all operators refer to the new i.v. For example, {lisp (FOR I FROM 1 TO N1 AS J FROM 1 TO N2 BY 2 AS K FROM N3 TO 1 BY -1 --)} terminates when {lisp I} exceeds {lisp N1}, or {lisp J} exceeds {lisp N2}, or {lisp K} becomes less than 1. After each iteration, {lisp I} is incremented by 1, {lisp J} by 2, and {lisp K} by -1. }} {Def {Type (I.S. Operator)} {Name OUTOF} {Args FORM} {NoParens} {Text For use with generators ({PageRef Tag Generators}). On each iteration, the i.v. is set to successive values returned by the generator. The i.s. terminates when the generator runs out. }} }{End SubSec Iteration Variable I.s.oprs} {Begin SubSec Condition I.s.oprs} {Title Condition I.s.oprs} {Text {Def {Type (I.S. Operator)} {Name WHEN} {Args FORM} {NoParens} {Text Provides a way of excepting certain iterations. For example, {lisp (FOR X IN Y COLLECT X WHEN (NUMBERP X))} collects only the elements of {lisp Y} that are numbers. }} {Def {Type (I.S. Operator)} {Name UNLESS} {Args FORM} {NoParens} {Text Same as {lisp WHEN} except for the difference in sign, i.e., {lisp WHEN Z} is the same as {lisp UNLESS (NOT Z)}. }} {Def {Type (I.S. Operator)} {Name WHILE} {Args FORM} {NoParens} {Text Provides a way of terminating the i.s. {lisp WHILE {arg FORM}} evaluates {arg FORM} {it before} each iteration, and if the value is {lisp NIL}, exits. }} {Def {Type (I.S. Operator)} {Name UNTIL} {Args FORM} {NoParens} {Text Same as {lisp WHILE} except for difference in sign, i.e., {lisp WHILE X} is equivalent to {lisp UNTIL (NOT X)}. }} {Def {Type (I.S. Operator)} {Name UNTIL} {PrintName {lisp UNTIL {arg N}} ({arg N} a number)} {Text Equivalent to {lisp UNTIL ({arg I.V.} GT {arg N})}. }} {Def {Type (I.S. Operator)} {Name REPEATWHILE} {Args FORM} {NoParens} {Text Same as {lisp WHILE} except the test is performed after the evalution of the body, but before the i.v. is reset for the next iteration. }} {Def {Type (I.S. Operator)} {Name REPEATUNTIL} {Args FORM} {NoParens} {Text Same as {lisp UNTIL}, except the test is performed after the evaluation of the body. }} {Def {Type (I.S. Operator)} {Name REPEATUNTIL} {PrintName {lisp REPEATUNTIL {arg N}} ({arg N} a number)} {Text Equivalent to {lisp REPEATUNTIL ({arg I.V.} GT {arg N})}. }} }{End SubSec Condition I.s.oprs} {Begin SubSec Other I.s.oprs} {Title Other I.s.oprs} {Text {Def {Type (I.S. Operator)} {Name FIRST} {Args FORM} {NoParens} {Text {arg FORM} is evaluated once before the first iteration, e.g., {lisp (FOR X Y Z IN L FIRST (FOO Y Z) {ellipsis})}, and {lisp FOO} could be used to initialize {lisp Y} and {lisp Z}. }} {Def {Type (I.S. Operator)} {Name FINALLY} {Args FORM} {NoParens} {Text {arg FORM} is evaluated after the i.s. terminates. For example, {lisp (FOR X IN L BIND Y←0 DO (IF ATOM X THEN Y←Y+1) FINALLY (RETURN Y))} will return the number of atoms in {lisp L}. }} {Def {Type (I.S. Operator)} {Name EACHTIME} {Args FORM} {NoParens} {Text {arg FORM} is evaluated at the beginning of each iteration before, and regardless of, any testing. For example, consider, {lispcode (FOR I FROM 1 TO N DO ({ellipsis} (FOO I) {ellipsis}) UNLESS ({ellipsis} (FOO I) {ellipsis}) UNTIL ({ellipsis} (FOO I) {ellipsis}))} The user might want to set a temporary variable to the value of {lisp (FOO I)} in order to avoid computing it three times each iteration. However, without knowing the translation, he would not know whether to put the assignment in the operand to {lisp DO}, {lisp UNLESS}, or {lisp UNTIL}, i.e., which one would be executed first. He can avoid this problem by simply writing {lisp EACHTIME (SETQ J (FOO I))}. }} {Def {Type (I.S. Operator)} {Name DECLARE:} {Args DECL} {NoParens} {Text Inserts the form {lisp (DECLARE {arg DECL})} immediately following the {fn PROG} variable list in the translation, or, in the case that the translation is a mapping function rather than a {fn PROG}, immediately following the argument list of the lambda expression in the translation. This can be used to declare variables bound in the iterative statement to be compiled as local or special variables (see {PageRef Tag LOCALVARS}). For example {lisp (FOR X IN Y DECLARE: (LOCALVARS X) {ellipsis})}. Several {lisp DECLARE:}s can apppear in the same i.s.; the declarations are inserted in the order they appear. }} {Def {Type (I.S. Operator)} {Name DECLARE} {Args DECL} {NoParens} {Text Same as {lisp DECLARE:}. Note that since {fn DECLARE} is also the name of a function, {lisp DECLARE} cannot be used as an i.s. operator when it appears as {fn CAR} of a form, i.e. as the first i.s. operator in an iterative statement. However, {lisp declare} (lower-case version) {it can} be the first i.s. operator. }} {Def {Type (I.S. Operator)} {Name ORIGINAL} {Args I.S.OPR OPERAND} {NoParens} {Text {arg I.S.OPR} will be translated using its original, built-in interpretation, independent of any user defined i.s. operators. See {PageRef FN I.S.OPR}. }} There are also a number of i.s.oprs that make it easier to create iterative statements that use the clock, looping for a given period of time. See Timers, {PageRef Tag Timers}. }{End SubSec Other I.s.oprs} {Begin SubSec Miscellaneous} {Title Miscellaneous} {Text {Begin UnlabeledList Miscellaneous} {Item Lowercase versions of all i.s. operators are equivalent to the uppercase, e.g., {lisp (for X in Y {ellipsis})}. } {Item Each i.s. operator is of lower precedence than all Interlisp forms, so parentheses around the operands can be omitted, and will be supplied where necessary, e.g., {lisp BIND (X Y Z)} can be written {lisp BIND X Y Z, OLD (X←{arg FORM})} as {lisp OLD X←{arg FORM}}, {lisp WHEN (NUMBERP X)} as {lisp WHEN NUMBERP X}, etc. } {Item {index RETURN (in iterative statement)}{lisp RETURN} or {index GO (in iterative statement)}{lisp GO} may be used in any operand. (In this case, the translation of the iterative statement will always be in the form of a {lisp PROG}, never a mapping function.) {lisp RETURN} means return from the i.s. (with the indicated value), {it not} from the function in which the i.s appears. {lisp GO} refers to a label elsewhere in the function in which the i.s. appears, except for the labels {lisp $$LP}, {lisp $$ITERATE}, and {lisp $$OUT} which are reserved, as described below. } {Item In the case of {index FIRST (I.S. Operator)}{lisp FIRST}, {index FINALLY (I.S. Operator)}{lisp FINALLY}, {index EACHTIME (I.S. Operator)}{lisp EACHTIME}, {lisp DECLARE:} or one of the i.s.types, e.g., {lisp DO}, {lisp COLLECT}, {lisp SUM}, etc., the operand can consist of more than one form, e.g., {lisp COLLECT (PRINT X:1) X:2}, in which case a {lisp PROGN} is supplied. } {Item Each operand can be the name of a function, in which case it is applied to the (last) i.v., e.g., {lisp (FOR X IN Y DO PRINT WHEN NUMBERP)} is the same as {lisp (FOR X IN Y DO (PRINT X) WHEN (NUMBERP X))}. Note that the i.v. need not be explicitly specified, e.g., {lisp (IN Y DO PRINT WHEN NUMBERP)} will work. For i.s.types, e.g., {lisp DO}, {lisp COLLECT}, {lisp JOIN}, the function is always applied to the first i.v. in the i.s., whether explicity named or not. For example, {lisp (IN Y AS I FROM 1 TO 10 DO PRINT)} prints elements on {lisp Y}, not integers between 1 and 10. Note that this feature does not make much sense for {lisp FOR}, {lisp OLD}, {lisp BIND}, {lisp IN}, or {lisp ON}, since they "operate" before the loop starts, when the i.v. may not even be bound. In the case of {index BY (I.S. Operator)}{lisp BY} in conjunction with {index IN (I.S. Operator)}{lisp IN}, the function is applied to the current {it tail} e.g., {lisp FOR X IN Y BY CDDR ...} is the same as {lisp FOR X IN Y BY (CDDR X)...}. } {Item While the exact form of the translation of an iterative statement depends on which operators are present, a {fn PROG} will always be used whenever the i.s. specifies dummy variables, i.e., if a {lisp BIND} operator appears, or there is more than one variable specified by a {lisp FOR} operator, or a {fn GO}, {fn RETURN}, or a reference to the variable {lisp $$VAL} appears in any of the operands. When a {fn PROG} is used, the form of the translation is: {lispcode (PROG {arg VARIABLES} {bracket initialize} $$LP {bracket eachtime} {bracket test} {bracket body} $$ITERATE {bracket aftertest} {bracket update} (GO $$LP) $$OUT {bracket finalize} (RETURN $$VAL))} where {lisp {bracket test}} corresponds to that portion of the loop that tests for termination and also for those iterations for which {lisp {bracket body}} is not going to be executed, (as indicated by a {lisp WHEN} or {lisp UNLESS}); {lisp {bracket body}} corresponds to the operand of the i.s.type, e.g., {lisp DO}, {lisp COLLECT}, etc.; {lisp {bracket aftertest}} corresponds to those tests for termination specified by {lisp REPEATWHILE} or {lisp REPEATUNTIL}; and {lisp {bracket update}} corresponds to that part that resets the tail, increments the counter, etc. in preparation for the next iteration. {lisp {bracket initialize}}, {lisp {bracket finalize}}, and {lisp {bracket eachtime}} correspond to the operands of {lisp FIRST}, {lisp FINALLY}, and {lisp EACHTIME}, if any. Note that since {lisp {bracket body}} always appears at the top level of the {fn PROG}, the user can insert labels in {lisp {bracket body}}, and {fn GO} to them from within {lisp {bracket body}} or from other i.s. operands, e.g., {lisp (FOR X IN Y FIRST (GO A) DO (FOO) A (FIE))}. However, since {lisp {bracket body}} is dwimified as a list of forms, the label(s) should be added to the dummy variables for the iterative statement in order to prevent their being dwimified and possibly "corrected", e.g., {lisp (FOR X IN Y BIND A FIRST (GO A) DO (FOO) A (FIE))}. The user can also {fn GO} to {lisp $$LP}, {lisp $$ITERATE}, or {lisp $$OUT}, or explicitly set {lisp $$VAL}.{index $$VAL Var} } {End UnlabeledList Miscellaneous} }{End SubSec Miscellaneous} {Begin SubSec Errors in Iterative Statements} {Title Errors in Iterative Statements} {Text {index errors in iterative statements} An error will be generated and an appropriate diagnostic printed if any of the following conditions hold: {Begin LabeledList errors} {Indent 5percent} {Label 1.} {item Operator with null operand, i.e., two adjacent operators, as in {lisp FOR X IN Y UNTIL DO --} } {Label 2.} {item Operand consisting of more than one form (except as operand to {lisp FIRST}, {lisp FINALLY}, or one of the i.s.types), e.g., {lisp FOR X IN Y (PRINT X) COLLECT --}. } {Label 3.} {item {lisp IN}, {lisp ON}, {lisp FROM}, {lisp TO}, or {lisp BY} appear twice in same i.s. } {Label 4.} {item Both {lisp IN} and {lisp ON} used on same i.v. } {Label 5.} {item {lisp FROM} or {lisp TO} used with {lisp IN} or {lisp ON} on same i.v. } {Label 6.} {item More than one i.s.type, e.g., a {lisp DO} and a {lisp SUM}. } {End LabeledList errors} In 3, 4, or 5, an error is not generated if an intervening {lisp AS} occurs. If an error occurs, the i.s. is left unchanged. If no {lisp DO}, {lisp COLLECT}, {lisp JOIN} or any of the other i.s.types are specified, CLISP will first attempt to find an operand consisting of more than one form, e.g., {lisp FOR X IN Y {emphasize (PRINT X)} WHEN ATOM X}, and in this case will insert a {lisp DO} after the first form. (In this case, condition 2 is not considered to be met, and an error is not generated.) If CLISP cannot find such an operand, and no {lisp WHILE} or {lisp UNTIL} appears in the i.s., a warning message is printed: {lisp NO DO, COLLECT, OR JOIN:} followed by the i.s.{index NO DO, COLLECT, OR JOIN Error} Similarly, if no terminating condition is detected, i.e., no {lisp IN}, {lisp ON}, {lisp WHILE}, {lisp UNTIL}, {lisp TO}, or a {fn RETURN} or {fn GO}, a warning message is printed:{foot unless the value of {var CLISPI.S.GAG}{index CLISPI.S.GAG Var} is {lisp T} (initially {lisp NIL}). }{comment endfootnote} {lisp POSSIBLE NON-TERMINATING ITERATIVE STATEMENT:}{index POSSIBLE NON-TERMINATING ITERATIVE STATEMENT Error} followed by the iterative statement. However, since the user may be planning to terminate the i.s. via an error, control-E, or a {fn RETFROM} from a lower function, the i.s. is still translated. }{End SubSec Errors in Iterative Statements} {Begin SubSec Defining New Iterative Statement Operators} {Title Defining New Iterative Statement Operators} {Text {index *BEGIN* defining new iterative statement operators} The following function is available for defining new or redefining existing iterative statement operators: {FnDef {FnName I.S.OPR} {FnArgs NAME FORM OTHERS EVALFLG} {Text {arg NAME} is the name of the new i.s.opr. If {arg FORM} is a list, {arg NAME} will be a new {it i.s.type}{index i.s.type} (see {PageRef Tag i.s.type}), and {arg FORM} its body. {arg OTHERS} is an (optional) list of additional i.s. operators and operands which will be added to the i.s. at the place where {arg NAME} appears. If {arg FORM} is {lisp NIL}, {arg NAME} is a new i.s.opr defined entirely by {arg OTHERS}. In both {arg FORM} and {arg OTHERS}, the atom {lisp $$VAL} can be used to reference the value to be returned by the i.s., {lisp I.V.} to reference the current i.v., and {lisp BODY} to reference {arg NAME}'s operand. In other words, the current i.v. will be substituted for all instances of {lisp I.V.} and {arg NAME}'s operand will be substituted for all instances of {lisp BODY} throughout {arg FORM} and {arg OTHERS}. If {arg EVALFLG} is {lisp T}, {arg FORM} and {arg OTHERS} are evaluated at translation time, and their values used as described above. A dummy variable for use in translation that does not clash with a dummy variable already used by some other i.s. operators can be obtained by calling {lisp (GETDUMMYVAR)}. {lisp (GETDUMMYVAR T)} will return a dummy variable and also insure that it is bound as a {fn PROG} variable in the translation. {note Should (GETDUMMYVAR BINDIT) be documented separately, with its own index entry?} If {arg NAME} was previously an i.s.opr and is being redefined, the message {lisp ({arg NAME} REDEFINED)} will be printed (unless {var DFNFLG}={lisp T}), and all expressions using the i.s.opr {arg NAME} that have been translated will have their translations discarded. }} For example, for {lisp COLLECT}, {arg FORM} would be {lisp (SETQ $$VAL (NCONC1 $$VAL BODY))}. For {lisp SUM}, {arg FORM} would be {lisp ($$VAL←$$VAL+BODY)},{foot {lisp $$VAL+BODY} is used instead of {lisp (IPLUS $$VAL BODY)} so that the choice of function used in the translation, i.e., {fn IPLUS}, {fn FPLUS}, or {fn PLUS}, will be determined by the declarations then in effect. {note bletch!! is there no way to do this without using CLISP infix operators??} }{comment endfootnote} {arg OTHERS} would be {lisp (FIRST $$VAL←0)}. For {lisp NEVER}, {arg FORM} would be {lisp (IF BODY THEN $$VAL←NIL (GO $$OUT)))}.{foot {lisp (IF BODY THEN (RETURN NIL))} would exit from the i.s. immediately and therefore not execute the operations specified via a {lisp FINALLY} (if any). }{comment endfootnote} For {lisp THEREIS}, {arg FORM} would be {lisp (IF BODY THEN $$VAL←I.V. (GO $$OUT))}. Examples: To define {lisp RCOLLECT}, a version of {lisp COLLECT} which uses {fn CONS} instead of {fn NCONC1} and then reverses the list of values: {lispcode (I.S.OPR 'RCOLLECT '($$VAL←(CONS BODY $$VAL)) '(FINALLY (RETURN (DREVERSE $$VAL)))]} To define {lisp TCOLLECT}, a version of {lisp COLLECT} which uses {fn TCONC}: {lispcode (I.S.OPR 'TCOLLECT '(TCONC $$VAL BODY) '(FIRST $$VAL←(CONS) FINALLY (RETURN (CAR $$VAL)))]} To define {lisp PRODUCT}: {lispcode (I.S.OPR 'PRODUCT '($$VAL←$$VAL*BODY) '(FIRST $$VAL←1)]} To define {lisp UPTO}, a version of {lisp TO} whose operand is evaluated only once: {lispcode (I.S.OPR 'UPTO NIL '(BIND $$FOO←BODY TO $$FOO)]} To redefine {lisp TO} so that instead of recomputing {arg FORM} each iteration, a variable is bound to the value of {arg FORM}, and then that variable is used: {lispcode (I.S.OPR 'TO NIL '(BIND $$END FIRST $$END←BODY ORIGINAL TO $$END)]} Note the use of {index ORIGINAL (I.S. Operator)}{lisp ORIGINAL} to redefine {lisp TO} in terms of its original definition. {lisp ORIGINAL} is intended for use in redefining built-in operators, since their definitions are not accessible, and hence not directly modifiable. Thus if the operator had been defined by the user via {fn I.S.OPR}, {lisp ORIGINAL} would not obtain its original definition. In this case, one presumably would simply modify the i.s.opr definition. {fn I.S.OPR} can also be used to define synonyms for already defined i.s. operators by calling {fn I.S.OPR} with {arg FORM} an atom, e.g., {lisp (I.S.OPR 'WHERE 'WHEN)} makes {lisp WHERE} be the same as {lisp WHEN}. Similarly, following {lisp (I.S.OPR 'ISTHERE 'THEREIS)}, one can write {lisp (ISTHERE ATOM IN Y)}, and following {lisp (I.S.OPR 'FIND 'FOR)} and {lisp (I.S.OPR 'SUCHTHAT 'THEREIS)}, one can write {lisp (FIND X IN Y SUCHTHAT X MEMBER Z)}. In the current system, {lisp WHERE}{index WHERE (I.S. Operator)} is synonymous with {lisp WHEN}, {lisp SUCHTHAT}{index SUCHTHAT (I.S. Operator)} and {lisp ISTHERE}{index ISTHERE (I.S. Operator)} with {lisp THEREIS}, {lisp FIND}{index FIND (I.S. Operator)} with {lisp FOR}, and {lisp THRU}{index THRU (I.S. Operator)} with {lisp TO}. If {arg FORM} is the atom {lisp MODIFIER}{index MODIFIER Litatom}, then {arg NAME} is defined as an i.s.opr which can immediately follow another i.s. operator (i.e., an error will not be generated, as described previously). {arg NAME} will not terminate the scope of the previous operator, and will be stripped off when {fn DWIMIFY} is called on its operand. {lisp OLD} is an example of a {lisp MODIFIER} type of operator. The {lisp MODIFIER} feature allows the user to define i.s. operators similar to {lisp OLD}, for use in conjunction with some other user defined i.s.opr which will produce the appropriate translation. The file package command {lisp I.S.OPRS}{index I.S.OPRS FileCom}({PageRef FileCom I.S.OPRS}) will dump the definition of i.s.oprs. {lisp (I.S.OPRS PRODUCT UPTO)} as a file package command will print suitable expressions so that these iterative statement operators will be (re)defined when the file is loaded. {index *END* defining new iterative statement operators} }{End SubSec Defining New Iterative Statement Operators} {Begin Note} Date: 15 FEB 1980 0015-PST From: TEITELMAN larry reported a serious problem in current loadup which is that any iteraive statement containing a COLLECT which does not translate into a mapcar, e.g. a collect with a WHILE or a BIND etc. does not dwimify correctly. (1) there is a new loadup which I believe fixes the problem. Hoever, the fix was not straightforard and it is possible i may hve introduced a new problem. if anything develops while i am away, you can simply (2) repair the fix in the current byte.sav by remproving the I.S.OPR property from fcollect (thereby causing it to translate using the (SETQ $$VAL (NCONC1 $$VAL body)) translation. the problem stemmed from fcollect, which is the nly i.s.opr in the world which both evaluates its argument, and also defines an i.s.type. the problem is that it used to be handled by a kluge to take into account the fact that there wasnt anyay to compute the i.s.type so as to use a dummy variable, and also compute an expressionwhich bound the same dummy variable. similar i.s.oprs for inside used to do something like (SUBST (GENSYM) 'VAR exp) where exp included the BIND. but in fcollects case, this didnt work because the BIND and the i.s.type had to be handled separately. I had fixed this up in general but not handled the fcollect case correctly. In case any ofyou wish to use the new faciliy, which is somewhat cleaner, thre is now a functon called GETDUMMYVAR which is to be called from inside of an i.s.opr. It first takes variables from the list ($$TEM1 ... $$TEM6) and then uses GENSYM. If its argument is T, it ALSO effectively binds the variable, i.e. puts it in the progvars list. To see how it is used, look at the i.s.opr def for fcollect, and also for inside, outof etc. {End Note} {Begin Note} Date: 7 FEB 1979 2239-PST From: TEITELMAN Subject: translation of iterative statements ok, if FOO is not defined and has a macro, will translate to (FUNCTION (LAMBDA (X) (FOO X] rather than (FUNCTION FOO) {End Note} {Begin Note} Date: 25 MAR 1977 2308-PST From: TEITELMAN Subject: internal iterative variables not all of the information you want is available when you want it. in fact puting in the EVAL form of i.s.opr took me about two weeks because the situation is as follows (1) you cant dwimify their operands until the entire i.s. has been scanned and all the vars found (2) but some i.s.oprs might contain BINDS etc. so is important to expand the i.s.oprs before dwimifying any of them, however (3) one cannot substitute in the operands into the i.s.opr definitions until after dwiifying since othrwise any oerrors corrected in the operands wont be seen int he oriiginal i.s. furthermore (4) if i subsitute in before dwimifying, i cant distinguish case where user wirtes a $$VAL, thereby requiring a PROG in the translation, or one comes as a result of an i.s.opr expansion such as collect. now for the particulars of your requests: if all you want to do is see if some other operator has been seen prevously in the i.s., this is simple. there is a variable called i.s.ptrs, which is a list of the i.s.oprs seen. CAR of each entry is the lowercase version of the i.s. (in the current lisp, this variable is called forptrs in case you want to experiment). so you can in fact determine if a dfor has been seen at the time of expanding a dbind. the tail of the i.s. immediatel foollowing the operatr is also available at eval time, as is the tail of the i.s. as of the next operator, i.e. everything between the two tails (after dwiifying which doesnt happen till later) will be the body. the way you find this is each element on i.s.ptrs is of the form (opr tail1 tail2) where tail1 points into th i.s. as of opr (however, (CAR of thattail may not be equal to opr, e.g. CAR may be the uppercase version, opr the lower case, or in the caseof an i.s.type opr will be the atm i.s.type, but car of tail1 may be do, collect, etc.) tail2 points to he next opr. so what you want is cdr of tail1, i.e. (CDR (CADR (CAR LAST I.S.PTRS))) note however that if you depend on this sort of thing, then for your dfor, you will have to either handle the cases (DFOR X INTEGER --) (DFOR (X INTEGER) --) (DFOR X ← & INTEGER ← & --) etc. all yourself, or else restrict yourself. {End Note} {index *END* i.s.oprs} {index *END* iterative statements} }{End SubSec The Iterative Statement}