2.10 User extension

2.10.3 Reference pages

define-loop-macro Macro

Syntax:define-loop-macro loop-macro-synonym

The macrodefine-loop-macro allows you to use any loop iteration control keyword or loop method name to initiate loop processing.

;; Define the FOR macro to replace LOOP FOR in a construct.
> (define-loop-macro for)
FOR

> (for x in '(1 2 3 4) by #'cddr collect x) (1 3)

;; The REPEAT macro replaces the LOOP REPEAT construct. > (define-loop-macro repeat) REPEAT

> (repeat 3 do (print "What I say three times is true.")) "What I say three times is true." "What I say three times is true." "What I say three times is true." NIL

;; Define WITH as a loop synonym. > (define-loop-macro with) WITH

;; The first test occurs after the summation clause. > (with i = -3 sum i while (< (incf i) 0)) -6

;; Now the test occurs before the summation clause. > (with i = -3 while (< (incf i) 0) sum i) -3

;; Shadow the DO inherited from the LISP package, and define DO as ;; a loop synonym. > (shadow 'do) T

> (define-loop-macro do) DO

> (do (print 5) (return 6)) 5 6 > (lisp:do () (t (print 5) 6) (print "Won't see this")) 5 6

define-loop-method Macro

Syntax:define-loop-method method-name-or-names method-function list-of-allowable-prepositions&rest method-specific-data

The macrodefine-loop-method allows you to define a new form of iteration for a data structure that cannot be accessed with an integer index, such as a hash table, the symbols in a package, or the successive outputs of a generator function. For such a structure, you must define a loop method function that takes specified arguments and that returns a list of the parts of a general loop program; that is, the function generates Lisp code fragments.

A loop method logically consists of three parts:

the loop method function

a list of allowable prepositions

a list of method-specific data, or an empty list if no such data exist

You usedefine-loop-method to tell the Loop Facility the name of your loop method and to specify its three parts.

All loop keywords are identified by their print name; however,define-loop-method ensures that there is an external symbol in theloop package of the same name as the method. It also checks that there are external symbols of the same name as any prepositions. When an instance of the user-defined method is parsed, the method name that is passed as an argument to the method-specific function, as well as any prepositions, will be symbols present in theloop package.

A simplified, general version of a loop program is shown here:

'(let (init-bindings) 
   (block nil 
     (tagbody    prologue-code 
      begin-loop main-body-code 
                 variable-stepping-code 
                 (when end-test-code (go end-loop)) 
                 (go begin-loop) 
      end-loop   epilogue-code 
                 (return return-value)))) 

The relationships among the several parts of code that the method function can produce are discussed in the reference pages.

Note: The use ofdefine-loop-method is an advanced topic, and the discussion that follows reflects the complexity of the construct. Casual users of the Loop Facility should not needdefine-loop-method; most iteration needs can be satisfied by the supplied loop constructs.

Thedefine-loop-method macro takes the following arguments:

method-name-or-names

This argument is a name or a list of names that can be used after the loop keywordbeing to specify the iteration method. A name can be either a symbol or a string; a symbol of that name will be interned in theloop package as an external symbol. It does not matter whether the symbol previously existed in theloop package.

method-function

This argument is a user-defined loop method function that will guide iteration. The function takes specific arguments and returns specific values. A discussion of the loop method function follows the description of the arguments fordefine-loop-method.

list-of-allowable-prepositions

This argument is a list of names that the Loop Facility uses to determine how the source code that follows the name is parsed. For example, prepositions in the expressionfor x being the strangenesses of object{preposition value}* are parsed and collected until no more matches with the names in the list-of-allowable-prepositions exist. You can specify any of the prepositionsof,in,from,downfrom,to,downto,below,above, andby, or you can name new prepositions. A preposition name can be either a string or a symbol, since the Loop Facility identifies loop keywords, loop methods, and loop prepositions by their print name rather than by symbol identity.

&rest method-specific-data

These arguments are optional parameters that are passed to the loop method as a list and can be used by the loop method for any purpose. For example, you might use a symbol flag in the first method-specific-data position for the loop method function that handles iteration over symbols in packages; the symbol flag could differentiate between locally and externally accessible symbols. Use a list of method-specific-data arguments if you have declared several names indefine-loop-method and you want the method to take different actions based on the name of the method function the user supplies.

