iterate - Psuedocodic Iteration
Version: 1.5
Licence: MIT
Repository: iterate/iterate - Gitlab
See also: awesome-cl#iteration
In case of any inaccuracies, ambiguities or suggestions, please create an issue here.
iterate is a powerful iteration facility for Common Lisp, and a alternative to loop. As opposed to loop, iterate is more lispy, extensible, besides being more powerful.
See The Iterate Manual for a more detailed explanation of the various facilities.
For a tutorial on looping in Common Lisp, check out Loops, Iterate and Mapping - The Common Lisp Cookbook.
HIGHLIGHTS
- def*: extensibility
- display-iterate-clauses: quick help for clauses
- dsetq: for use outside the
iterform - finally-protected: for protecting against unwindings
- finding-maximizing:
loopequivalent can be quite verbose - for: lots of variants!
- generate
- in:
loopequivalent can be quite verbose - there's no support for "true" parallel assignment - see with
OTHER POINTS
iteratedoes not declare variables unless asked to. See Types and Declarations.intovarstores the value intovarinstead of returning it.- There, probably, are some idiosyncrasies involved with running the epilogue code - it is not run "always":
always,finding,finish,thereis.
Notes for myself - indications of incomplete documentation
- Documentation for
findingis incomplete here. - Documentation for
def*,forhas been omitted here. - When are the variable values for prologue defined?
- Will there be future versions of iterate? What is the compatibility relation between
terminateandfinishforfor-nextGeneralized Drivers?
Installation
If quicklisp is set-up, simply (ql:quickload 'iterate).
Otherwise head over here.
Accumulation vs Reduction
The differences between accumulate and reducing are slight. One difference is that the functions take their arguments in a different order. Another is that in the absence of init-val, accumulate will use nil, whereas reducing will generate different code that avoids any dependence on the initial value. The reason for having both clauses is that one usually thinks of reductions (like sum) and accumulations (like collect) as different beasts.
API (CLAUSE) REFERENCE
accumulate
accumulateexprbyfunc&optional initial-valueinit-valintovar
This is a general-purpose accumulation clause. func should be a function of two arguments, the value of expr and the value accumulated so far in the iteration, and it should return the updated value. If no initial value is supplied, nil is used.
CL-USER> (iter (for i in '(1 2 3))
(accumulate i by
(lambda (i values-so-far)
(cons i values-so-far))))
(3 2 1)
CL-USER> (iter (for i in '(1 2 3))
(accumulate i by
(lambda (i values-so-far)
(cons i values-so-far))
initial-value '(init)))
(3 2 1 INIT)
CL-USER> (iter (for i in '(1 2 3))
(accumulate i by
(lambda (i values-so-far)
(cons i values-so-far))
initial-value '(init) into var))
NIL
See Accumulations, Accumulation vs Reduction and reducing.
accumulating
An alias for accumulate.
adjoining
adjoiningexptr&optional intovartesttestatplaceresult-typetype
Like collect, but only adds the value of exptr if it is not already present. test, which defaults to #'eql, is the test to be used with member.
after-each
after-each &restforms
Executes forms at the end of the loop body, after each iteration. Forms may contain iterate clauses.
CL-USER> (iter (for i below 4)
(after-each (print var))
(if (oddp i)
(collect i into var)
(collect (* 2 i) into var)))
; (0)
; (0 1)
; (0 1 4)
; (0 1 4 3)
NIL
See Code Placement and Problems with Code Movement.
always
alwaysexpr
CL-USER> (iter (for i below 4) (always (evenp i)))
NIL
- If expr ever evaluates to nil, then nil is immediately returned; the epilogue code is not executed.
- If expr never evaluates to nil, the epilogue code is executed and the last value of expr (or t if expr was never evaluated) is returned (whereas loop would constantly return t).
appending
appendingexptr&optional intovaratplace
Like collect, but behaves like the Common Lisp append, and works only on lists.
CL-USER> (iter (for i in '((1) (2 3) (4 5 6)))
(appending i))
(1 2 3 4 5 6)
CL-USER> (iter (for i in '((1) (2 3) (4 5 6)))
(appending i into var))
NIL
See Accumulations.
as
An alias for for.
collect
collectexptr&optional intovartesttestatplaceresult-typetype
CL-USER> (iter (for i from 1 to 5)
(collect i))
(1 2 3 4 5)
CL-USER> (iter (for i from 1 to 5)
(collect i at start)) ;; likely to be faster
(5 4 3 2 1)
placecan be eitherbeginning/startorend: default value isend.typeshould be a subtype ofsequence- default islist; however, the type of sequence being constructed inside the loop is undefined when a non-list type is specified.
CL-USER> (iter (for i from 1 to 3)
(collect i into vec result-type 'vector)
(print vec)
(finally (return vec)))
; (1)
; (1 2)
; (1 2 3)
#(1 2 3)
typeorplacemay be optionally quoted.
See Accumulations.
collecting
Alias for collect.
count
Alias for counting.
This, probably, overrides the CL count when used in top-level inside an iterate loop.
CL-USER> (iter (for i in '(1 2 3))
(finally (return (count 1 '(1 2 1)))))
2
counting
countingexpr&optional intovar
See Reductions and accumulate.
declare-variables
(declare (declare-variables))
- iterate does not declare variable types unless asked to.
- Declaration of types of user introduced symbols can be done by either the usual Common Lisp
declare, but this declaration should be inside the iter form. - Declaration of internal variables or use of
therequires one to usedeclare-variables, or setiterate:::*always-declare-variables* tot`.
CL-USER> (macroexpand-1 '(iter (for (the fixnum el) in '(1 2 3))
(declare (DECLARE-VARIABLES))
(count (oddp el))))
;; note that this produces a type declaration for el.
CL-USER> (macroexpand-1 '(iter (for el in '(1 2 3))
(declare (DECLARE-VARIABLES))
(count (oddp el))))
;; this does not produce a type declaration for el.
CL-USER> (macroexpand-1 '(iter (for (the fixnum el) in '(1 2 3))
(count (oddp el))))
;; this does not produce any declarations.
defclause-sequence
[Undocumented here.]
See Extensibility Aids.
defmacro-clause
[Undocumented here]
See Rolling Your Own.
defmacro-driver
[Undocumented here.]
See Writing Drivers.
defsynonym
[Undocumented here.]
See Extensibility Aids.
display-iterate-clauses
display-iterate-clauses &optionalclause-spec
CL-USER> (display-iterate-clauses 'repeat)
; REPEAT Repeat the loop some number of times
T
CL-USER> (display-iterate-clauses '(for in-vector))
; FOR IN-VECTOR &OPTIONAL FROM UPFROM DOWNFROM TO DOWNTO ABOVE BELOW BY
; WITH-INDEX Elements of a vector
T
See On-line help.
dsetq
dsetqtemplate expr
Can be used outside iter.
CL-USER> (foo)
FIRST
SECOND
CL-USER> (progn
(dsetq (values a b) (foo))
(list a b)) ;; undeclared variables warning
(FIRST SECOND)
See Destructuring.
else
else &restforms
Forms are executed if loop is never entered, but is terminated normally.
CL-USER> (iter (for i in '(1 2 3)) (while nil)
(else (write 'else)))
; ELSE
NIL
See Code Placement and Problems with Code Movement.
finally
finally &restforms
Forms are executed after a normal termination of the loop.
CL-USER> (iter (for i in '(1 2 3)) (finally (write 'end)))
; END
NIL
See Code Placement and Problems with Code Movement.
finally-protected
finally-protected &restforms
Forms are executed "always" - regardless of whether the termination was notmal
CL-USER> (iter (for i in-vector '(1 2 3))
(finally-protected (write 'error)))
;; warnings
ERROR ; Evaluation aborted on #<SIMPLE-TYPE-ERROR expected-type: VECTOR datum: (1 2 3)>.
CL-USER> (iter (for i in '(1 2 3))
(finally-protected (write 'no-error)))
; NO-ERROR
NIL
See Code Placement and Problems with Code Movement.
finding
findingexprsuch-thattest&optionally intovaron-failurefailure-value
- The loop terminates (with epilogue code) whenever
testevaluates to non-nil. exprthat satifies thetest, orfailure-value, ornilis returned (unless modified by epilogue).failure-valueis always evaluated.
CL-USER> (iter (for x in '(1 2 3))
(finding x such-that #'evenp on-failure 'not-found))
2
CL-USER> (iter (for x in '(1 2 3))
(finding x such-that #'evenp on-failure (error "not found")))
; Evaluation aborted on #<SIMPLE-ERROR "not found" {1002F63063}>.
CL-USER> (iter (for x in '(1 2 3))
(if (evenp x) (leave x))
(finally (error "not found")))
2
findingexprmaximizingm-expr&optionally intovar
findingexprminimizingm-expr&optionally intovar
- Returns
exprcorresponding to the maximum value ofm-expr. - If
m-expris never evaluated (how?), the return value isnilor0depending on the type (or its absence) ofexpr(orvarif supplied.) - Here,
m-exprcan also be a list of two symbols.
CL-USER> CL-USER> (iter (for list in '((1) (2 3) nil))
(finding list maximizing (length list)))
(2 3)
CL-USER> (iter (for i in '(1 2 3))
(finding (* 2 i) maximizing (- i) into (twice neg))
(finally (return (values twice neg))))
2
-1
[Example required for the case when m-expr is not evaluated.]
See Finders.
finish
finish
Stop the loop (and run the epilogue code).
CL-USER> (iter (for i in '(1 2 3)) (if (evenp i) (finish)))
NIL
See Control Flow.
first-iteration-p
first-iteration-p
t in the first cycle of the loop, otherwise nil.
CL-USER> (iter (for el in '(nil 1 2 nil 3))
(when el
(unless (first-iteration-p)
(princ ", "))
(princ el)))
; , 1, 2, 3
NIL
See Boolean Tests.
first-time-p
first-iteration-p
t only when the expression is evaluated for the first time.
CL-USER> (iter (for el in '(nil 1 2 nil 3))
(when el
(unless (first-time-p)
(princ ", "))
(princ el)))
; 1, 2, 3
NIL
See Boolean Tests.
for
[Undocumented here.]
See
- Numeric Iteration
- Sequence Iteration
- Variable Binding and Setting
- Generalized Drivers
- Previous Values of Driver Variables
generate
See Generators and for.
CL-USER> (iter (for el in '(a b nil c))
(generate i upfrom 1)
(if el (collect (cons el (next i)))))
((A . 1) (B . 2) (C . 3))
for can be replaced by generate to achieve the desired result, except in the case of Variable Binding and Setting.
generating
Alias for generate
if-first-time
if-first-timethen&optionalelse
CL-USER> (iter (for i in '(1 2 3))
(if-first-time
(princ 'first)
(print 'not-first)))
; FIRST
; NOT-FIRST
; NOT-FIRST
NIL
See Control Flow.
in
inname&restforms
CL-USER> (defvar ar #2A((1 2 3) (4 5 6)))
AR
CL-USER> (iter outer
(for i below (array-dimension ar 0))
(iter (for j below (array-dimension ar 1))
(in outer
(collect (aref ar i j)))))
(1 2 3 4 5 6)
See Named Blocks.
initially
in &restforms
Place the forms in the prologue of the loop.
CL-USER> (iter (initially (princ 'hi))
(for i below 3)
(print i))
; HI
; 0
; 1
; 2
NIL
CL-USER> (iter (for i below 3)
(initially (princ i)))
; -1 ;; this is probably an undefined behaviour.
NIL
See Code Placement and Problems with Code Movement.
leave
leave &optionalvalue
Returns from the current iterate form with value or nil.
CL-USER> (iter (for i below 3)
(leave
(iter (for j below 2)
(if (oddp j) (leave j)))))
1
See Control Flow.
maximize
maximizeexpr&optional intovar
CL-USER> (iter (for list in '((1) (1 2) nil))
(maximize (length list)))
2
See Reductions and finding.
maximizing
Alias for maximize.
minimize
minimizeexpr&optional intovar
CL-USER> (iter (for list in '((1) (1 2) nil))
(minimize (length list)))
0
See Reductions and finding.
minimizing
Alias for minimize.
multiply
multiplyexpr&optional intovar
CL-USER> (iter (for i from 1 to 5)
(multiply i))
120
Initial value of *var`* is 1.
See Reductions.
multiplying
Alias for [multiply](#
nconcing
nconcingexptr&optional intovartesttestatplaceresult-typetype
See Accumulations and collect.
never
neverexpr
Effectively (always (not expr)), but does not influence the last value returned by a possible other always clause.
CL-USER> (iter (repeat 2)
(always 2)
(never nil))
2
next
See generate.
next-iteration
next-iteration
CL-USER> (iter (for i below 3)
(if (oddp i)
(next-iteration)
(collect i)))
(0 2)
nunioning
nunioningexptr&optional intovartesttestatplaceresult-typetype
See Accumulations and collect.
reducing
reducingexprbyfunc&optional initial-valueinit-valintovar
CL-USER> (iter (for i in '(1 2 3))
(reducing i by
(lambda (value-so-far i)
(cons i value-so-far))
initial-value ()))
(3 2 1)
See Reductions, Accumulation vs Reduction and accumulate.
repeat
repeatn
Repeat the loop n times.
See Drivers.
CL-USER> (iter (repeat 3) (print 'doing))
; DOING
; DOING
; DOING
NIL
sum
sumexpr&optional intovar
CL-USER> (iter (for i from 1 to 5)
(sum i))
15
summing
Alias for sum.
terminate
`terminate
Use to terminate for-next clause. Effectively an alias for finish - but use with for-next to maintain compatibility with future versions of iterate(!).
CL-USER> (iter (for i upfrom 0)
(if (> i 5) (terminate) (collect i)))
(0 1 2 3 4 5)
CL-USER> (iter (initially (setq i 0))
(for i next
(if (> i 10)
(terminate)
(incf i))))
NIL
See Generalized Drivers.
thereis
thereisexpr
- If
expris ever non-nil, its value is returned without running the epilogue code. - Otherwise epilogue code is run, and
nilis returned. - Cannot be used with
alwaysornever.
unioning
unioningexptr&optional intovartesttestatplaceresult-typetype
See Accumulations and collect.
until
untilexpr
CL-USER> (iter (for i in '(1 2 3 4 5))
(until (> i 5))
(collect i))
(1 2 3 4 5)
Equivalent to (if expr (finish)).
See finish and Control Flow.
while
repeatn
untilexpr
Equivalent to (if (not expr) (finish)).
CL-USER> (iter (for i below 10)
(while (= 0 (rem i 5)))
(collect i))
(0)
See finish and Control Flow.
with
withvar&optional =value
var is bound to value before the loop is entered. Binding happens sequentially, as while using a let*, and not in parallel as with let.
CL-USER> (iter (with i = 0)
(while (< i 3))
(collect (incf i)))
(1 2 3)
CL-USER> (iter (with i = 1)
(for i below 3)
(collect (incf i)))
; Evaluation aborted on #<SIMPLE-ERROR "Iterate~@[, in ~a~]:~%Duplicate variable: ~a" {1004185183}>.
See Variable Binding and Setting and Parallel Binding and Stepping.
OTHER RESOURCES ON ITERATE
[DLI]: appendix/Don't Loop, Iterate