mirror of
https://github.com/PDP-10/its.git
synced 2026-01-13 15:27:28 +00:00
276 lines
12 KiB
Plaintext
Executable File
276 lines
12 KiB
Plaintext
Executable File
;;;-*-Lisp-*-
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
;;; ITERATION FUNCTIONS ;;;
|
||
;;; Peter Szolovits (PSZ @ MIT-ML) ;;;
|
||
;;; July 16, 1976 ;;;
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
;;; revised by PSZ & RAMESH on Mar. 22, 1979 ;;;
|
||
;;; revised by LH@MIT-ML on May 9, 1979 ;;;
|
||
;;; revised again by BYRON@MIT-ML on July 12, 1979 ;;;
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
||
;;;This package defines a set of functions to provide an
|
||
;;;approximation of INTERLISP's iteration statement facility
|
||
;;;within a MACRO package for MACLISP.
|
||
;;;
|
||
;;;
|
||
;;;For the simplest exposition of its use and utility, here are
|
||
;;;a few examples of how the iterations statements may be used:
|
||
;;;
|
||
;;; (FOR I FROM 1 TO 3 COLLECT I) ==> (1 2 3)
|
||
;;;
|
||
;;; (COLLECT (CONS I X) FOR X IN '(A B C D E) AS I BY 3)
|
||
;;; ==> ((1 . A) (4 . B) (7 . C) (10 . D) (13 . E))
|
||
;;;
|
||
;;; (UNLESS (ATOM X) JOIN X FOR X IN '((A B C) (D E) F (G)))
|
||
;;; ==> (A B C D E G)
|
||
;;;
|
||
;;; (FOR X ON '(A B C D) AS I FROM 1 ADJOIN (PRINT I) X)
|
||
;;; 1
|
||
;;; 2
|
||
;;; 3
|
||
;;; 4
|
||
;;; ==> (A B C D B C D C D D)
|
||
;;;
|
||
;;; (FIRST (SETQ FOO '(A B (C D) E))
|
||
;;; WHILE (ATOM (CAR FOO)) DO (SETQ FOO (CDR FOO)) (PRINT FOO))
|
||
;;; (B (C D) E)
|
||
;;; ((C D) E)
|
||
;;; ==> NIL
|
||
;;;
|
||
;;; (BIND X (FOO '(A B (C D) E))
|
||
;;; WHILE (ATOM (SETQ X (CAR FOO)))
|
||
;;; COLLECT (SETQ FOO (CDR FOO)) (CONS X X))
|
||
;;; ==> ((A . A) (B . B))
|
||
;;;
|
||
;;; (FOR X IN '(A B C D) FIRST-TIME (MEMQ X '(E F G C 1 2 3)))
|
||
;;; ==> (C 1 2 3)
|
||
;;;
|
||
;;;FOR now supports LET-type "destructuring" wherever variables are
|
||
;;; explicitly bound (by BIND, FOR, or AS) so:
|
||
;;;
|
||
;;; (FOR (X Y) IN '((1 2) (3 4)) COLLECT (+ X Y) ==> (3 7)
|
||
;;;
|
||
;;; (BIND ((X Y) '(2 4)) FOR I FROM 1 TO 2 COLLECT (+ X Y I)) ==> (7 8)
|
||
;;;
|
||
;;*page
|
||
|
||
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
;;; GENERAL DESCRIPTION ;;;
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
||
;;;
|
||
;;; An ITERATION is a convenient manner of writing a complex
|
||
;;;LISP looping expression when control is desired of various
|
||
;;;aspects of the iteration which make the system-provided
|
||
;;;functions (e.g., MAPCAR, DO) too rigid or too cumbersome.
|
||
;;;
|
||
;;; An iteration statement consists of a number of clauses,
|
||
;;;described below, written in succession within a single S-EXPR.
|
||
;;;
|
||
;;; Every iteration has at most one MAIN CLAUSE, which
|
||
;;;controls what, if anything, is collected as the result of the
|
||
;;;iteration. The default provided main clauses are:
|
||
;;;
|
||
;;; DO (or DOING) -- evaluated for side-effect only; return is
|
||
;;; NIL.
|
||
;;; COLLECT -- A list of the values of every evaluation of the
|
||
;;; main clause is returned (c.f. MAPCAR).
|
||
;;; JOIN -- The values of every evaluation of the main clause
|
||
;;; are NCONCed together to form the value (c.f.
|
||
;;; MAPCAN).
|
||
;;; ADJOIN -- Like JOIN, but joining is by APPEND rather than
|
||
;;; NCONC. Every joined segment is copied exactly
|
||
;;; once, even if there is only one segment.
|
||
;;; COUNT -- The number of non-NIL values of the evaluations of
|
||
;;; the main clause is returned.
|
||
;;; SUM -- The sum of the values of the evaluations of the main
|
||
;;; clause is returned.
|
||
;;; FIRST-TIME -- The value of the iteration is the first
|
||
;;; non-NIL value of the main clause, and iteration
|
||
;;; terminates when and if this occurs.
|
||
;;; PRINT (or PRINTING) -- PRINT's the values of the evaluations
|
||
;;; of the main clause.
|
||
;;; RESULT -- Sets the RESULT variable to the value of the expression
|
||
;;; and exits.
|
||
;;;
|
||
;;;Other main clauses may be added. Each must be signalled by a
|
||
;;;keyword marked by the !FUNCTION property with an appropriate
|
||
;;;function to fill in the iteration template for it.
|
||
;;;
|
||
;;;
|
||
;;; The binding of LOOP VARIABLES and AUXILLIARY VARIABLES is
|
||
;;;controlled by the BIND, FOR and AS clauses. The BIND keyword is
|
||
;;;followed by the variables or (variable initial-val) or
|
||
;;;(variable-structure initial-val) <as in LET> forms to be
|
||
;;;bound. Those variables are bound, and the initial-vals are evaluated
|
||
;;;before any of the bindings for this iteration. The FOR and AS
|
||
;;;clauses are equivalent and provide a way to have several loop
|
||
;;;variables. The keyword is followed by the name of the variable, and
|
||
;;;optionally by FIXNUM, FLONUM, or NOTYPE. NOTYPE is the default
|
||
;;;except for numeric (FROM, TO, DOWNTO, BY) variables, for which it is
|
||
;;;FIXNUM. An appropriate declaration to the compiler is made. The rest
|
||
;;;of each variable clause has one of the following forms:
|
||
;;;
|
||
;;; FROM e1 TO e2 BY e3 -- This is the numeric iteration clause.
|
||
;;; Its terms may appear in any order. Instead of
|
||
;;; the TO, we may have a DOWNTO term to indicate
|
||
;;; that the loop is for decrementing the var. FROM
|
||
;;; defaults to 1, BY to 1 with TO and -1 with
|
||
;;; DOWNTO. Incrementing is assumed if neither is
|
||
;;; stated. (Currently, no checking is performed to
|
||
;;; see that the types of args are consistent, and
|
||
;;; the type of arithmetic used is determined by the
|
||
;;; type specified. NOTYPE implies general
|
||
;;; arithmetic, and the default is FIXNUM.)
|
||
;;; IN list -- This is iteration over a list. The var gets
|
||
;;; successive elements of the list.
|
||
;;; ON list -- This is iteration over successive tails of the
|
||
;;; list.
|
||
;;; STARTING e1 STEPPING e2 -- This is a general form for giving
|
||
;;; initial and incremental values. The terms may
|
||
;;; be in either order. STARTING defaults to NIL,
|
||
;;; and if STEPPING is omitted, no stepping action
|
||
;;; is set up.
|
||
;;; TRAILING v1, or TRAILS v1 -- The iteration variable will take on the
|
||
;;; value that v1 had on the previous iteration. V1 should
|
||
;;; be some other iteration variable of this iteration. On
|
||
;;; the first iteration, since there is no previous value of
|
||
;;; v1, we use NIL.
|
||
;;; SET-TO e1, or = e1 -- On each iteration, e1 is evaluated and
|
||
;;; assigned to the variable. This is most useful when e1
|
||
;;; is expressed in terms of some other iteration
|
||
;;; variable(s). E1 is always computed in terms of the new
|
||
;;; values of the iteration variables, not the old.
|
||
;;; BEING pathname OF e1, or
|
||
;;; BEING e1 AND ITS pathname -- These are the exclusive and
|
||
;;; inclusive forms of the PATH ITERATION.
|
||
;;; Pathnames must be explicitly marked by the
|
||
;;; !PATH-FUNCTION property with a function to
|
||
;;; process them. This is a special feature, most
|
||
;;; useful for LMS and OWL, and in this package
|
||
;;; there are no paths defined by default. The
|
||
;;; keyword ALONG is synonymous with BEING. Note
|
||
;;; that there are variants of these subclauses, not
|
||
;;; described here, that are specifically tailored
|
||
;;; for iterating through the objects in a zone of
|
||
;;; an LMS node; these variants are recognized by
|
||
;;; the fact that EACH occurs where the pathname or
|
||
;;; ITS would normally have occurred.
|
||
;;;
|
||
;;; The sub-keywords like FROM, IN, etc., are recognized by having an
|
||
;;;!ITER-FUNCTION property; thus, others may be added to the package.
|
||
;;;
|
||
;;; TERMINATION CLAUSES allow specification of additional
|
||
;;; iteration termination conditions beyond any that are
|
||
;;; implied by FOR and AS clauses. The following exist:
|
||
;;;
|
||
;;; WHILE e1 -- e1 is evaluated at the beginning of each
|
||
;;; iteration, and the iteration terminates when e1
|
||
;;; is NIL.
|
||
;;; UNTIL e1 -- like WHILE but terminates when e1 is non-NIL.
|
||
;;; REPEAT-WHILE e1 -- e1 is evaluated at the end of each
|
||
;;; iteration, and the iteration terminates when e1
|
||
;;; is NIL; this guarantees at least one
|
||
;;; iteration.
|
||
;;; REPEAT-UNTIL e1 -- like REPEAT-WHILE but terminates when e1 is
|
||
;;; non-NIL.
|
||
;;;
|
||
;;;
|
||
;;; A SELECTION-CLAUSE is a filter on which iteration the
|
||
;;;main clause should be evaluated. A conjunction is implied if more
|
||
;;; than one selection exists. The following exist:
|
||
;;; WHEN e1 -- The main clause is evaluated if e1 is non-NIL.
|
||
;;; UNLESS e1 -- The main clause is evaluated if e1 is NIL.
|
||
;;;
|
||
;;;
|
||
;;; The PERIPHERAL CLAUSES are of three kinds:
|
||
;;;
|
||
;;; FIRST e1 -- Evaluates e1 after initially binding the vars
|
||
;;; but before starting the first iteration.
|
||
;;; FINALLY e1 -- Evaluates e1 after exiting the last iteration
|
||
;;; but before returning the answer. If the main
|
||
;;; clause is a value-returning clause, the result
|
||
;;; to be returned is in the variable RESULT (see notes).
|
||
;;; EACH-TIME e1 -- Evaluates e1 on every iteration of the loop,
|
||
;;; whether or not the selection test is passed.
|
||
;;;
|
||
|
||
;;*page
|
||
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
;;; CAVEATS ;;;
|
||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
||
;;;
|
||
;;;
|
||
;;; A few notes should be made about implementation features
|
||
;;;which affect the evaluation of iteration forms:
|
||
;;;
|
||
;;; 1. Like in the LISP DO, initialization and updating of the
|
||
;;;iteration variables takes place in parallel. Only SET-TO varaibles
|
||
;;;and their internally generated equivalents (e.g., "X in Y") are done
|
||
;;;after the others, and these should never lead to trouble. This is a
|
||
;;;significant change from earlier versions of the FOR package.
|
||
;;;
|
||
;;; 2. The iteration statement is translated to a LISP DO. In
|
||
;;;particular, this means that a (RETURN val) form may be
|
||
;;;evaluated at any place to return that particular val as the
|
||
;;;value of the iteration. If it is desired that any FINALLY
|
||
;;;clauses be evaluated and the value returned, then the
|
||
;;; following may be done:
|
||
;;; (SETQ RESULT val), if appropriate and if the main clause
|
||
;;; is value-returning
|
||
;;; (TERMINATE-ITERATION)
|
||
;;;Note that the RESULT clause may be used in many cases where this is
|
||
;;;desired.
|
||
;;;
|
||
;;; 3. Wherever possible, the order of evaluation of
|
||
;;;expressions whose evaluation order is not otherwise
|
||
;;;constrained is that suggested by the order of writing them in
|
||
;;;the iteration statement. This is only of significance if
|
||
;;;significant use of side effects is made.
|
||
;;;
|
||
;;; 4. Wherever a single expression may appear, more than one
|
||
;;;may appear. They will be implicitly surrounded by a PROGN,
|
||
;;;so all but the last will be evaluated for side effect only.
|
||
;;;
|
||
;;; 5. The name RESULT, in which the value of value-collecting
|
||
;;;iterations is built up, is selected only by default. If the
|
||
;;;value of the variable !RESULT-NAME is bound, that will be
|
||
;;;used instead.
|
||
;;;
|
||
;;; 6. Almost no error checking is currently done, so it is
|
||
;;;possible to get weird errors if the iterative statement is
|
||
;;;not well-formed.
|
||
;;;
|
||
;;; 7. This code is written using (I think) only one macro,
|
||
;;;!PUSH, which is not part of the standard LISP complement.
|
||
;;;!PUSH is defined herein to be equivalent to PUSH with its
|
||
;;;arguments reversed, except that it only works for simple
|
||
;;;(atomic) variables.
|
||
;;;
|
||
;;; 8. For efficiency, translations produced by these macros
|
||
;;;are saved in the array !MACRO-EXPANSIONS and further calls on
|
||
;;;the same form are translated by retrieval rather than
|
||
;;;recomputation. This, however, may cause some problems: For
|
||
;;;efficiency considerations (i.e., SXHASH or EQUAL are slow),
|
||
;;;retrieval is done by EQ comparison on the form. Thus, if the
|
||
;;;form has been edited since its original translation, an
|
||
;;;incorrect translation will be retrieved. Further, since all
|
||
;;;translated forms are referred to from !MACRO-EXPANSIONS, many
|
||
;;;un-garbage-collectable obsolete copies of a form can be
|
||
;;;retained during debugging runs. (E.g., if one keeps
|
||
;;;redefining some function which includes macro calls. For
|
||
;;;anyone who thinks they can solve this problem by retrieval on
|
||
;;;the MAKNUM of the form or by making the array untraced by the
|
||
;;;GC, be warned that either "fix" causes mis-translations.) The
|
||
;;;function !MACRO-FLUSH is provided to flush all existing
|
||
;;;translations and guarantee that new translations of all these
|
||
;;;macro forms are made. This also releases for
|
||
;;;garbage-collection all the "old" forms which are only pointed
|
||
;;;at by this translation mechanism.
|