The loop method function that is specified as the method-function argument todefine-loop-method has the following syntax:

method-function method-name iter-var iter-var-data-type prep-phrases inclusive? allowed-preps method-specific-data

The loop method function takes the following arguments:

method-name

This argument is the method name that invokes the method function being defined. It must be one of the names that you declare in the method-name-or-names argument ofdefine-loop-method.

iter-var

This argument is bound to the symbol variable (or destructuring list) occurring in the source code that is being expanded; the symbol or list is used as the iteration variable. For example, in the code fragment(for x being each star in the-sky...), the iter-var isx.

iter-var-data-type

This argument is bound to any type specification that immediately succeeds the iteration variable in the source code. For example, in the fragment(for (twinkle . amount) (nil . integer) being the winking of stars...), the iter-var argument is bound to(twinkle . amount), and iter-var-data-type argument is bound to(nil . integer). If no data type is specified, the default type isnil.

prep-phrases

This argument is a list of lists, of the form (prep expr), which contains the parsed prepositions and corresponding attributes from the source code. The prep element is a member of the list specified in the list-of-allowable-prepositions argument todefine-loop-method. For example, suppose that the list-of-allowable-prepositions for thestar loop method is(of in on around). For the expression(for x being each star of constellations in the-sky do...), the method function receives the prep-phrases argument((of constellations) (in the-sky)). The prepositions in this argument are listed in the order in which they appear in the source code. The Loop Facility assures that the preposition names are external symbols of theloop package; the symbol names are the same names that are used in the source code.

inclusive?

Ift, this flag indicates that the user has requested inclusive iteration. In inclusive iteration, the variable is bound in the first iteration to the value of the expression following the loop keywordbeing.

allowed-preps

This argument is a list of the allowed prepositions for the loop method, as specified in the argument list-of-allowable-prepositions fordefine-loop-method. Although these prepositions can be in any package, the loop method function receives a list of symbols that are interned in theloop package in the following manner:

(mapcar #'(lambda (x)
            (intern (symbol-name x)
                    (find-package "LOOP")))
        list-of-allowable-prepositions)

method-specific-data

This argument is the list specified as the&rest method-specific-data argument todefine-loop-method. The method function can inspect this list to determine its proper course of action.

The method function must return a list of either six or ten elements. Each element is a component of code in a general loop paradigm. The six-element case is an abbreviation of the ten-element case wherein the last four elements are merely replicated; thus, a return of(a b c d e f) is equivalent to a return of(a b c d e f c d e f).

The loop paradigm for the more general ten-element case can be described as follows:

'(let (init-bindings)                      ; first return value 
   (block ,(or block-name nil) 
     (tagbody ,@prologue-code            ; second return value 
              ,@prologue-test/step-code      ; values 7, 8, 9, & 10 
      begin-loop ,@main-body-code 
                 ,@test/step-code           ; values 3, 4, 5, & 6 
                 (go begin-loop) 
      end-loop ,@epilogue-code 
               ,@return-value-code))) 

The following ten values are returned by a general loop method function:

init-bindings
prologue-forms
pre-step-tests
steppings
post-step-tests
post-step-settings
prologue-pre-step-tests
prologue-steppings
prologue-post-step-tests
prologue-post-step-settings 

The parts of the general loop method paradigm can be divided into two groups: those over which the method function has no control, and those that are supplied by the method function.

The parts over which the method function has no control are obtained as follows:

block-name

If a block name exists, it is supplied by thenamed construct.

main-body-code

This code is supplied when the Loop Facility parses the rest of the source expression.

epilogue-code

This code is supplied by thefinally construct.

return-value-code

This code is supplied by collector constructs, such assum orcollect.

prologue-code

Aninitially construct adds code to this part of the paradigm.

The following elements are under method function control:

init-bindings

