REDUCE

7.3 Sequencing Evaluation

These functions provide for explicit control sequencing, and the definition of blocks altering the scope of local variables.

(let A:list [B:form]): any macro
The general form follows, the square brackets are used to indicate zero or more occurances of an expression.
    (LET ([(<var> <value>)]) [<body>])

The <value>s are evaluated (in an unspecified order), and then the <var>s are bound to these values. The body, consisting of the <body> forms, is evaluated in a left to right order. The value returned is the result of the last body form or nil if the body is empty. Note that the <value>s are evaluated in the outer environment, before the <var>s are bound.

This function is equivalent to

    ((lambda ([<var>]) [<body>]) [<value>])

The let-style is attractive since it places the <var>s close to their binding forms (<value>s), thereby increasing readability.

There are two shorthand formats for (<var> <value>). One is (< var >) and the other is just < var >. Both of these mean bind < var > to nil. As a rule of style, you should use (< var > nil) if you mean to use the value of < var > without assigning it it first, and just (< var >) or < var > if you do not care what < var > gets bound to.

The following expression returns the middle element of a vector.

    (let ((n (vector-size vector)))  
      (unless (zerop n)  
        (vector-fetch vector (add1 (/ n 2)))))

(let* A:list [B:form]): any macro
Let* is just like let except that it makes the assignments sequentially. That is, the first binding is made before the value for the second one is computed. The example below illustrates the difference between let and let*.
    1 lisp> (setq front 'red back 'orange)  
    orange  
    2 lisp> (let ((front 'blue) (back front))  back)  
    red  
    3 lisp> (let⋆ ((front 'blue) (back front))  back)  
    blue

(progn [U:form]): any open-compiled fexpr
U is a set of expressions which are executed sequentially. The value returned is the value of the last expression.

(prog2 A:form B:form): any open-compiled expr
Returns the value of the second argument B. Note that prog2 expects only two arguments.

(prog1 [U:form]): any macro
Prog1 is a function defined in the USEFUL package. Prog1 evaluates its arguments in order, like progn, but returns the value of the first.

(prog VARS:id-list [PROGRAM:id,form]): any open-compiled fexpr
VARS is a list of ids which are considered fluid if the prog is interpreted and local if compiled (see the ”Variables and Bindings” Section, 9.2). The prog’s variables are allocated space when the prog form is applied, and are deallocated when the prog is exited. Prog variables are initialized to nil. The program is a set of expressions to be evaluated in order of their appearance in the prog function. An id which appears at the top level of the program are labels which can be referred by go. The value returned by the prog function is determined by a return function or nil if the prog ”falls through”.
    (de sum-up (seq)  
       (prog (sum)  
         (setq sum 0)  
        loop  
         (when (null seq) (return sum))  
         (when (numberp (first seq))  
           (setq sum (plus sum (first seq)))  
           (setq seq (rest seq))  
           (go loop))))

    1 lisp> (sum-up '(1 3 5))  
    9  
    2 lisp> (sum-up '(a))  
    nil

There are restrictions as to where the control functions go and return may be placed. The functions go and return are intended to be used within a prog. This is so that they may have only locally determinable effects. Unfortunately these restrictions are not consistent across compiled and interpreted code. It is recommended that if a non-local exit is required then use catch and throw (see section 8.5).

In interpreted code, upon encountering a return a search is made for the latest instance of an entrance to a prog. If the search is successful then the evaluation of that prog is considered complete, the value returned is the argument to return. If the search for a prog fails then the message

⋆⋆⋆⋆⋆ RETURN attempted outside the scope of a PROG

is displayed. The treatment of return is much different when the code is compiled. A return may appear outside the scope of a prog. For example, the compiler will compile the sequence

(if (not (numberp x)) (return 'unknown))  
(compute x))

as if it were were enclosed within a prog.

(prog ()  
  (if (not (numberp x)) (return 'unknown))  
  (return (compute x))))

Upon reaching a go within interpreted code a search is made for the latest entrance to a prog. If this search is successful then a second search is made for a label which matches the argument to go. The failure of either search is an error. If the first search fails then

⋆⋆⋆⋆⋆ GO attempted outside the scope of a PROG

is printed. The message

⋆⋆⋆⋆⋆ ‘LABEL' is not a label within the current scope

is printed if the second search fails. When a prog form is compiled the compiler expects to be able to resolve all label references. Thus every go must appear within a prog otherwise the following error message is printed.

⋆⋆⋆⋆⋆ FORM invalid go

In addition, the argument to a go must refer to a label defined inside the prog which contains that go. The message

⋆⋆⋆⋆⋆ Compiler bug: missing label LABEL

is printed to indicate when this second restriction is not met.

(go LABEL:id): None Returned open-compiled fexpr
Go alters the normal flow of control within a prog function. The next statement of a prog function to be evaluated is immediately preceded by label.

(return U:form): None Returned open-compiled expr
Within a prog, return terminates the evaluation of a prog and returns U as the value of the prog.

7.3.1 Iteration

(while E:form [S:form]): nil macro
This is a commonly used construct for indefinite iteration in PSL. E is evaluated; if non-nil the S’s are evaluated from left to right and then the process is repeated. If E evaluates to nil the while returns nil. Exit may be used to terminate the while and to return a value. Next may be used to terminate the current iteration.

(repeat [S:form] E:form): nil macro
The S’s are evaluated left to right, and then E is evaluated. This is repeated until the value of E is non-nil, at which point repeat returns nil. Next and exit may be used in the S’s to branch to the next iteration of a repeat or to terminate one and possibly return a value.

(next): None Returned open-compiled, restricted macro
This terminates the current iteration of the most closely surrounding while or repeat, and causes the next to commence. Both while and repeat are macros which expand into prog’s and next is essentially a go. The restrictions on the placement of next are similar to those of go, see section 8.3 for details.

(exit [U:form]): None Returned open-compiled,restricted macro
The U’s are evaluated left to right, the most closely surrounding while or repeat is terminated, and the value of the last U is returned. With no argument nil is returned. Both while and repeat are macros which expand into prog’s and exit is essentially a return. The restrictions on the placement of exit are similar to those of return, see section 8.3 for details.
The following function defintion is intended to illustrate the use of repeat and while. The function will return a list of prime numbers which are less than or equal to the argument N, which is assumed to be greater than one.
  (de primes (n)  
    (let ((result (list 2))  
          (number 3)  
          (pointer)  
         (prime))  
      (while (<= number n)  
        (setq pointer result)  
        (setq prime t)  
        (repeat  
          (when (zerop (remainder number (first pointer)))  
            (setq prime nil))  
          (setq pointer (rest pointer))  
          (or (null pointer) (not prime)))  
        (when prime (setq result (aconc result number)))  
        (incr number))  
      (cons 1 result)))

For

A simple for construct is available in the basic PSL system; an extended version is defined in the USEFUL package. The basic PSL for provides only the iterator FROM and the action clause DO. PSL users should use the extended version, described here:

(for [S:form]): any macro
Each argument to for is a one of the clauses described below. If an argument is not a clause then one of the following continuable errors will occur.
    ⋆⋆⋆⋆⋆ For clauses may not be atomic: ‘SYMBOL'

    ⋆⋆⋆⋆⋆ Unknown for clause operator: ‘LIST'

A clause is a list, its first element is an identifier, the remaining elements are arguments. A clause may introduce a local variable, specify a return value, or specify when the iteration should cease.

The first few clauses are used to introduce local variables. Some of these clauses also provide the means to stop loop iteration.

    (in <variable> <list> <function>)

The variable <variable> is set to successive elements of <list>. The argument <function> is optional. If present, it may be either the name of a function or a lambda expression. The function is applied to the extracted element before it is assigned to <variable>. Once the argument <list> is exhausted the iteration will stop. The only argument which will be evaluated is <list>.

      1 lisp> (for (in v '(0 1) add1)  
                   (do (print v)))  
      1  
      2  
      nil

    (on <variable> <list>)

The variable <variable> is set to successive cdrs of <list>. The first value assigned to <variable> is <list>. Once the <list> is exhausted the iteration will stop. The only argument which will be evaluated is <list>.

      1 lisp> (for (on v '(0 1))  
                   (do (print v)))  
      (0 1)  
      (1)  
      nil

    (from <variable> <initial> <final> <step>)

The variable <variable> is set to <initial> for the first iteration of the loop. The value of <variable> will be incremented by <step> before each following iteration. Once the value of <variable> is larger than <final> the iteration will stop. Both <initial> and <step> are optional, the default values for each is 1. The argument <final> is optional, in which case the iteration must be stopped by another clause. Each argument except for <variable> will be evaluated once, before the first iteration. To specify <step> without <initial> and <final>, or <final> with <initial> omitted place nil in the slot to be omitted.

      1 lisp> (for (from v 1 5 2)  
                   (do (print v)))  
      1  
      3  
      5

    (FOR <variable> <initial> <next>)

At the outset of the first iteration, the variable <variable> will be set to the evaluation of <initial>. Prior to subsequent iterations, the expression <next> will be evaluated and assigned to <variable>.

      1 lisp> (for (for v 1 (add1 v))  
                   (until (> v 3))  
                   (do (print v)))  
 
      1  
      2  
      3  
      nil

    (with [<variable-form>])

Each argument <variable-form> is either <variable> or (<variable> <initial>). The square brackets are used to indicate zero or more occurances of <variable-form>. If the first form is used then the variable will be set to nil prior to the first iteration. With the second form the variable will be set to the value of <initial>.

    (do [<form>])

The square brackets are used to indicate zero or more occurances of <form>. Each expression <form> is evaluated during each iteration. They are evaluated in the order of their appearance. You may use return within a <form>, it will cause an immediate exit from the for.

There are two clauses which allow for the evaluation of expressions before the first iteration, and after the last iteration.

    (initially [<form>])

The square brackets are used to indicate zero or more occurances of <form>. Once the iteration variables have been bound to their values each expression <form> will be evaluated. They are evaluated in the order of their appearance.

    (finally [<form>])

The square brackets are used to indicate zero or more occurances of <form>. After the final iteration each expression <form> will be evaluated. They are evaluated in the order of their appearance. The use of the clauses always and never (described below), may prevent evaluation of the arguments to this clause. There are clauses which specify a return value, if none of them are used then the value of the last <form> will be the value of the for.

      1 lisp> (for (from v 1 3)  
                   (finally v))  
      4  
      nil  
      2 lisp> (for (for v 1 (add1 v))  
                   (always (< v 3))  
                   (finally v))  
      nil

The next few clauses are used to build a value to be returned from for Except for the returns and returning clauses, a second argument is used in these clauses to specify that instead of returning the result it will be stored as the value of this second argument. This means that the second argument should be an identifier, it will not be evaluated.

    1 lisp> (for (in v '(0 1))  
                 (collect (add1 v)))  
    (1 2)  
    2 lisp> (for (in v '(0 1))  
                 (collect (add1 v) result))  
    nil  
    3 lisp> result  
    (1 2)

If more than one return value is implied then an error will result

    1 lisp> (for (in v '(0 1))  
                 (collect v)  
                 (adjoin v))  
    ⋆⋆⋆⋆⋆ For loops may only return one value

    (returns [<form>])

Prior to returning from the for each <form> is evaluated. The order of evalution is left to right. The value of the last <form> is returned as the value of the for.A synonym for returns is returning.

    (collect <form> <variable>)

This clause is used to build a list. During each iteration the value of <form> is added at the end of the list. The use of the optional argument <variable> is described above.

      1 lisp> (for (on v '(one two))  
                   (collect v))  
      ((one two) (two))

    (ADJOIN <form> <variable>)  
    (ADJOINQ <form> <variable>)

These clauses are similar to collect. The difference is that the value of <form> is only added to the list if it is not already an element. To detemine membership in the list adjoin uses member, adjoinq uses memq. The use of the optional argument <variable> is described above.

      1 lisp> (for (in i '("one" "one"))  
                   (adjoin i one)  
                   (adjoinq i two))  
      nil  
      2 lisp> one  
      ("one")  
      3 lisp> two  
      ("one" "one")

    (JOIN <form> <variable>)  
    (CONC <form> <variable>)

These clauses are similar to collect. The difference is that the value of <form> is appended (nonc is used with the conc clause), to the end of the list. The use of the optional argument <variable> is described above.

      1 lisp> (for (on v '(one two))  
                   (join v))  
      (one two two)

You should be careful with conc. In the example, if conc were used in place of join the computation would never terminate.

    (UNION <form> <variable>)  
    (UNIONQ <form> <variable>)

These clauses are used to build a set. During each iteration the union of <form> and the set being constructed is computed. Set membership is determined with equal for union, and eq for unionq. The use of the optional argument <variable> is described above.

      1 lisp> (for (in v '((0 2) (0 1 2 3) (0)))  
                   (unionq v))  
      (3 2 1 0)

    (INTERSECTION <form> <variable>)  
    (INTERSECTIONQ <form> <variable>)

These clauses are similar to union and unionq. The difference is that the intersection of sets in constructed instead of the union. The use of the optional argument <variable> is described above.

      1 lisp> (for (in v '((0 2) (0 1 2 3) (0)))  
                   (intersectionq v))  
      (0)

    (COUNT <form> <variable>)

Returns the number of times <form> evaluates to a nonnil value. The use of the optional argument <variable> is described above.

      1 lisp> (for (in v '(0 1 2))  
                   (count (zerop (remainder v 2))))  
      2

    (SUM <form> <variable>)

Returns the sum of each evaluation of <form>. The use of the optional argument <variable> is described above.

      1 lisp> (for (in v '(2 3 4))  
                   (sum v))  
      9

    (PRODUCT <form> <variable>)

Returns the product of each evaluation of <form>. The use of the optional argument <variable> is described above.

      1 lisp> (for (in v '(2 3 4))  
                   (product v))  
      24

    (MAXIMIZE <form> <variable>)

Returns the maximum value of <form>. The use of the optional argument <variable> is described above.

      1 lisp> (for (in v '(1 2 3))  
                   (maximize v))  
      3

    (MINIMIZE <form> <variable>)

Returns the minimum value of <form>. The use of the optional argument <variable> is described above.

      1 lisp> (for (in v '(1 2 3))  
                   (minimize v))  
      1

    (MAXIMAL <value> <test> <variable>)  
    (MINIMAL <value> <test> <variable>)

These clauses are generalizations of the clauses maximize and minimize. Maximal determines the greatest value of <test> over all of the loop iterations. The corresponding value of <value> is returned. As a particular case it is possible to return the value of an iteration variable for which some function attains a maximum value. The functions used for comparisons are greaterp and lessp. The use of the optional argument <variable> is described above.

      1 lisp> (for (in v '(2 -2))  
                   (minimal v (- (expt v 2) (⋆ 7 v))))  
      2

The remaining clauses are used to control loop iteration.

    (ALWAYS [<form>])

The square brackets are used to indicate zero or more occurances of <form>. If there is more than one form then the clause is equivalent to (ALWAYS (and [<form>])). This clause is used to specify a return value, t is returned if each <form> is non-nil during each iteration. If one of the <form>s evaluates to nil then the for is terminated and nil is returned, this means that arguments of any finally clause will not be evaluated.

      1 lisp> (for (in v '(1 0 2))  
                   (always (> v 0))  
                   (do (print v)))  
      1  
      nil  
 
    (never [<form>])

The square brackets are used to indicate zero or more occurances of <form>. If there is more than one form then the clause is equivalent to (NEVER (or [<form>])). Equivalent to (ALWAYS (not [<form>])).

    (THEREIS <form>)

If the argument <form> evaluates to a non-nil value then the for is terminated, the value of <form> is the return value.

      1 lisp> (for (in v '(-1 0 2))  
                   (thereis (and (zerop v) v))  
                   (do (print v)))  
      -1  
      0

    (WHILE [<form>])

The square brackets are used to indicate zero or more occurances of <form>. If there is more than one form then the clause is equivalent to (WHILE (and [<form>])). Loop iteration stops if any <form> evaluates to nil.

      1 lisp> (for (from v 2 nil -1)  
                   (while (> v 0))  
                   (collect (sqrt v))  
                   (finally (print v)))  
      0  
      (1.41421 1.0)

    (UNTIL [<form>])

The square brackets are used to indicate zero or more occurances of <form>. If there is more than one form then the clause is equivalent to (UNTIL (or [<form>])). Loop iteration stops if any <form> evaluates to a non nil value.

    (WHEN <form>)  
      Jump to the next iteration if the value of <form> is nil.

      1 lisp> (for (in v '(-2 2))  
                   (when (> v 0))  
                   (do (print (sqrt v))))  
      1.41421  
      nil

    (UNLESS <form>)

Jump to the next iteration if the value of <form> is non-nil.

The evaluation of a for expression follows a specific order, irregardless of the order in which you place clauses.

  1. Bind loop variables to their initial values. Each of the expressions which represent initial values is evaluated before the variables are bound.
  2. If an initially clause is present, evaluate its arguments.
  3. Check for termination. A terminating condition may be specified by an in, on, from, while, until, thereis, always, or never clause. Satisfaction of a condition will force control to step 4 (unless the condition was specified by always or never).
  4. If present, check when and unless clauses. If any condition is not satisfied then evaluate the body. The body is constructed from the clauses do, collect, adjoin, adjoinq, join, conc, union, unionq, intersection, intersectionq, count, sum, product, maximize, minimize, maximal, and minimal. Continue at step 3.
  5. If an finally clause is present, evaluate its arguments. Return the value of the last argument unless a returns or returning clause is present. Otherwise evaluate their arguments, return the value of the last.

(for* [S:form]): any macro
Identical to for except that variable bindings and updates are done sequentially instead of in parallel

7.3.2 Mapping Functions

The mapping functions long familiar to LISP programmers are present in PSL. However, we believe that the for construct described above or the simpler foreach described below is generally more useful, since it obviates the usual necessity of constructing a lambda expression, and is often more transparent. Mapping functions with more than two arguments are not currently supported. Note however that several lists may be iterated along with for, and with considerably more generality. For example:

(let ((i 0))  
  (mapcar l (function (lambda (x)  
                        (setq i (add1 i))  
                        (cons i x)))))

may be expressed more transparently as

(for (in x l) (from i 1) (collect (cons i x)))

To augment the simpler for loop present in basic PSL the following list iterator has been provided:

(foreach U:any): any macro
This macro is essentially equivalent to the the map functions as follows:

Possible forms are: Setting x to successive elements of u:

    (foreach x in u do (foo x)) --> (mapc u 'foo)  
    (foreach x in u collect (foo x))--> (mapcar u 'foo)  
    (foreach x in u conc (foo x))       --> (mapcan u 'foo)  
    (foreach x in u join (foo x))       --> (mapcan u 'foo)

Setting x to successive cdrs of u:

    (foreach x on u do (foo x)) --> (map u 'foo)  
    (foreach x on u collect (foo x))--> (maplist u 'foo)  
    (foreach x on u conc (foo x))       --> (mapcon u 'foo)  
    (foreach x on u join (foo x))       --> (mapcon u 'foo)

Within the context of for the JOIN is used to append successive values. However, inside foreach successive values are concatenated together.

    1 lisp> (setq x '(a b c) y (1 2 3))  
    (1 2 3)  
    2 lisp> (for (in u '(x y)) (join (eval u)))  
    (A B C 1 2 3)  
    3 lisp> x  
    (A B C)  
    4 lisp> (foreach u in '(x y) join (eval u))  
    (A B C 1 2 3)  
    5 lisp> x  
    (A B C 1 2 3)

(map X:list FN:function): nil expr
Applies FN to successive cdrs of X. The first value passed to FN is X, unless X is nil.
    1 lisp> (map '(one two) #'print)  
    (one two)  
    (two)  
    nil

(mapc X:list FN:function): nil expr
FN is applied to successive elements of list X, nil is returned.
    1 lisp> (mapc '(one two) #'print)  
    one  
    two  
    nil

(mapcar X:list FN:function): list expr
Similar to mapc except that the return value is a list of the results of each applicaton of FN.
    1 lisp> (map '((one) (two)) #'(lambda (i) i))  
    (one two)

(mapcan X:list FN:function): list expr
Similar to mapcar except that the values returned by FN are nconced together instead of being listed together. The argument X may be modified to construct the result.
    1 lisp> (setq this '((one) (two)))  
    ((one) (two))  
    2 lisp> (mapcan this #'(lambda (i) i))  
    (one two)  
    3 lisp> this  
    ((one two) (two))

(maplist X:list FN:function): list expr
Similar to map except that the return value is a list of the results of each application of FN.
    1 lisp> (maplist '(one two) #'(lambda (i) i))  
    ((one two) (two))

(mapcon X:list FN:function): list expr
Similar to maplist except that the values returned by FN are nconced together instead of being listed together.
    1 lisp> (setq this '(one two))  
    (one two)  
    2 lisp> (mapcon this #'(lambda (i) (copy i)))  
    (one two two)

Note that the nconc happens as the mapping process proceeds, not afterward. Therefore the result is not the same as nconcing the results of a maplist. Consider what would occur if

    (mapcon this #'(lambda (i) i))

had been used in the example above. Mapcon would apply its second argument to its first argument giving a partial result of (one two). Notice that the first argument to mapcon is eq to this partial result. Now mapcon applies its second argument to (two), the cdr of its first argument. The partial result becomes (one two two). However, the first argument to mapcon has been modified because nconc was used to build the partial result. The value of the first argument is now (one two two). The computation will never terminate, the length of the first argument to mapcon will continue to grow.

Do

(do A:list B:list [S:form]): any macro
The general form follows, the square brackets are used to indicate zero or more occurances of an expression.
    (DO ([<var-form>]) (<exit-test> [<result>]) [<body>])

A <var-form> is either an id or a list of the form

    (<var> <initial> <next>)

There are four basic steps in the evaluation of a do form.

  1. .....
  2. If <exit-test> evaluates to a non-nil value the <result> forms are evaluated and the do is exited, unbinding the local variables. The value of a do is the value of the last <result> unless there are no <result> forms, in which case nil is returned.
  3. The body of the do, consisting of the <body> forms, is evaluated in left to right order.
  4. The <next> forms are evaluated in parallel, the values are assigned to the corresponding <var>s rather than being bound. Once this is complete the process continues at step 2.

If <next> is omitted, the value of the corresponding <var> is left unchanged during step 4. If both <initial> and <next> are omitted or if the <var-form> is an id then the variable is bound to nil in step 1 and left unchanged during step 4.

The function definition below illustrates the use of do. This function will reverse the order of elements in a list.

    (de reverse (sequence)  
      (do ((local sequence (rest local))  
           (result nil (cons (first local) result)))  
          ((null local) result)))

(do* A:list B:list [C:form]): any macro
Do* is like do, except the variable bindings and updatings are done sequentially instead of in parallel. The following is a definition of assoc using do*.
    (de assoc (item a-list)  
      (do⋆ ((local a-list (rest local))  
            (pair (first local) (first local)))  
           ((or (null local)  
                (equal item (first pair)))  
            local)))

If do had been used, evaluation of the <initial> form (first local) would have resulted in an error. Either local would be unbound or the value of local from the surrounding environment would have been used.

(do-loop A:list B:list C:list [S:form]): any macro
Do-loop is like do, except that it takes an additional argument, a prologue. The general form follows, the square brackets are used to indicate zero or more occurances of an expression.
      (DO-LOOP ([<var-form>]) ([<first>])  
               (<exit-test>[<result>]) [<body>])

This is executed just like the corresponding do, except that after the initial bindings are established, but before the exit test is first evaluated, the prologue forms, consisting of the <first> forms, are evaluated in a left to right order. Note that all of the <first> forms are evaluated exactly once, assuming that none of the <first> forms causes an error.

(do-loop* A:list B:list C:list [S:form]): any macro
Do-loop* does the variable bindings and updates sequentially instead of in parallel.