2.10 User extension
define-loop-macro
loop-macro-synonym
define-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) FORdefine-loop-method Macro> (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
method-name-or-names method-function list-of-allowable-prepositions&rest
method-specific-data
define-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.
define-loop-method
to tell the Loop Facility the name of your loop method and to specify its three parts.
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.
'(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))))
define-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.
define-loop-method
macro takes the following arguments:
being
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.
define-loop-method
.
for 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
define-loop-method
and you want the method to take different actions based on the name of the method function the user supplies.
define-loop-method
has the following syntax:
method-function
method-name iter-var iter-var-data-type prep-phrases inclusive? allowed-preps method-specific-data
define-loop-method
.
(for x being each star in the-sky
...)
, the iter-var isx
.
(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
.
define-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.
t
, 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
.
define-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)
&rest
method-specific-data argument todefine-loop-method
. The method function can inspect this list to determine its proper course of action.
(a b c d e f)
is equivalent to a return of(a b c d e f c d e f)
.
'(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)))
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
named
construct.
finally
construct.
sum
orcollect
.
initially
construct adds code to this part of the paradigm.
nil
. 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.
let
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.
initially
construct also adds forms to this element.
psetq
.
setq
.
'(progn (when '(or ,@pre-step-tests) (loop-finish)) (psetq ,@steppings) (when '(or ,@post-step-tests) (loop-finish)) (setq ,@post-step-settings))
progn
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
.
psetq
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.
;; 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 ...)
loop: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
defloop
, loop:named-variable
defloop
method-name-or-names access-function size-function&optional
sequence-type element-type
defloop
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.
defloop
macro takes the following arguments:
being
iteration clause.
;; 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)
define-loop-method
get-loop-method
name
get-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)))
define-loop-method
loop:named-variable
name
loop:named-variable
finds a variable specified in ausing
construct, removes it from the data base, and returns it.
using
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"
Generated with Harlequin WebMaker