This element is a list of lists of pairs or triplets of the form (var value-form) or (var value-form type-spec); in a list that is simply (var), the value-form defaults tonil. In each case, the actual code in thelet form binds var to the evaluation of the corresponding value-form. If a type-spec is supplied, var is declared to be of that type. If the value-form is null, rather than(quote nil), an initial value is chosen according to any type-spec that is given. The default value for afixnum is 0, for afloat is 0.0, and for other types isnil. See Section 2.8.1 on page 38 for more information.

Since these bindings are made by alet construct, they occur in parallel; thus, there can be no dependencies among the bindings. Required dependencies can be specified by using the Common Lisp special formsetq in the prologue-code element.

prologue-code

This element is a list of forms that are evaluated before the first iteration inside the loop prologue. Theinitially construct also adds forms to this element.

test/step-code, prologue-test/step-code

The loop method function uses two blocks of code for testing and stepping. These blocks can express loops in which the end test for the very first iteration is different from the end test for other iterations; that is, the condition tested for by the code in prologue-test/step-code can differ from the condition tested for by the code in test/step-code. In practice, this often is not the case. More often the user desires the end tests to be performed before entering the main loop body, so that zero times around the loop is permissible; in this case, the user supplies the same end test for both the loop prologue and the main body.

In addition to establishing variable bindings, the user's main control over the structure of the loop is through the following subparts of test/step-code and of prologue-test/step-code:

pre-step-tests

This step tests for an end condition before any variables are stepped.

steppings

This step assigns values to variables by using the Common Lisp macropsetq.

post-step-tests

This step tests for an end condition after the main variable stepping is complete.

post-step-settings

This step assigns values to any additional variables by usingsetq.

The test/step-code fragment is equivalent to the following code:

'(progn (when '(or ,@pre-step-tests) 
          (loop-finish)) 
        (psetq ,@steppings) 
        (when '(or ,@post-step-tests) 
          (loop-finish)) 
        (setq ,@post-step-settings)) 

This code sample shows that some of the code fragments returned by the loop method function are lists of items that are inserted into aprogn in the appropriate place. Thus, pre-step-tests and post-step-tests are each a list of forms, each form of which must evaluate to false for the loop to continue. The steppings and post-step-settings are each lists of pairs that match variables to values as part of asetq.

Note: Actualpsetq andsetq special forms are not used; internal versions of these constructs that permit destructuring are used instead. See Section 2.8.2 on page 39 for more information.

Since the six-element return value format expands into the ten-element format by merely duplicating the last four parts of test/step-code, the loop is treated as if the end test were at the beginning by default. The Loop Facility inspects the resultant code for commonality and, where possible, makes an optimization that produces only one copy of the end tests and steppings, as the following example demonstrates:

;; Suppose you have the following code. 
     '(tagbody ,@prologue-code 
        (progn (when some-condition (go end-loop))
               (psetq,@steppings)) 
        begin-loop ,@main-body-code 
                   (progn (when some-condition (go end-loop)) 
                          (psetq ,@steppings)) 
                   ,@main-body-code
                   (go begin-loop) 
        end-loop ...) 

;; The Loop Facility translates this code into an optimized ;; version. '(tagbody ,@prologue-code begin-loop (progn (when some-condition (go end-loop)) (psetq ,@steppings)) ,@main-body-code (go begin-loop) end-loop ...)

The loop method function that follows is used in the example forloop:named-variable.

;; This example defines an iteration path called LIST-ELEMENTS
;; that iterates over the elements of a list by using a defined
;; loop method.
> (define-loop-method (list-element list-elements)
    list-elements-method (of using))
T

See Also: defloop, loop:named-variable

defloop Macro

Syntax:defloop method-name-or-names access-function size-function&optional sequence-type element-type

The macrodefloop allows you to define an additional form of iteration for a data structure than can be accessed with an integer index, ranging from 0 to the size of the structure.

Thedefloop macro takes the following arguments:

method-name-or-names

This argument is a name or a list of names to be used as new loop keywords in thebeing iteration clause.

access-function

This argument specifies the accessing function for iteration. The function must take two arguments: the data structure itself and the index of the item in data structure that should be returned.

size-function

This argument is a function that returns the size of the data structure; it takes the data structure as an argument.

sequence-type

This argument specifies the data type of the data structure.

element-type

This argument specifies the data type of the elements in the data structure.

This type of iteration is similar to iterating over a sequence; see Section 2.9 on page 43 for more information about iterating over sequences.

;; Define a loop method that iterates over simple strings.
> (defloop (schar schars) schar length simple-string string-char)
T

;; Then the following two examples are equivalent. > (loop for c being each schar of "Temp String" count c) 11

> (loop for c being each element of (the simple-string "Temp String") count c) 11

;; Collect nonspace characters in a subsequence of a string. > (loop for c being each schar of "Temporary String" from 3 upto 11 unless (eql c #\space) collect c) (#\p #\o #\r #\a #\r #\y #\S #\t)

See Also: define-loop-method

get-loop-method Function

Syntax:get-loop-method name

The functionget-loop-method returns the loop method function, the list of allowable prepositions, and the method-specific data for the user-defined loop method of the specified name; if no such loop method is defined, it returnsnil.

> (get-loop-method 'EXTERNAL-SYMBOLS)
(LOOP::SYMBOLS-ITERATION-METHOD (LOOP:IN LOOP:OF LOOP:EACH) 
((QUOTE DO-EXTERNAL-SYMBOLS)))

See Also: define-loop-method

loop:named-variable Function

Syntax:loop:named-variable name

The functionloop:named-variable finds a variable specified in ausing construct, removes it from the data base, and returns it.

If the name argument is a symbol that matches one of the symbols specified in ausing construct, the variable specified in theusing construct is returned. In the default case, the variable that is returned is the same as a Common Lispgensym value.

;;; The function LIST-ELEMENTS-METHOD is a user-defined loop
;;; method that supports iteration over the elements of a list. 
;;; The function uses the local variable LIST-VAR to hold the
;;; successive tails of the list.  In the first iteration, 
;;; LIST-VAR is the entire list.  The user can access
;;; LIST-VAR with the USING construct.  The current element in the 
;;; iteration is always the car of the list LIST-VAR.

> (defun list-elements-method (name iter-var iv-data-type prep-phrases inclusivep allowed-preps data) (declare (ignore name allowed-preps data)) ;; The user must supply a preposition to describe what to do. (unless prep-phrases (error "Must have OF clause after LIST-ELEMENTS")) ;; Check for extra unknown prepositions. (unless (= (length prep-phrases) 1) (error "Must have exactly one preposition after LIST-ELEMENTS, not ~S" prep-phrases)) ;; Since inclusive iteration is not supported, check and ;; signal an error if necessary. (when inclusivep (error "LIST-ELEMENTS path does not support inclusive iteration"))

;; Obtain either a new variable (GENSYM) or a variable ;; specified by the user in which to hold the successive tails ;; of the list. ;; The variable bindings are as follows: ;; LIST-VAR is the entire given list. ;; ITER-VAR is NIL, of type user gives. It is initialized ;; in the first iteration section. (let ((list-var (loop:named-variable 'list))) (list '((,list-var ,(second (first prep-phrases))) (,iter-var nil ,@(and iv-data-type (list iv-data-type)))) nil ; prolog: none. nil ; pre-step-endtest: none. ;; steppings: LIST-VAR becomes its cdr. '(,list-var (cdr ,list-var)) ;; post-step-endtest: no more elements? '(null ,list-var) ;; post-step-settings: set ITER-VAR to car. '(,iter-var (car ,list-var)) ;; This prologue-test/step-code section prevents LIST-VAR ;; from being advanced too soon. '(null ,list-var) ; prologue-pre-step-endtest: exit first ; if there is nothing to iterate. nil ; prologue-steppings: none. nil ; prologue-post-step-endtest: none. ;; prologue-post-step-settings: set iter-var to car. '(,iter-var (car ,list-var)))))

;;; Now use it. > (loop for elem being the list-elements of '(a b c) do (print elem) finally (return "All done")) A B C "All done"

> (loop for elem being the list-elements of '(a b c) using (list my-tail) do (format t "~&~S: ~S" my-tail elem) finally (return "All done")) (A B C): A (B C): B (C): C "All done"


The Loop Facility - 9 SEP 1996

Generated with Harlequin WebMaker