diff --git a/README.org b/README.org index 3046c03c..15536d3b 100644 --- a/README.org +++ b/README.org @@ -95,28 +95,13 @@ Unlike ~cl-loop~, ~loopy~ uses parenthetical expressions instead of "clauses". (collect evens i) (collect odds i)) (finally-return odds evens)) - - (loopy (numbers i :from 1 :to 10) - (if (cl-evenp i) - (collect i :into evens) - (collect i :into odds)) - (finally-return odds evens)) #+end_src ~loopy~ supports destructuring for iteration commands like =list= and accumulation commands like =sum= or =collect=. #+begin_src emacs-lisp - ;; Summing the nth elements of arrays: - ;; => (8 10 12 14 16 18) - (loopy (list (list-elem1 list-elem2) - '(([1 2 3] [4 5 6]) - ([7 8 9] [10 11 12]))) - (sum [sum1 sum2 sum3] list-elem1) - (sum [sum4 sum5 sum6] list-elem2) - (finally-return sum1 sum2 sum3 sum4 sum5 sum6)) - - ;; Or, more simply: + ;; Summing the nth elements of sub-arrays: ;; => (8 10 12 14 16 18) (loopy (list list-elem '(([1 2 3] [4 5 6]) ([7 8 9] [10 11 12]))) @@ -162,7 +147,7 @@ automatically ~let~-bound so as to not affect code outside of the loop. ~loopy~ has arguments for binding (or not binding) variables, executing code before or after the loop, executing code only if the loop completes, and for -setting the macro's return value (default ~nil~). This is in addition to the +setting the macro's return value (default: ~nil~). This is in addition to the looping features themselves. All of this makes ~loopy~ a useful and convenient choice for looping and @@ -193,28 +178,9 @@ Common Lisp. Generally, all of the packages handle basic use cases in similar ways. One large difference is that ~iterate~ can embed its looping constructs in arbitrary -code. Loopy is currently provides this feature as a separate macro, -~loopy-iter~, which expands looping constructs using ~macroexpand~. - -#+begin_src emacs-lisp - (require 'loopy-iter) - - ;; Things to node: - ;; - `accum-opt' produces more efficient accumulations for names variables - ;; - `cycling' is another name for `repeat' - ;; => ((-9 -8 -7 -6 -5 -4 -3 -2 -1) - ;; (0) - ;; (1 2 3 4 5 6 7 8 9 10 11)) - (loopy-iter (accum-opt positives negatives zeroes) - (numbering i :from -10 :to 10) - ;; Normal `let' and `pcase', not Loopy constructs: - (let ((var (1+ i))) - (pcase var - ((pred cl-plusp) (collecting positives var)) - ((pred cl-minusp) (collecting negatives var)) - ((pred zerop) (collecting zeroes var)))) - (finally-return negatives zeroes positives)) -#+end_src +code. Loopy currently provides this feature as a separate macro, +~loopy-iter~, which expands looping constructs using ~macroexpand~ +(see [[*Loop Commands in Arbitrary Code][Loop Commands in Arbitrary Code]] in this README). Loopy is not yet feature complete. Please request features or report problems in this project’s [[https://github.com/okamsn/loopy/issues][issues tracker]]. While basic uses are covered, some of the @@ -313,13 +279,21 @@ Iterate to Emacs Lisp. (require 'loopy-iter) ;; => ((1 2 3) (-3 -2 -1) (0)) - (loopy-iter (accum-opt positives negatives other) - (numbering i :from -3 :to 3) - (pcase i - ((pred cl-plusp) (collecting positives i)) - ((pred cl-minusp) (collecting negatives i)) - (_ (collecting other i))) - (finally-return positives negatives other)) + ;; Things to node: + ;; - `accum-opt' produces more efficient accumulations for names variables + ;; - `cycling' is another name for `repeat' + ;; => ((-9 -8 -7 -6 -5 -4 -3 -2 -1) + ;; (0) + ;; (1 2 3 4 5 6 7 8 9 10 11)) + (loopy-iter (accum-opt positives negatives zeroes) + (numbering i :from -10 :to 10) + ;; Normal `let' and `pcase', not Loopy constructs: + (let ((var (1+ i))) + (pcase var + ((pred cl-plusp) (collecting positives var)) + ((pred cl-minusp) (collecting negatives var)) + ((pred zerop) (collecting zeroes var)))) + (finally-return negatives zeroes positives)) ;; => 6 (loopy-iter (listing elem '(1 2 3)) diff --git a/doc/loopy-doc.org b/doc/loopy-doc.org index c02e5cdc..6f16dc35 100644 --- a/doc/loopy-doc.org +++ b/doc/loopy-doc.org @@ -1,4 +1,13 @@ # -*- lexical-binding: t; -*- +# Conventions for this document: +# +# 1. Because the exporter doesn't support custom Texinfo indices, +# - Special macro arguments go in pindex +# - Loop commands go in tindex +# +# 2. "Loopy" is the library, "~loopy~" is the macro, and "=loopy=" is the loop +# command. Now that we have ~loopy~ and ~loopy-iter~, looping constructs +# should generally be described as part of "Loopy", not a particular macro. #+title: Loopy: A Looping and Iteration Macro #+author: Earl Hyatt @@ -20,7 +29,7 @@ #+TEXINFO_DIR_DESC: A better looping and iteration macro. #+MACRO: dfn @@texinfo:@dfn{$1}@@ -#+MACRO: kbd @@texinfo:@kbd{$1}@@ +#+MACRO: kbd (eval (org-texinfo-kbd-macro $1)) #+MACRO: file @@texinfo:@file{$1}@@ #+MACRO: var @@texinfo:@var{$1}@@ @@ -63,7 +72,7 @@ libraries =seq= ([[info:elisp#Sequence Functions]]) and =cl-lib= ([[info:cl]])). - [[#optimizing-accumulations][Optimizing Accumulations]] - [[#checking-conditions][Checking Conditions]] - [[#control-flow][Control Flow]] - - [[#conditionals][Conditionals]] + - [[#conditional-execution][Conditional Execution]] - [[#skipping-cycles][Skipping Cycles]] - [[#early-exit][Early Exit]] - [[#sub-loops][Sub-Loops]] @@ -84,9 +93,11 @@ libraries =seq= ([[info:elisp#Sequence Functions]]) and =cl-lib= ([[info:cl]])). - [[#iteration-clauses][Iteration Clauses]] - [[#accumulation-clauses][Accumulation Clauses]] - [[#other-clauses][Other Clauses]] -- [[#macro-argument-and-loop-command-index][Macro Argument and Loop Command Index]] -- [[#variable-index][Variable Index]] -- [[#concept-index][Concept Index]] +- [[#index-of-concepts][Index of Concepts]] +- [[#index-of-variables][Index of Variables]] +- [[#index-of-functions-and-macros][Index of Functions and Macros]] +- [[#index-of-special-macro-arguments][Index of Special Macro Arguments]] +- [[#index-of-loop-commands][Index of Loop Commands]] - [[#footnotes][Footnotes]] * Introduction @@ -98,6 +109,7 @@ Loopy is a library for looping and iteration, with supplemental features for destructuring. Upfront, the features provided are summarized below. They are described thoroughly later in this document. +#+findex: loopy - ~loopy~ :: A macro similar to ~cl-loop~. Unlike ~cl-loop~, ~loopy~ uses parenthetical forms instead of keyword "clauses". @@ -117,21 +129,28 @@ described thoroughly later in this document. (finally-return odds evens)) #+end_src +#+findex: loopy-iter - ~loopy-iter~ :: A macro similar to Common Lisp's Iterate macro ([[#loopy-iter]]). - Unlike Common Lisp's ~loop~, the ~iterate~ macro allows the embedding of its + Unlike Common Lisp's ~loop~, the ~iterate~ macro allows one to embed its looping constructs inside of arbitrary code. To be clear, ~loopy-iter~ is _not_ a port of ~iterate~ to Emacs Lisp. #+begin_src emacs-lisp ;; => ((1 2 3) (-3 -2 -1) (0)) (loopy-iter (numbering i -3 3) + ;; Normal `pcase' macro, not a `loopy' feature: (pcase i + ;; `collecting' is a `loopy' feature: ((pred cl-plusp) (collecting positives i)) ((pred cl-minusp) (collecting negatives i)) (_ (collecting other i))) (finally-return positives negatives other)) #+end_src +#+findex: loopy-let +#+findex: loopy-setq +#+findex: loopy-lambda +#+findex: loopy-ref - ~loopy-let*~, ~loopy-setq~, ~loopy-lambda~, and ~loopy-ref~ :: Destructuring macros that can be used outside of ~loopy~ and ~loopy-iter~ ([[#basic-destructuring]]). For convenience, Loopy provides its own form of @@ -155,6 +174,18 @@ described thoroughly later in this document. (list my-list my-vector))) #+end_src +- A ~pcase~ pattern :: Loopy provides a ~pcase~ pattern, called =loopy=, which + can be used in macros like ~pcase-lambda~ and ~pcase-let~. Some of the + destructuring macros provided by Loopy are implemented using this ~pcase~ + macro. + + #+begin_src emacs-lisp + ;; => (1 2 3 (:k1 4) 4) + (pcase-let (((loopy (a b c &rest d &key k1)) + '(1 2 3 :k1 4))) + (list a b c d k1)) + #+end_src + Some other things to note are: @@ -223,8 +254,8 @@ Some other things to note are: - One can declare which accumulations variables should be optimized (and so treated as user-inaccessible during the loop). This can make accumulation - into multiple named variables much faster. As needed, can make Loopy optimize - for at-start accumulations or at-end accumulations. + into multiple named variables much faster. As needed, one can make Loopy + optimize for at-start accumulations or at-end accumulations. #+begin_src emacs-lisp ;; Expands into the efficient `push'-`nreverse' idiom, not @@ -261,11 +292,12 @@ local to the loop, add code that runs before/after the loop, and/or set the ultimate return value of the macro. For convenience and clarity, expressions that generate code in the loop body are -called "loop commands" ([[#loop-commands][Loop Commands]]). Expressions that generate code around -the loop are called "special macro arguments" or just "macro arguments" as -opposed to "loop commands" ([[#macro-arguments][Special Macro Arguments]]). +called {{{dfn(loop commands)}}} ([[#loop-commands][Loop Commands]]). Expressions that generate code +around the loop are called {{{dfn(special macro arguments)}}} or just +{{{dfn(macro arguments)}}} as opposed to {{{dfn(loop commands)}}} +([[#macro-arguments][Special Macro Arguments]]). -"Loop commands" are the main building blocks of the ~loopy~ macro, such as the +Loop commands are the main building blocks of the ~loopy~ macro, such as the command =list= in the expression =(list i '(1 2 3))=. A command inserts code into the loop body, but can also perform additional setup like initializing variables. Many commands set a condition for ending the loop. In the case of @@ -275,10 +307,10 @@ elements, the loop is forced to end. In general, a loop ends when any looping condition required by a loop command evaluates to ~nil~. If no conditions are needed, then the loop runs infinitely -until a early-exit command is reached ([[#exiting-the-loop-early][Exiting the Loop Early]]). +until an early-exit command is reached ([[#exiting-the-loop-early][Exiting the Loop Early]]). The default return value of the loop is ~nil~. Other return values must be -stated explicitly, as in one of the early-exit commands or part of the +stated explicitly, as in one of the early-exit commands or as part of the =finally-return= macro argument, or come from accumulating loop commands using an implied accumulation variable ([[#accumulation-commands][Accumulation Commands]]). @@ -301,7 +333,11 @@ command, and will signal an error if that fails. These special macro arguments are always processed before loop commands, regardless of the order of the arguments passed to ~loopy~. -#+findex: named +For clarity, some special macro arguments have multiple names. For example, the +special macro argument =with= can also be written as =init=, because the +argument =with= is used to initialized variables. + +#+pindex: named - =named= or just a symbol :: Name the loop. This also names the ~cl-block~ which contains the loop. This can be of the form =(named NAME)= or just =NAME=. @@ -322,9 +358,9 @@ regardless of the order of the arguments passed to ~loopy~. (return-from outer j)))) #+end_src -#+findex: with -#+findex: let* -#+findex: init +#+pindex: with +#+pindex: let* +#+pindex: init - =with=, =let*=, =init= :: Declare variables before the loop, in order. This can also be used to initialize variables referenced by loop commands. =with= can use destructuring ([[#basic-destructuring]]). @@ -336,25 +372,18 @@ regardless of the order of the arguments passed to ~loopy~. (b (1+ a))) ; Set `b' to 1+1=2. (list i '(1 2 3)) ; Bind `i' to elements of the list. (collect (+ i a b))) ; Collect sum of `a', `b', and each `i' into a list. - - ;; => 16 - (loopy - (let* (my-sum 10)) ; Bind `my-sum' to 10. - (list i '(1 2 3)) ; Bind `i' to elements of the list. - (sum my-sum i) ; Set `my-sum' to `i' + `my-sum'. - (finally-return my-sum)) ; Return the value of `my-sum'. #+end_src -#+findex: without -#+findex: no-with -#+findex: no-init +#+pindex: without +#+pindex: no-with +#+pindex: no-init - =without=, =no-with=, =no-init= :: Variables that ~loopy~ should not try to initialize. ~loopy~ tries to initialize all of the variables that it uses in a ~let~-like form, but that isn’t always desired. #+begin_src emacs-lisp ;; Without `without', `loopy' would try to initialize `a' to nil, which would - ;; overwrite the value of 5 above. + ;; shadow the variable `a' bound by the `let' form. ;; => (5 4 3 2 1) (let ((a 5)) @@ -362,19 +391,12 @@ regardless of the order of the arguments passed to ~loopy~. (until (zerop a)) ; Leave loop when `a' equals 0. (collect a) ; Collect the value of `a' into a list. (set a (1- a)))) ; Set `a' to the value of `(1- a)'. - - ;; => (5 4 3 2 1) - (let ((a 5)) - (loopy (no-init a) - (while (not (zerop a))) - (collect a) - (set a (1- a)))) #+end_src -#+findex: before -#+findex: before-do -#+findex: initially-do -#+findex: initially +#+pindex: before +#+pindex: before-do +#+pindex: initially-do +#+pindex: initially - =before-do=, =before=, =initially-do=, =initially= :: Run Lisp expressions before the loop starts, after variables are initialized. @@ -385,48 +407,29 @@ regardless of the order of the arguments passed to ~loopy~. (cl-incf b)) ; Add 1 to `b'. (list i '(1 2 3)) ; Set `i' to each element in the list. (collect (+ i a b))) ; Collect each sum into a list. - - ;; => (1 2 3) - (loopy (with (a 1)) - ;; Message before the loop starts: - (initially (message "Starting loop...")) - (list i '(1 2 3)) - (collect i)) #+end_src -#+findex: after-do -#+findex: after -#+findex: else-do -#+findex: else +#+pindex: after-do +#+pindex: after +#+pindex: else-do +#+pindex: else - =after-do=, =after=, =else-do=, =else= :: Run Lisp expressions after the loop successfully completes. This is similar to Python’s ~else~ statement following a ~for~ or ~while~ loop. Unlike ~progn~, the return values of the expressions _do not_ affect the return value of the macro. #+begin_src emacs-lisp - ;; Messages that no odd number was found: - ;; => nil - (loopy (list i '(2 4 6 8)) - (when (cl-oddp i) - (do (message "Odd number found.")) - (return t)) ; Make the loop return `t'. - (after-do - (message "No odd number found.") - ;; The macro already return `nil' by default, - ;; but one can still use `cl-return' to be more explicit. - (cl-return nil))) - ;; Messages that an odd number was found: ;; => t (loopy (list i '(2 4 5 8)) (when (cl-oddp i) (do (message "Odd number found.")) (return t)) - (else (message "No odd number found."))) + (after-do (message "No odd number found."))) #+end_src -#+findex: finally-do -#+findex: finally +#+pindex: finally-do +#+pindex: finally - =finally-do=, =finally= :: Run Lisp expressions after the loop exits, always. Unlike ~progn~, the return values of the expressions _do not_ affect the return value of the macro. @@ -446,7 +449,7 @@ regardless of the order of the arguments passed to ~loopy~. (finally-do 999)) #+end_src -#+findex: finally-return +#+pindex: finally-return - =finally-return= :: Return a value, regardless of how the loop completes. These arguments override any explicit return values given in commands like =return= and =return-from=, as well as any implicit return values that can @@ -464,8 +467,8 @@ regardless of the order of the arguments passed to ~loopy~. (finally-return 1 2)) #+end_src -#+findex: finally-protect -#+findex: finally-protected +#+pindex: finally-protect +#+pindex: finally-protected - =finally-protect=, =finally-protected= :: Wrap the loop in ~unwind-protect~ (not to be confused with ~condition-case~). The arguments to this special macro argument (which are Lisp expressions) can access the variables used by @@ -494,8 +497,8 @@ regardless of the order of the arguments passed to ~loopy~. my-collection))) #+end_src -#+findex: flag -#+findex: flags +#+pindex: flag +#+pindex: flags - =flag=, =flags= :: Options that change the behavior of ~loopy~ ([[#flags]]). For example, one can opt to use a different destructuring system, such as what is provided by the Dash library. See that linked section for more @@ -521,8 +524,8 @@ regardless of the order of the arguments passed to ~loopy~. (return a b)) #+end_src -#+findex: accum-opt -#+findex: opt-accum +#+pindex: accum-opt +#+pindex: opt-accum - =accum-opt=, =opt-accum= :: Accumulation variables whose use should be optimized ([[#optimized-accums]]). Implicit accumulation variables are always optimized, but explicit variables are unoptimized by default @@ -550,7 +553,7 @@ regardless of the order of the arguments passed to ~loopy~. #+end_src -#+findex: wrap +#+pindex: wrap - =wrap= :: A list of forms in which to wrap the loop itself (that is, not =before-do=, =after-do=, or anything else). Each form can be either a list or a symbol. If a list, the loop is inserted into the end of the list. If @@ -658,7 +661,7 @@ is assigned a value from the list after collecting =i= into =coll=. #+END_SRC For convenience and understanding, the same command might have multiple names, -called {{{dfn(aliases)}}}. Similary, the =array= command has the alias +called {{{dfn(aliases)}}}. For example, the =array= command has the alias =string=, because the =array= command can be used to iterate through the elements of an array or string[fn:1]. You can define custom aliases using the macro ~loopy-defalias~ ([[#custom-aliases][Custom Aliases]]). @@ -684,9 +687,10 @@ For simplicity, the commands are described using the following notation: such as in =array|string=. - =VAR= is an unquoted symbol that will be used as a variable name, such as =i= in =(list i my-list)=. -- =FUNC= is a quoted Lisp function name, such as ~#'my-func~ or ~'my-func~, a - variable whose value is a function, or a ~lambda~ expression. -- =NAME= is an unquoted name of a loop (or, more accurately, of a =cl-block=). +- =FUNC= is an expression that evaluates to a function or symbol naming a + function, such as ~#'my-func~, ~'my-func~, a variable whose value is a + function, or a ~lambda~ expression. +- =NAME= is an unquoted name of a loop (or, more accurately, of a ~cl-block~). - =EXPR= is a single Lisp expression, such as =(+ 1 2)=, ='(1 2 3)=, =my-var=, or =(some-function my-var)=. =EXPRS= means multiple expressions. Really, we are concerned with the value of the expression, not the expression itself. @@ -785,6 +789,7 @@ commands, such as =set= and =list=. Destructuring in accumulation commands ([[#sequence-reference-iteration]]) works slightly differently, and is described more in those sections. +#+cindex: pcase pattern loopy In addition to what can be done in loop commands, several features are available for using Loopy's destructuring outside of ~loopy~ loops ([[#destr-macros]]), including the ~pcase~ pattern =loopy=. @@ -828,30 +833,70 @@ Below are two examples of destructuring in ~cl-loop~ and ~loopy~. You can use destructured assignment by passing an unquoted sequence of symbols -as the =VAR= argument of a loop command. Loopy supports destructuring lists and -arrays (which includes strings and vectors). +as the =VAR= argument of a loop command. Loopy supports destructuring lists, +arrays (which includes strings and vectors), and generic sequences as defined by +the =seq.el= library. + - To destructure lists, use a list, as in =(a b c)=. + - To destructure arrays, use a vector, as in =[a b c]=. + - To destructure sequences generically using =seq.el= (mainly via ~seq-elt~ and ~seq-drop~), use a vector or a list whose first element is =&seq=, as in =[&seq a b c]= and =(&seq a b c)=. -This sequence of symbols can be shorter than the destructured sequence, /but not -longer/. If shorter, the unassigned elements of the destructured sequence are -simply ignored. + +The sequence of symbols can receive less values than there are in the sequence, +/but not more/. If less, the remainder of the sequence is simply ignored. If +more, than an error is signalled. Note that this differs from ~seq-let~, which +can bind unfound values to ~nil~ in some cases, as seen below. + +#+begin_src emacs-lisp + ;; Ignores third element of the vector: + ;; + ;; => ((1 4) (2 5)) + (loopy (list [a b] '([1 2 3] [4 5 6])) + (collect as a) + (collect bs b) + (finally-return as bs)) + + ;; Signals an error: + (loopy (list [a b c] '([1 2] [3 4])) + (collect as a) + (collect bs b) + (collect cs c) + (finally-return as bs cs)) + + ;; Does not signal an error: + ;; + ;; (1 2 nil) + (seq-let (a b c) + [1 2] + (list a b c)) + + ;; Signals an error: + (loopy (list (&seq a b c) '([1 2] [3 4])) + (collect as a) + (collect bs b) + (collect cs c) + (finally-return as bs cs)) +#+end_src The content of this destructuring sequence is similar to =cl-lib=, and is #+begin_example -POSITIONAL-VARIABLES -&optional OPTIONAL-VARIABLES -&rest REST-VARIABLE -&key KEY-VARIABLES [&allow-other-keys] -&map MAP-VARIABLES -&aux AUXILLIARY-VARIABLES + [&seq] + [&whole WHOLE-VARIABLE] + [POSITIONAL-VARIABLES] + [&optional OPTIONAL-VARIABLES] + [&rest REST-VARIABLE] + [&key KEY-VARIABLES [&allow-other-keys]] + [&map MAP-VARIABLES] + [&aux AUXILLIARY-VARIABLES] #+end_example -in which at least one of the above constructs must be provided. +in which at least one of the above constructs that binds variables must be +provided. #+begin_src emacs-lisp ;; => (1 2 3 @@ -951,7 +996,7 @@ In more detail, the elements of the destructuring sequence can be: ~lambda~ list. These variables can themselves be sequences to be further destructured. - When used after optional values, the =&rest= value is the subsequence starting + When used after optional values, the =&rest= value is the sub-sequence starting at the index after any possible optional values, even when those optional values are not actually present. If the sequence is not long enough, then the sub-sequence is empty. @@ -1195,12 +1240,12 @@ In more detail, the elements of the destructuring sequence can be: - =(KEY VAR)= or =[KEY VAR]=, in which =VAR= itself can be a sequence - - =(VAR)= or =[VAR]=, in which =VAR= is a symbol + - =(VAR)= or =[VAR]=, in which =VAR= must be a _symbol_ - a symbol =VAR= When =KEY= is not given, then the key is the symbol =VAR=, as in ~(quote - VAR)~. Unlike with =&key=, it is not prepended with a colon. + VAR)~. Unlike with =&key=, it is _not_ prepended with a colon. #+begin_src emacs-lisp ;; => ((1 2 3 4 27)) @@ -1265,8 +1310,8 @@ In more detail, the elements of the destructuring sequence can be: an array or as a list, when applicable. Sequences destructured using =&seq= can still use =&whole=, =&optional=, - =&rest=, and =&map=. However, lists destructured using =&seq= cannot be - destructured using =&key=. + =&rest=, and =&map=. However, note that because the type of the destructured + sequence is not known ahead of time, =&key= cannot be used with =&seq=. #+begin_src emacs-lisp ;; => ((0 1 2 nil nil) @@ -1284,17 +1329,17 @@ In more detail, the elements of the destructuring sequence can be: :END: These generic commands are for settings values and running sub-commands or -sub-expressions during the loop. These commands do not affect macro's return -value and do no affect how the loop iterates. +sub-expressions during the loop. These commands do not affect the macro's +return value and do no affect how the loop iterates. -#+findex: do +#+tindex: do - =(do EXPRS)= :: Evaluate multiple Lisp expressions, like a =progn=. You cannot include arbitrary code in the loop body in ~loopy~. Trying to do so will result in errors, as the macro will attempt to interpret such code as - a command. + a loop command. - To use loopy commands in arbitrary code, use the macro ~loopy-iter~ instead + To use loop commands in arbitrary code, use the macro ~loopy-iter~ instead ([[#loopy-iter]]). #+BEGIN_SRC emacs-lisp @@ -1302,7 +1347,7 @@ value and do no affect how the loop iterates. (do (message "%d" i))) #+END_SRC -#+findex: command-do +#+tindex: command-do - =(command-do [CMDS])= :: Evaluate multiple loop commands, as if in a =progn=. This is similar to =do=, but runs commands instead of normal Lisp expressions. Currently, this command is only useful when used within the @@ -1322,9 +1367,10 @@ value and do no affect how the loop iterates. (sum i))) #+end_src -#+findex: set -#+findex: setting -- =(set VAR EXPR [EXPRS])= :: Bind =VAR= to each =EXPR= in order. Once the last +#+tindex: set +#+tindex: setting +- =(set VAR EXPR [EXPRS])= :: Bind =VAR= to each =EXPR=, in order. In the n-th + cycle of the loop, bind =VAR= to the n-th =EXPR=. Once the last =EXPR= is reached, it is used repeatedly for the rest of the loop. This command also has the aliases =setting=. @@ -1349,9 +1395,9 @@ value and do no affect how the loop iterates. (finally-return coll)) #+END_SRC -#+findex: set-prev -#+findex: setting-prev -#+findex: prev-set +#+tindex: set-prev +#+tindex: setting-prev +#+tindex: prev-set - =(set-prev VAR VAL &key back)= :: Bind =VAR= to a value =VAL= from a previous cycle in the loop. With =BACK= (default: 1), use the value from that many cycles previous. _If not enough cycles have passed yet, then the value of @@ -1410,14 +1456,14 @@ value and do no affect how the loop iterates. :DESCRIPTION: Iterating through sequences, etc. :END: -Iteration commands bind local variables and determine when the loop ends. If no -command sets an ending condition, then the loop runs forever. Infinite loops -can be exited by using early-exit commands ([[#exiting-the-loop-early]]) or boolean -commands ([[#boolean-commands]]). +{{{dfn(Iteration commands)}}} bind local variables and determine when the loop +ends. If no command sets an ending condition, then the loop runs forever. +Infinite loops can be exited by using early-exit commands +([[#exiting-the-loop-early]]) or boolean commands ([[#boolean-commands]]). -Iteration commands must occur in the top level of the ~loopy~ form or in a -sub-loop command ([[#sub-loops]]). Using them elsewhere and trying to do something -like the below example will signal an error. +Iteration commands must occur in the top level of the macro or in the top level +of a sub-loop command ([[#sub-loops]]). Using them elsewhere and trying to do +something like the below example will signal an error. #+begin_src emacs-lisp ;; Signals an error: @@ -1536,10 +1582,10 @@ loop. This restriction allows for producing more efficient code. :DESCRIPTION: Looping a certain number of times. :END: -#+findex: cycle -#+findex: cycling -#+findex: repeat -#+findex: repeating +#+tindex: cycle +#+tindex: cycling +#+tindex: repeat +#+tindex: repeating - =(cycle|repeat [VAR] EXPR)= :: Run the loop for =EXPR= iterations. If given, then during the loop, =VAR= is set to the number of iteration steps @@ -1578,8 +1624,8 @@ loop. This restriction allows for producing more efficient code. (return 'return-command-ran)) #+END_SRC -#+findex: iter -#+findex: iterating +#+tindex: iter +#+tindex: iterating - =(iter [VAR] EXPR &key close yield-result)= :: Iterate through the values returned by an Emacs Lisp iterator ([[info:elisp#Generators]]). =EXPR= is an iterator object produced by a calling a generator function. If given, =VAR= @@ -1685,14 +1731,14 @@ loop. This restriction allows for producing more efficient code. :DESCRIPTION: Iterating through numbers. :END: -For iterating through numbers, there is the general =numbers= command, and its -variants =numbers-up= and =numbers-down=. +For iterating through numbers, there is the =numbers= command and its +more limited variants =numbers-up= and =numbers-down=. -#+findex: num -#+findex: nums -#+findex: number -#+findex: numbering -#+findex: numbers +#+tindex: num +#+tindex: nums +#+tindex: number +#+tindex: numbering +#+tindex: numbers - =(numbers|nums VAR &key KEYS)= :: Iterate through numbers. =KEYS= is one or several of =from=, =upfrom=, =downfrom=, =to=, =upto=, =downto=, =above=, =below=, =by=, and =test=. @@ -1753,8 +1799,8 @@ variants =numbers-up= and =numbers-down=. To specify the step size, one can use the keyword =:by=. Except when =:test= is given, _the value for =:by= must be positive_. Other keyword arguments - (=:upfrom=, =:downfrom=, =:upto=, =:downto=, =:above=, and =:below=) control - whether the variable is incremented or decremented. + (that is, =:upfrom=, =:downfrom=, =:upto=, =:downto=, =:above=, and =:below=) + control whether the variable is incremented or decremented. #+begin_src emacs-lisp ;; => (1 3 5) @@ -1776,8 +1822,8 @@ variants =numbers-up= and =numbers-down=. keyword, one can use the keywords =:downfrom=, =:downto=, =:upfrom=, =:upto=, =:above=, and =:below=. The keywords =:from= and =:to= don't by themselves specify a direction, and they can be used without conflict with the keyword - arguments that do. Using arguments that contradict one another will signal - an error. + arguments that do specify a direction. Using arguments that contradict one + another will signal an error. #+begin_src emacs-lisp ;; => (3 2 1) @@ -1834,7 +1880,7 @@ variants =numbers-up= and =numbers-down=. receives =VAR= as the first argument and the final value as the second argument, as in ~(funcall TEST VAR FINAL-VAL)~. =test= can only be used with =from= and =to=; it cannot be used with keywords that already describe a - direction and ending condition. To match the behavior of ~cl-loop~, the + direction and an ending condition. To match the behavior of ~cl-loop~, the default testing function is ~#'<=~. When =test= is given, =by= can be negative. As there is no default end value when =test= is given, =to= must also be given. @@ -1867,9 +1913,9 @@ If you prefer using positional arguments to keyword arguments, you can use the commands =numbers-up= and =numbers-down= to specify directions. These commands are simple wrappers of the above =numbers= command. -#+findix: nums-down -#+findex: numbers-down -#+findex: numbering-down +#+tindex: nums-down +#+tindex: numbers-down +#+tindex: numbering-down - =(numbers-down|nums-down VAR START [END] &key by)= :: Equivalent to =(numbers VAR :from START [:downto END] &key by)=. This command exists only for convenience. @@ -1885,9 +1931,9 @@ are simple wrappers of the above =numbers= command. (collect i)) #+end_src -#+findix: nums-up -#+findex: numbers-up -#+findex: numbering-up +#+tindex: nums-up +#+tindex: numbers-up +#+tindex: numbering-up - =(numbers-up|nums-up VAR START [END] &key by)= :: Equivalent to =(numbers VAR :from START [END] &key by)=. This command exists only for convenience. @@ -1976,10 +2022,10 @@ source sequences. #+end_src -#+findex: array -#+findex: string -#+findex: arraying -#+findex: stringing +#+tindex: array +#+tindex: string +#+tindex: arraying +#+tindex: stringing - =(array|string VAR EXPR [EXPRS] &key KEYS)= :: Loop through the elements of the array =EXPR=. In Emacs Lisp, strings are arrays whose elements are characters. @@ -1987,9 +2033,9 @@ source sequences. This command also has the aliases =arraying= and =stringing=. =KEYS= is one or several of =from=, =upfrom=, =downfrom=, =to=, =upto=, - =downto=, =above=, =below=, =by=, and =index=. =index= names the variable - used to store the index being accessed. For others, see the =numbers= - command. + =downto=, =above=, =below=, =by=, =test=, and =index=. =index= names the + variable used to store the index being accessed. For the others, see the + =numbers= command. If multiple arrays are given, then the elements of these arrays are distributed into an array of lists. In that case, the above keywords apply to @@ -2018,9 +2064,9 @@ source sequences. (collect i)) #+END_SRC -#+findex: cons -#+findex: conses -#+findex: consing +#+tindex: cons +#+tindex: conses +#+tindex: consing - =(cons|conses VAR EXPR &key by)= :: Loop through the cons cells of =EXPR=. Optionally, find the cons cells via the function =by= instead of =cdr=. @@ -2038,11 +2084,11 @@ source sequences. (finally-return coll)) #+END_SRC -#+findex: list -#+findex: listing -#+findex: each +#+tindex: list +#+tindex: listing +#+tindex: each - =(list|each VAR EXPR [EXPRS] &key by)= :: Loop through each element of the - list =EXPR=. Optionally, update the list using =by= instead of =cdr=. + list =EXPR=. Optionally, move through the list using =by= instead of =cdr=. This command also has the alias =listing=. @@ -2068,14 +2114,14 @@ source sequences. (collect i)) #+END_SRC -#+findex: map -#+findex: map-pairs -#+findex: mapping -#+findex: mapping-pairs +#+tindex: map +#+tindex: map-pairs +#+tindex: mapping +#+tindex: mapping-pairs - =(map|map-pairs VAR EXPR &key unique)= :: Iterate through the dotted key-value pairs of map =EXPR=, using the function ~map-pairs~ from the =map.el= library. This library generalizes working with association lists ("alists"), property - lists ("plists"), hash-tables, and vectors. + lists ("plists"), hash tables, and vectors. This command also has the aliases =mapping= and =mapping-pairs=. @@ -2130,8 +2176,8 @@ source sequences. #+end_src Depending on how a map is created, a map might contain a key multiple times. - Currently, the function ~map-pairs~ returns such keys. By default, the - ~loopy~ command =map-pairs= ignores such duplicate keys. This is for two + Currently, the function ~map-pairs~ returns such keys. However, by default, + the ~loopy~ command =map-pairs= ignores such duplicate keys. This is for two reasons: 1. This is more consistent with the command =map-ref=, for which such duplicates are more likely to cause errors. @@ -2163,8 +2209,8 @@ source sequences. (collect (list key val))) #+end_src -#+findex: sequence -#+findex: sequencing +#+tindex: sequence +#+tindex: sequencing - =(sequence VAR EXPR [EXPRS] &key KEYS)= :: Loop through the sequence =EXPR=, binding =VAR= to the elements of the sequence (a list or an array). Because it is more generic, =sequence= is somewhat less efficient than the @@ -2227,13 +2273,13 @@ source sequences. (collect i)) #+END_SRC -#+findex: seq -#+findex: seqing -- =(seq VAR EXPR [EXPRS] &key KEYS)= :: For generic a sequence implementing the - features of the library =seq.el=, loop through the generic sequence =EXPR=, - binding =VAR= to the elements of the sequence. Because it is more generic, - =seq= can be slower than the =sequence= command, which in turn is somewhat - less efficient than the =list= and =array= commands. +#+tindex: seq +#+tindex: seqing +- =(seq VAR EXPR [EXPRS] &key KEYS)= :: For a generic sequence which implements + the features of the library =seq.el=, loop through the generic sequence + =EXPR=, binding =VAR= to the elements of the sequence. Because it is more + generic, =seq= can be slower than the =sequence= command, which in turn is + somewhat less efficient than the =list= and =array= commands. If multiple generic sequences are given, then these keyword arguments apply to the resulting generic sequence of distributed elements. @@ -2269,8 +2315,8 @@ source sequences. (collect i)) #+end_src -#+findex: stream -#+findex: streaming +#+tindex: stream +#+tindex: streaming - =(stream VAR EXPR &key by)= :: Iterate through the elements for the stream =EXPR=. If =by= is non-nil (default: 1), then move to the next n-th element during each iteration. This command is a special case of the =substream= @@ -2290,8 +2336,8 @@ source sequences. (collect (stream-first i))) #+end_src -#+findex: substream -#+findex: substreaming +#+tindex: substream +#+tindex: substreaming - =(substream VAR EXPR &key by length)= :: Iterate through the sub-streams of stream =EXPR=, similar to the command =cons=. If =by= is non-nil (default: 1), then move to the next n-th substream during each iteration. If =length= @@ -2370,16 +2416,16 @@ same keywords as the =numbers= command ([[#numeric-iteration]]) for working with the index and choosing a range of the sequence elements through which to iterate. -#+findex: sequence-index -#+findex: sequencing-index -#+findex: seq-index -#+findex: seqing-index -#+findex: array-index -#+findex: arraying-index -#+findex: list-index -#+findex: listing-index -#+findex: string-index -#+findex: stringing-index +#+tindex: sequence-index +#+tindex: sequencing-index +#+tindex: seq-index +#+tindex: seqing-index +#+tindex: array-index +#+tindex: arraying-index +#+tindex: list-index +#+tindex: listing-index +#+tindex: string-index +#+tindex: stringing-index - =(sequence-index VAR EXPR &key KEYS)= :: Iterate through the indices of =EXPR=. There is only one implementation of this command; there are no @@ -2483,10 +2529,10 @@ sequence elements through which to iterate. In addition to those keywords, they also have an =index= keyword, which names the variable used to store the accessed index during the loop. -#+findex: array-ref -#+findex: string-ref -#+findex: arraying-ref -#+findex: stringing-ref +#+tindex: array-ref +#+tindex: string-ref +#+tindex: arraying-ref +#+tindex: stringing-ref - =(array-ref|string-ref VAR EXPR &key KEYS)= :: Loop through the elements of the array =EXPR=, binding =VAR= as a ~setf~-able place. @@ -2517,8 +2563,8 @@ the accessed index during the loop. (finally-return my-str)) #+END_SRC -#+findex: list-ref -#+findex: listing-ref +#+tindex: list-ref +#+tindex: listing-ref - =(list-ref VAR EXPR &key by)= :: Loop through the elements of the list =EXPR=, binding =VAR= as a ~setf~-able place. Optionally, update the list via function =by= instead of ~cdr~. @@ -2551,8 +2597,8 @@ the accessed index during the loop. (finally-return my-list)) #+END_SRC -#+findex: map-ref -#+findex: mapping-ref +#+tindex: map-ref +#+tindex: mapping-ref - =(map-ref VAR EXPR &key key unique)= :: Loop through the values of map =EXPR=, binding =VAR= as a ~setf~-able place. Like the command =map=, this command uses the =map.el= library. @@ -2605,8 +2651,8 @@ the accessed index during the loop. (finally-return map loopy-result)) #+end_src -#+findex: sequence-ref -#+findex: sequencing-ref +#+tindex: sequence-ref +#+tindex: sequencing-ref - =(sequence-ref VAR EXPR &key KEYS)= :: Loop through the elements of the sequence =EXPR= (an array or list), binding =VAR= as a ~setf~-able place. @@ -2637,8 +2683,8 @@ the accessed index during the loop. (finally-return my-str)) #+END_SRC -#+findex: seq-ref -#+findex: seqing-ref +#+tindex: seq-ref +#+tindex: seqing-ref - =(seq-ref VAR EXPR &key KEYS)= :: Loop through the elements of the generic sequence =EXPR=, via the features of the library =seq.el=, binding =VAR= as a ~setf~-able place. @@ -2690,9 +2736,9 @@ the accessed index during the loop. :DESCRIPTION: Accumulating values into new sequences, aggregating values, etc. :END: -Accumulation commands are used to accumulate or aggregate values into a -variable, such as creating a list of values or summing the elements in a -sequence. +{{{dfn(Accumulation commands)}}} are used to accumulate or aggregate values into +a variable, such as creating a list of return values or summing the elements of +a sequence. Unlike iteration commands, you can refer to the same accumulation variable in multiple accumulation commands if needed. @@ -2744,15 +2790,17 @@ be overridden by using the the =return= and =return-from= loop commands or the #+begin_src emacs-lisp ;; => (1 2 3) - (cl-loop for i from 1 to 3 collect i) + (cl-loop for i from 1 to 3 + collect i) ;; => (1 2 3) - (loopy (numbers i :from 1 :to 3) (collect i)) + (loopy (numbers i :from 1 :to 3) + (collect i)) #+end_src #+vindex: loopy-result Unlike ~cl-loop~, Loopy uses a default accumulation variable, which is named -~loop-result~. This variable can be used in the =after-do=, =finally-do=, and +~loopy-result~. This variable can be used in the =after-do=, =finally-do=, and =finally-return= special macro arguments. #+begin_src emacs-lisp @@ -2865,13 +2913,13 @@ conflict, use the =with= special macro argument, as noted above. (finally-return my-accum)) #+end_src -By default, one must specify separate accumulation variables to be able to -accumulate into separate values. This can make accumulation slower, because -~loopy~ ensures that named accumulation variables (excluding the previously -mentioned ~loopy-result~) have the correct value during the loop. For example, -~loopy~ will construct named accumulation variables containing lists in the -correct order, instead of using the more efficient ~push~-~nreverse~ idiom. -This behavior can be disabled by optimizing accumulations using the =accum-opt= +One must specify separate accumulation variables to be able to accumulate into +separate values. This can make accumulation slower, because ~loopy~ ensures +that named accumulation variables (excluding the previously mentioned +~loopy-result~) have the correct value during the loop. For example, ~loopy~ +will construct named accumulation variables containing lists in the correct +order, instead of using the more efficient ~push~-~nreverse~ idiom. This +behavior can be disabled by optimizing accumulations using the =accum-opt= special macro argument ([[#optimized-accums]]). Below are examples of an optimized accumulation and an un-optimized @@ -2951,10 +2999,10 @@ is more complex and uses a slower way of building the accumulated list. #+attr_texinfo: :tag Warning #+begin_quote -You should not try to access implied (or optimized) accumulation results (for -example, ~loopy-result~) while the loop is running. Implied results are only -required to be correct after the loop ends (before code in =else-do= is run), -allowing for more efficient code. +In general, you should not try to access implied (or optimized) accumulation +results (for example, ~loopy-result~) while the loop is running. Implied +results are only required to be correct after the loop ends (before code in +=else-do= is run), allowing for more efficient code. Furthermore, because using a =return= or =return-from= command overrides implied return values, using these commands can prevent implied accumulation results @@ -2997,7 +3045,7 @@ all described below. - =into= :: An alternative way to specify the variable into which to accumulate values. One would normally just give =VAR= as the first argument of the loop command, but if you wish, you can use this keyword - argument for a more ~cl-loop~-like syntax. + argument for a syntax more like ~cl-loop~. As all accumulation commands support this keyword, it is not listed in any command definition. @@ -3024,7 +3072,8 @@ all described below. This argument is similar to the =:test= argument used by =cl-lib=, but is closer to the optional =testfn= argument used by =seq= (for example, in ~seq-contains-p~). There are two important differences: - 1. Tests default to ~equal~, like in other Emacs Lisp libraries, not ~eql~. + 1. The default test function is ~equal~, like in other Emacs Lisp libraries, + not ~eql~. 2. The first argument is the existing value or sequence and the second argument is the tested value. This is the /opposite/ of the order used by ~cl-member~ and ~memq~. @@ -3046,9 +3095,10 @@ all described below. #+cindex: accumulation keyword key - =key= :: A one-argument function that transforms _both_ the tested value and - the value from sequence used by the =test= keyword. + the elements of the sequence passed to the function given by the =test= + keyword. - The keyword =key= is useful to avoid applying a transforming function to the + The keyword =key= is useful to avoid applying a transformation to the tested value more than once when searching through a long sequence, as would be done if it were called explicitly in =test=. @@ -3070,9 +3120,10 @@ all described below. #+end_src -The arguments to the =test= and =key= parameters can be quoted functions or -variables, just like when using ~cl-union~, ~cl-adjoin~, and so on. ~loopy~ -knows how to expand efficiently for either case. +The arguments to the =test= and =key= parameters can be literal quoted functions +or expressions which evaluate to functions, just like when using ~cl-union~, +~cl-adjoin~, and so on. ~loopy~ knows how to expand efficiently for either +case. *** Generic Accumulation :PROPERTIES: @@ -3082,7 +3133,7 @@ knows how to expand efficiently for either case. Generic accumulation commands are more explicit uses of the accumulation variable. They are very similar to updating a variable's value -using the =set= command and exist for situation not covered by the other +using the =set= command and exist for situations not covered by the other accumulation commands. - =reduce= is like ~cl-reduce~, calling a function that receives (1) the @@ -3094,8 +3145,8 @@ accumulation commands. The commands are described in more detail below. -#+findex: reduce -#+findex: reducing +#+tindex: reduce +#+tindex: reducing - =(reduce VAR EXPR FUNC)= :: Reduce =EXPR= into =VAR= by =FUNC=, like in ~cl-reduce~ and ~(funcall FUNC VAR EXPR)~. =FUNC= is called with =VAR= as the first argument and =EXPR= as the second argument. This is unlike @@ -3151,8 +3202,8 @@ The commands are described in more detail below. (finally-return my-reduction)) #+end_src -#+findex: accumulate -#+findex: accumulating +#+tindex: accumulate +#+tindex: accumulating - =(accumulate|accumulating VAR EXPR FUNC)= :: Accumulate the result of applying function =FUNC= to =EXPR= and =VAR= like in ~(funcall FUNC EXPR VAR)~. =EXPR= and =VAR= are used as the first and second arguments to =FUNC=, respectively. @@ -3194,8 +3245,8 @@ The commands are described in more detail below. (finally-return my-accum)) #+end_src -#+findex: set-accum -#+findex: setting-accum +#+tindex: set-accum +#+tindex: setting-accum - =(set-accum VAR EXPR)= :: Set the accumulation variable =VAR= to the value of =EXPR=. @@ -3237,8 +3288,8 @@ The commands are described in more detail below. Numeric accumulation work on numbers, such as by repeatedly adding or multiplying values together. -#+findex: count -#+findex: counting +#+tindex: count +#+tindex: counting - =(count VAR EXPR)= :: Count the number of times that =EXPR= evaluates to a non-nil value. =VAR= starts at 0 and is incremented by 1 each time. @@ -3251,10 +3302,10 @@ multiplying values together. (finally-return non-nil-count)) #+END_SRC -#+findex: max -#+findex: maxing -#+findex: maximize -#+findex: maximizing +#+tindex: max +#+tindex: maxing +#+tindex: maximize +#+tindex: maximizing - =(maximize|max VAR EXPR)= :: Repeatedly set =VAR= to the greater of the values =VAR= and =EXPR=. =VAR= starts at negative infinity (~-1.0e+INF~), so that any other value should be greater that it. @@ -3268,10 +3319,10 @@ multiplying values together. (finally-return my-max)) #+END_SRC -#+findex: min -#+findex: minimize -#+findex: minnning -#+findex: minimizing +#+tindex: min +#+tindex: minimize +#+tindex: minnning +#+tindex: minimizing - =(minimize|min VAR EXPR)= :: Repeatedly set =VAR= to the lesser of the values =VAR= and =EXPR=. =VAR= starts at positive infinity (~1.0e+INF~), so that any other value should be less than it. @@ -3285,8 +3336,8 @@ multiplying values together. (finally-return my-min)) #+END_SRC -#+findex: multiply -#+findex: multiplying +#+tindex: multiply +#+tindex: multiplying - =(multiply VAR EXPR)= :: Repeatedly set =VAR= to the product of the values =EXPR= and =VAR=. =VAR= starts at 1. @@ -3299,8 +3350,8 @@ multiplying values together. (finally-return 5-factorial)) #+END_SRC -#+findex: sum -#+findex: summing +#+tindex: sum +#+tindex: summing - =(sum VAR EXPR)= :: Repeatedly set =VAR= to the sum of the values of =EXPR= and =VAR=. =VAR= starts at 0. @@ -3322,8 +3373,8 @@ multiplying values together. Sequence accumulation commands are used to join lists (such as =union= and =append=) and to collect items into lists (such as =collect= and =adjoin=). -#+findex: adjoin -#+findex: adjoining +#+tindex: adjoin +#+tindex: adjoining - =(adjoin VAR EXPR &key at test key)= :: Repeatedly add =EXPR= to =VAR= if it is not already present in the list. @@ -3332,27 +3383,30 @@ Sequence accumulation commands are used to join lists (such as =union= and Unlike ~cl-adjoin~ and like the other accumulation commands, this command defaults to adjoining =EXPR= to the end of =VAR=, not the beginning. + Unlike Common Lisp's Iterate, Loopy's =adjoin= command intentionally does + not provide a =:result-type= keyword. Instead, manipulate the accumulation + variable (by default, ~loopy-result~) directly, such as with ~seq-into~. + #+begin_src emacs-lisp ;; => ((1 . 1) (1 . 2) (2 . 3)) (loopy (list i '((1 . 1) (1 . 2) (1 . 2) (2 . 3))) (adjoin i)) - ;; Coerced to a vector /after/ the loop ends. + ;; No `:result-type', use `seq-into' on `loopy-result': + ;; ;; => [1 2 3 4] (loopy (list i '(1 2 3 3 4)) - (adjoin my-var i) - (when (vectorp my-var) - (return 'is-vector)) - (finally-return my-var)) + (adjoin i) + (finally-return (seq-into loopy-result 'vector))) - ;; => [4 3 2 1] + ;; => (4 3 2 1) (loopy (list i '(1 2 3 3 4)) (adjoin my-var i :at 'start) (finally-return my-var)) #+end_src -#+findex: append -#+findex: appending +#+tindex: append +#+tindex: appending - =(append VAR EXPR &key at)= :: Repeatedly concatenate =EXPR= to =VAR=, as if by the function ~append~. @@ -3369,13 +3423,18 @@ Sequence accumulation commands are used to join lists (such as =union= and (append i :at start)) #+END_SRC -#+findex: collect -#+findex: collecting +#+tindex: collect +#+tindex: collecting - =(collect VAR EXPR &key at)= :: Collect the value of =EXPR= into the list =VAR=. By default, elements are added to the end of the list. This command also has the alias =collecting=. + Unlike Common Lisp's Iterate, Loopy's =collect= command intentionally does + not provide a =:result-type= keyword. Instead, manipulate the accumulation + variable (by default, ~loopy-result~) directly, such as with ~seq-into~ or + ~cl-coerce~. + #+BEGIN_SRC emacs-lisp ;; => '(1 2 3) (loopy (list i '(1 2 3)) @@ -3403,8 +3462,8 @@ Sequence accumulation commands are used to join lists (such as =union= and (collect j :at 'end)) #+END_SRC -#+findex: concat -#+findex: concating +#+tindex: concat +#+tindex: concating - =(concat VAR EXPR &key at)= :: Repeatedly ~concat~ the value of =EXPR= onto =VAR=, as a string. For concatenating values into a vector, see the command =vconcat=. @@ -3423,8 +3482,8 @@ Sequence accumulation commands are used to join lists (such as =union= and (finally-return str1 str2 str3)) #+END_SRC -#+findex: nconc -#+findex: nconcing +#+tindex: nconc +#+tindex: nconcing - =(nconc VAR EXPR &key at)= :: Repeatedly and /destructively/ concatenate the value of =EXPR= onto =VAR= as if by using the function ~nconc~. @@ -3451,8 +3510,8 @@ Sequence accumulation commands are used to join lists (such as =union= and (nconc i :at start)) #+END_SRC -#+findex: nunion -#+findex: nunioning +#+tindex: nunion +#+tindex: nunioning - =(nunion VAR EXPR &key test at key)= :: Repeatedly and /destructively/ insert into =VAR= the elements of =EXPR= which are not already present in =VAR=. @@ -3476,8 +3535,8 @@ Sequence accumulation commands are used to join lists (such as =union= and (finally-return var1 var2)) #+end_src -#+findex: prepend -#+findex: prepending +#+tindex: prepend +#+tindex: prepending - =(prepend VAR EXPR)= :: Repeatedly concatenate =EXPR= onto the front of =VAR=, as if by the function ~append~. @@ -3500,10 +3559,10 @@ Sequence accumulation commands are used to join lists (such as =union= and (finally-return my-list))) #+end_src -#+findex: push -#+findex: pushing -#+findex: push-into -#+findex: pushing-into +#+tindex: push +#+tindex: pushing +#+tindex: push-into +#+tindex: pushing-into - =(push-into|push VAR EXPR)= :: Collect the value of =EXPR= into a list, adding values to the front of =VAR= as if by using the function ~push~. @@ -3520,8 +3579,8 @@ Sequence accumulation commands are used to join lists (such as =union= and (finally-return my-list)) #+END_SRC -#+findex: union -#+findex: unioning +#+tindex: union +#+tindex: unioning - =(union VAR EXPR &key test at key)= :: Repeatedly insert into =VAR= the elements of the list =EXPR= that are not already present in =VAR=. @@ -3545,8 +3604,8 @@ Sequence accumulation commands are used to join lists (such as =union= and (finally-return var1 var2)) #+end_src -#+findex: vconcat -#+findex: vconcating +#+tindex: vconcat +#+tindex: vconcating - =(vconcat VAR EXPR &key at)= :: Repeatedly concatenate the value of =EXPR= onto =VAR= via the function ~vconcat~. For concatenating values into a string, see the command =concat=. @@ -3566,8 +3625,8 @@ Sequence accumulation commands are used to join lists (such as =union= and *** Other Accumulation Commands -#+findex: find -#+findex: finding +#+tindex: find +#+tindex: finding - =(find VAR EXPR TEST &key ON-FAILURE)= :: If the expression =TEST= is non-nil, then the loop stops and =VAR= is set to the value of =EXPR=. If =TEST= is never non-nil, then =VAR= is set to the value of =ON-FAILURE=, if provided. @@ -3640,21 +3699,22 @@ Implied accumulation variables are not required to always be in the correct order, so commands using such variables can produce more efficient code. #+begin_src emacs-lisp - ;; Similar in efficiency to the below: + ;; Similar in efficiency to the below `dotimes': + ;; ;; => (2 3 4 5 6 7 8 9 10 11 12 13 ...) - (loopy (list i (number-sequence 1 1000)) + (loopy (numbers i :to 1000) (collect (1+ i))) ;; => (2 3 4 5 6 7 8 9 10 11 12 13 ...) (let (result) - (dolist (i (number-sequence 1 1000)) + (dotimes (i 1000) (push (1+ i) result)) (nreverse result)) #+end_src The situation becomes more complex when commands place values at both sides of a -sequence. In that case, ~loopy~ keeps track of the beginning /and/ the end of -the sequence. ~loopy~ /does not/ merely append to the end of the accumulating +sequence. In that case, Loopy keeps track of the beginning /and/ the end of +the sequence. Loopy /does not/ merely append to the end of the accumulating list, since that would be much slower for large lists. #+begin_src emacs-lisp @@ -3677,8 +3737,8 @@ In such cases, ~loopy~ will naively optimize placing values at whichever side of the sequences appears to be more used. In the example below, note that even though the commands to insert values at the front of the list are never actually run, ~loopy~ will still optimize for frontal insertions. Here, ~loopy~ simply -counts that 2 commands seem to place values at the front of the list while only -1 command seems to place values at the end. +counts that 2 commands seem to place values at the front of the list and that +only 1 command seems to place values at the end. #+begin_src emacs-lisp ;; This code optimizes for insertions at the front of the list: @@ -3695,8 +3755,8 @@ optimizations ([[#macro-arguments]]). With it, you can (1) treat an explicit variable as if it were implicit and optionally (2) specify which side of a sequence you expect to use more. The arguments passed to =accum-opt= are either symbols (such as ~loopy-result~) or lists of a symbol and a position. To be -clear, use of the variable ~loopy-result~ is always at least naively optimized -in the manner described above. +clear, ~loopy-result~, when used as an implied variable, is always at least +naively optimized in the manner described above. In the example below, see that 1. Accumulation into the named variable ~coll~ has been explicitly optimized for @@ -3760,7 +3820,7 @@ create conflicting initial values for the implicit return value. #+END_QUOTE -#+findex: always +#+tindex: always - =(always [VAR] EXPR &key into)= :: Check the result of the condition =EXPR=. If the condition evaluates to ~nil~, end the loop. If the command was run, return the value of the condition via =VAR=. Otherwise, if the command was @@ -3802,7 +3862,7 @@ create conflicting initial values for the implicit return value. (always (and (> i 5) "hello")))) #+END_SRC -#+findex: never +#+tindex: never - =(never [VAR] EXPR &key into)= :: Check the condition =EXPR=. If the condition is ever non-~nil~, then the loop is exited and returns ~nil~ via =VAR=. Otherwise the loop returns ~t~ via =VAR=. @@ -3818,9 +3878,9 @@ create conflicting initial values for the implicit return value. #+attr_texinfo: :tag Note #+begin_quote Unlike the =always= command, =never= does not store any information in the - variable until it ends the loop. Therefore, =never= does not affect the - loop's implicit return value when using the =always= command so long as the - conditions of =never= are always ~nil~. + variable until it ends the loop. Therefore, so long as the conditions of + =never= are always ~nil~, =never= does not affect the loop's implicit return + value when using the =always= command. Be aware, though, that this behavior depends on =always= and =never= using the same variable. @@ -3843,7 +3903,7 @@ create conflicting initial values for the implicit return value. (never nil)) #+end_src -#+findex: thereis +#+tindex: thereis - =(thereis [VAR] EXPR &key into)= :: Check the result of the condition =EXPR=. If the condition evaluates to a non-~nil~ value, the loop returns that value via =VAR=. Otherwise, the loop returns ~nil~ via =VAR=. @@ -3878,17 +3938,22 @@ create conflicting initial values for the implicit return value. :DESCRIPTION: When to run loop commands. :END: -*** Conditionals +This section describes: +- how to conditionally execute loop commands +- how to skip loop cycles +- how to leave the loop early, with or without forcing a return value + +*** Conditional Execution :PROPERTIES: :CUSTOM_ID: conditionals :DESCRIPTION: Choosing if commands should run. :END: -Conditional commands in ~loopy~ can take multiple sub-commands, and work like -their Lisp counterparts. There is therefore no need for an =and= command as -used in ~cl-loop~. +{{{dfn(Conditional execution commands)}}} in ~loopy~ can take multiple +sub-commands, and work like their Lisp counterparts. There is therefore no need +for an =and= command as used in ~cl-loop~. -#+findex: cond +#+tindex: cond - =(cond [(EXPR CMDS) [...]])= :: Run the commands =CMDS= following the first non-nil condition =EXPR=. This is the ~loopy~ version of the ~cond~ special form from normal Emacs Lisp. @@ -3903,7 +3968,7 @@ used in ~cl-loop~. (finally-return evens odds not-numbers)) #+END_SRC -#+findex: if +#+tindex: if - =(if EXPR CMDS)= :: Run the first command if =EXPR= is non-nil. Otherwise, run the remaining commands. This is the ~loopy~ version of the ~if~ special form from normal Emacs Lisp. @@ -3918,7 +3983,7 @@ used in ~cl-loop~. (finally-return odds evens some-threes)) #+END_SRC -#+findex: when +#+tindex: when - =(when EXPR CMDS)= :: Run =CMDS= only if =EXPR= is non-nil. This is the ~loopy~ version of the ~when~ macro from normal Emacs Lisp. @@ -3933,7 +3998,7 @@ used in ~cl-loop~. (finally-return only-evens)) #+END_SRC -#+findex: unless +#+tindex: unless - =(unless EXPR CMDS)= :: Run =CMDS= only if =EXPR= is nil. This is the ~loopy~ version of the ~unless~ macro from normal Emacs Lisp. @@ -3954,10 +4019,15 @@ used in ~cl-loop~. :DESCRIPTION: Immediately beginning the next iteration. :END: -#+findex: skip -#+findex: continue -#+findex: skipping -#+findex: continuing +{{{dfn(Skip commands)}}} are used to skip iteration cycles in the loop, +immediately jumping to the next iteration. They are equivalent to the +~continue~ statement used by Python and other languages. For this reason, they +have aliases that use the word "continue" instead of the word "skip". + +#+tindex: skip +#+tindex: continue +#+tindex: skipping +#+tindex: continuing - =(skip|continue)= :: Skip the remaining commands and continue to the next loop iteration. @@ -3970,10 +4040,10 @@ used in ~cl-loop~. (collect i)) #+END_SRC -#+findex: skip-from -#+findex: continue-from -#+findex: skipping-from -#+findex: continuing-from +#+tindex: skip-from +#+tindex: continue-from +#+tindex: skipping-from +#+tindex: continuing-from - =(skip-from|continue-from NAME)= :: Skip the remaining commands and continue to the next loop iteration of the loop =NAME=. @@ -3996,20 +4066,20 @@ used in ~cl-loop~. :DESCRIPTION: Leaving the loop early, with or without returning values. :END: +{{{dfn(Early-exit commands)}}} are used to leave the loop before it completes. The loop is contained in a ~cl-block~, which can be exited by the function ~cl-return-from~. Indeed, the =return= and =return-from= commands described -below are just wrappers around that function. - -As with the =finally-return= special macro argument, passing multiple return -values to those commands will return a list of those values. If no value is -given, ~nil~ is returned. +below are just wrappers around that function. As with the =finally-return= +special macro argument, passing multiple return values to =return= and +=return-from= will return a list of those values. If no value is given, ~nil~ +is returned. In Loopy, implied accumulation variables can be modified a final time after the -loop exits in order to finalize their values. For example, if an accumulated -list is built in reverse, then it will be reversed into the correct order after -the loop completes. Loopy has "return" commands for (1) immediately returning a -value from the loop without finalizing values and "leave" commands for (2) -leaving the loop without forcing a return value, allowing values to be +loop exits in order to finalize their values. For example, if an optimized +accumulated list is built in reverse, then it will be reversed into the correct +order after the loop completes. Loopy has "return" commands for immediately +returning a value from the loop without finalizing values and "leave" commands +for leaving the loop without forcing a return value, allowing values to be finalized. #+begin_src emacs-lisp @@ -4055,7 +4125,7 @@ affect the return value of the loop. As noted in [[#macro-arguments]], the special macro argument =finally-return= overrides the return value of the loop, including values that would have been -returned by any =return= commands. +returned by any "return" commands. #+begin_src emacs-lisp ;; => 22 @@ -4075,8 +4145,8 @@ returned by any =return= commands. #+end_src -#+findex: leave -#+findex: leaving +#+tindex: leave +#+tindex: leaving - =(leave)= :: Leave the current loop without forcing a return value. This command also has the alias =leaving=. @@ -4089,10 +4159,10 @@ returned by any =return= commands. (collect i))) #+end_src -#+findex: leave-from -#+findex: leaving-from -- =(leave-from|leaving-from NAME)= :: Leave the loop =NAME= without forcing a - return value. This command is equivalent to =(at NAME (leave))= ([[#sub-loops]]). +#+tindex: leave-from +#+tindex: leaving-from +- =(leave-from NAME)= :: Leave the loop =NAME= without forcing a return value. + This command is equivalent to =(at NAME (leave))= ([[#sub-loops]]). This command also has the alias =leaving-from=. @@ -4107,8 +4177,8 @@ returned by any =return= commands. (collect i)) #+end_src -#+findex: return -#+findex: returning +#+tindex: return +#+tindex: returning - =(return [EXPRS])= :: Return from the current loop without finalizing values, returning =[EXPRS]=. @@ -4122,8 +4192,8 @@ returned by any =return= commands. (return j))) #+END_SRC -#+findex: return-from -#+findex: returning-from +#+tindex: return-from +#+tindex: returning-from - =(return-from NAME [EXPRS])= :: Return from the loop =NAME=, returning =[EXPRS]=. This command is equivalent to =(at NAME (return))= ([[#sub-loops]]). @@ -4138,9 +4208,10 @@ returned by any =return= commands. (return-from outer-loop 'bad-val?)))) #+END_SRC -#+findex: while +#+tindex: while - =(while COND)= :: Leave the loop once =COND= is false, without forcing a - return value. =(while COND)= is the same as =(until (not COND))=. + return value. =(while COND)= is the same as =(until (not COND))= + and =(unless COND (leave))=. #+begin_src emacs-lisp ;; => (1 2 3 4) @@ -4156,9 +4227,10 @@ returned by any =return= commands. (collect i)) #+end_src -#+findex: until +#+tindex: until - =(until COND)= :: Leave the loop once =COND= is true, without forcing a return - value. =(until COND)= is the same as =(while (not COND))=. + value. =(until COND)= is the same as =(while (not COND))= + and =(when COND (leave))=. #+begin_src emacs-lisp ;; => (1 2 3 4) @@ -4185,13 +4257,13 @@ Loopy provides two sets of commands for working with sub-loops: during the expansion of outer loops. 2. The =at= command, which controls the named outer loop that commands interact with. For example, it can control to which loop an implied accumulation - variable is scoped and so for which loop that variable is used as an implied - return value. + variable is scoped, which determines which loop uses that variable as an + implied return value. In the example below, the arguments of the =do= command are inserted into the -loop body literally, so by the time the inner loop expands, the outer loop has -already been expanded into normal Emacs Lisp code, and so the inner macro cannot -find any outer Loopy loop named "outer". +loop body literally, so by the time the inner loop expands, the outer loop might +have already been expanded into normal Emacs Lisp code, in which case the inner +macro would not find any outer Loopy loop named "outer". #+begin_src emacs-lisp ;; Can signal an error or not work as expected: @@ -4227,9 +4299,9 @@ used literally, and is not guaranteed to be able to affect macro expansion. The commands are described in more detail below. -#+findex: loopy command +#+tindex: loopy - =(loopy [SPECIAL-MACRO-ARGUMENTS or CMDS])= :: Use the ~loopy~ macro as a - command. + loop command. #+begin_src emacs-lisp ;; => (1 11 2 12 3 13 4 14) @@ -4243,9 +4315,9 @@ The commands are described in more detail below. (collect (+ j 10))))) #+end_src -#+findex: loopy-iter command +#+tindex: loopy-iter - =(loopy-iter [SPECIAL-MACRO-ARGUMENTS or CMDS or LISP-EXPRS])= :: Use the - ~loopy-iter~ macro as a command ([[#loopy-iter]]). + ~loopy-iter~ macro as a loop command ([[#loopy-iter]]). This feature can only be used after first loading the library =loopy-iter=. @@ -4264,10 +4336,11 @@ The commands are described in more detail below. (collecting (10+ j)))))) #+end_src -#+findex: at -- =(at LOOP-NAME [CMDS])= :: Parse commands with respect to =LOOP-NAME=. For - example, a =leave= subcommand would exit the loop =LOOP-NAME=, and an - accumulation command would create a variable in that super-loop. +#+tindex: at +- =(at LOOP-NAME [CMDS])= :: Parse commands with respect to the super-loop + =LOOP-NAME=. For example, a =leave= subcommand would exit the super-loop + =LOOP-NAME=, and an accumulation command would create a variable in that + super-loop. If one did not use =at= in the below example, then the accumulation would be local to the sub-loop and the return value of the loop =outer= would be ~nil~. @@ -4368,7 +4441,7 @@ macros. ~cl-symbol-macrolet~. Do not confuse this with the behavior of ~cl-letf~, which temporarily binds those places to a value. - This macro uses the destructuring found in the sequence reference iteration + This macro uses the destructuring found in the sequence-reference iteration commands ([[#sequence-reference-iteration]]). There are some limitations to this functionality in Emacs Lisp, which are described in that section. @@ -4407,14 +4480,18 @@ provided by Emacs). However, while ~loopy~ and ~loopy-iter~ were influenced by #+begin_src emacs-lisp (require 'loopy-iter) ; <- Must `require' to load feature. - ;; => ((1 2 3) (-3 -2 -1) (0)) - (loopy-iter (accum-opt positives negatives other) - (numbering i :from -3 :to 3) - (pcase i - ((pred cl-plusp) (collecting positives i)) - ((pred cl-minusp) (collecting negatives i)) - (_ (collecting other i))) - (finally-return positives negatives other)) + ;; => ((-9 -8 -7 -6 -5 -4 -3 -2 -1) + ;; (0) + ;; (1 2 3 4 5 6 7 8 9 10 11)) + (loopy-iter (accum-opt positives negatives zeroes) + (numbering i :from -10 :to 10) + ;; Normal `let' and `pcase', not Loopy constructs: + (let ((var (1+ i))) + (pcase var + ((pred cl-plusp) (collecting positives var)) + ((pred cl-minusp) (collecting negatives var)) + ((pred zerop) (collecting zeroes var)))) + (finally-return negatives zeroes positives)) ;; => (1 2 3) (loopy-iter (listing elem '(1 2 3)) @@ -4495,14 +4572,18 @@ This method recognizes all commands and their aliases in the user option #+caption: The first example, but now using keyword symbols. #+begin_src emacs-lisp - ;; => ((1 2 3) (-3 -2 -1) (0)) + ;; => ((-9 -8 -7 -6 -5 -4 -3 -2 -1) + ;; (0) + ;; (1 2 3 4 5 6 7 8 9 10 11)) (loopy-iter (arg accum-opt positives negatives other) - (for numbers i :from -3 :to 3) - (pcase i - ((pred cl-plusp) (accum collect positives i)) - ((pred cl-minusp) (accum collect negatives i)) - (_ (accum collect other i))) - (arg finally-return positives negatives other)) + (for numbers i :from -10 :to 10) + ;; Normal `let' and `pcase', not Loopy constructs: + (let ((var (1+ i))) + (pcase var + ((pred cl-plusp) (accum collect positives var)) + ((pred cl-minusp) (accum collect negatives var)) + ((pred zerop) (accum collect zeroes var)))) + (arg finally-return positives negatives zeroes)) #+end_src While the symbols =for=, =accum=, =exit=, and =arg= are named for iteration, @@ -4838,7 +4919,6 @@ and produce inefficient code. (finally-return whole temporary?)) #+end_src - * Custom Aliases :PROPERTIES: :CUSTOM_ID: custom-aliases @@ -5146,8 +5226,8 @@ A loop command has 7 main places to put code: #+vindex: loopy--pre-conditions - =loopy--pre-conditions= :: Expressions that determine if the =while= loop runs/continues, such as whether a list still has elements in it. If there is - more than one expression, than all expressions are used in an ~and~ special - form. + more than one expression, than all expressions are used in the Emacs Lisp + special form ~and~. #+vindex: loopy--main-body - =loopy--main-body= :: Expressions that make up the main body of the loop. The @@ -5314,9 +5394,11 @@ This is an example of a command that prints a message during the main loop body. To implement a custom loop-body command, Loopy needs two pieces of information: 1. The keyword that names your command -2. The parsing function that can turn uses of your command into instructions. +2. The parsing function that can transform occurences of your command into + instructions. -Importantly, your custom commands cannot share a name. +Importantly, as with Emacs Lisp functions, there can only be one command with a +given name. For example, say that you're tired of typing out =(do (message "Hello, %s %s" PERSONAL-NAME FAMILY-NAME))= and would prefer to instead use =(greet @@ -5325,6 +5407,7 @@ main body, so the definition of the parsing function is quite simple. #+BEGIN_SRC emacs-lisp (require 'cl-lib) + (require 'macroexp) (cl-defun my-loopy-greet-command-parser ((_ personal-name &optional family-name)) @@ -5475,9 +5558,9 @@ something like: The best way to satisfy the second requirement is to set the initial value of the accumulation variable to ~t~ and to make that accumulation variable the -implied return value of the loop. As described in [[#early-exit]], leaving a loopy -early (such as via the =leave= command) does not change the implied return value -of a loop. The logic of the command would be: +implied return value of the loop. As described in [[#exiting-the-loop-early]], +leaving a loopy early (such as via the =leave= command) does not change the +implied return value of a loop. The logic of the command would be: 1. Initialize the accumulation variable to ~t~. 2. During the loop, if the condition is ~nil~, immediately set the accumulation variable to ~nil~ and leave the loop. If the condition is always non-~nil~, @@ -5672,8 +5755,9 @@ loopy-commands RET)}}}. :END: ~loopy~ is a better version of ~cl-loop~, judging by the following: -- It as fast or faster than ~cl-loop~, though some optimizations are more - explicit. + +- It generally as fast or faster than ~cl-loop~, though some optimizations are + more explicit. - Loop commands are always evaluated in order. @@ -5700,7 +5784,7 @@ loopy-commands RET)}}}. collect (cons i j)) #+end_src -- Taking influence from CL's Iterate, several ~loopy~ commands are more +- Taking influence from Common Lisp's Iterate, several ~loopy~ commands are more featureful than their ~cl-loop~ counterparts. #+begin_src emacs-lisp @@ -5753,28 +5837,28 @@ For the commands operating on hash tables, see also the generic iteration command =map-pairs=, which works generically on hash tables, association lists ("alists"), property lists ("plists"), and vectors. -| ~cl-loop~ | ~loopy~ | -|-----------------------------------------------+-------------------------------------------------| -| =for VAR from EXPR1 to EXPR2 by EXPR3= | =(numbers VAR :from EXPR1 :to EXPR2 :by EXPR3)= | -| =for VAR in LIST [by FUNCTION]= | =(list VAR LIST :by FUNC)= | -| =for VAR on LIST [by FUNCTION]= | =(cons VAR VAL :by FUNC)= | -| =for VAR in-ref LIST by FUNCTION= | =(list-ref VAR LIST :by FUNC)= | -| =for VAR across ARRAY= | =(array VAR ARRAY)= | -| =for VAR across-ref ARRAY= | =(array-ref VAR ARRAY)= | -| =for VAR being the elements of SEQUENCE= | =(sequence VAR SEQUENCE)= | -| =for VAR being the elements of-ref SEQUENCE= | =(sequence-ref VAR SEQUENCE)= | -| =for VAR being the symbols [of OBARRAY]= | None so far. Use ~mapatoms~. | -| =for VAR being the hash-keys of HASH-TABLE= | =(list VAR (hash-table-keys HASH-TABLE))= | -| =for VAR being the hash-values of HASH-TABLE= | =(list VAR (hash-table-values HASH-TABLE))= | -| =for VAR being the key-codes of KEYMAP= | None so far. Use ~map-keymap~. | -| =for VAR being the key-bindings of KEYMAP= | None so far. Use ~map-keymap~. | -| =for VAR being the key-seqs of KEYMAP= | None so far. | -| =for VAR being the overlays [of BUFFER]= | None so far. Use ~overlay-lists~. | -| =for VAR being the intervals [of BUFFER]= | None so far. | -| =for VAR being the frames= | =(list VAR (frame-list))= | -| =for VAR being the windows [of FRAME]= | =(list VAR (window-list FRAME))= | -| =for VAR being the buffers= | =(list VAR (buffer-list))= | -| =for VAR = EXPR1 then EXPR2= | =(set VAR EXPR1 EXPR2)= | +| ~cl-loop~ | ~loopy~ | +|-----------------------------------------------+-----------------------------------------------------------------| +| =for VAR from EXPR1 to EXPR2 by EXPR3= | =(numbers VAR :from EXPR1 :to EXPR2 :by EXPR3)= | +| =for VAR in LIST [by FUNCTION]= | =(list VAR LIST :by FUNC)= | +| =for VAR on LIST [by FUNCTION]= | =(cons VAR VAL :by FUNC)= | +| =for VAR in-ref LIST by FUNCTION= | =(list-ref VAR LIST :by FUNC)= | +| =for VAR across ARRAY= | =(array VAR ARRAY)= | +| =for VAR across-ref ARRAY= | =(array-ref VAR ARRAY)= | +| =for VAR being the elements of SEQUENCE= | =(sequence VAR SEQUENCE)= | +| =for VAR being the elements of-ref SEQUENCE= | =(sequence-ref VAR SEQUENCE)= | +| =for VAR being the symbols [of OBARRAY]= | None so far. Use ~mapatoms~. | +| =for VAR being the hash-keys of HASH-TABLE= | =(list VAR (hash-table-keys HASH-TABLE))= | +| =for VAR being the hash-values of HASH-TABLE= | =(list VAR (hash-table-values HASH-TABLE))= | +| =for VAR being the key-codes of KEYMAP= | None so far. Use ~map-keymap~. | +| =for VAR being the key-bindings of KEYMAP= | None so far. Use ~map-keymap~. | +| =for VAR being the key-seqs of KEYMAP= | None so far. | +| =for VAR being the overlays [of BUFFER]= | =(list VAR (with-current-buffer BUFFER (car (overlay-lists))))= | +| =for VAR being the intervals [of BUFFER]= | None so far. | +| =for VAR being the frames= | =(list VAR (frame-list))= | +| =for VAR being the windows [of FRAME]= | =(list VAR (window-list FRAME))= | +| =for VAR being the buffers= | =(list VAR (buffer-list))= | +| =for VAR = EXPR1 then EXPR2= | =(set VAR EXPR1 EXPR2)= | ** Iteration Clauses :PROPERTIES: @@ -5840,19 +5924,29 @@ special form ~cond~. | =do EXPRS= | =(do EXPRS)= as a loop command | | =return EXPR= | =(return EXPR)= as a loop command | -* Macro Argument and Loop Command Index +* Index of Concepts :PROPERTIES: - :INDEX: fn + :INDEX: cp :END: -* Variable Index +* Index of Variables :PROPERTIES: :INDEX: vr :END: -* Concept Index +* Index of Functions and Macros :PROPERTIES: - :INDEX: cp + :INDEX: fn + :END: + +* Index of Special Macro Arguments + :PROPERTIES: + :INDEX: pg + :END: + +* Index of Loop Commands + :PROPERTIES: + :INDEX: tp :END: * Footnotes diff --git a/doc/loopy.texi b/doc/loopy.texi index 1556b0f8..a1a5a0ee 100644 --- a/doc/loopy.texi +++ b/doc/loopy.texi @@ -46,9 +46,11 @@ libraries @samp{seq} (@ref{Sequence Functions,,,elisp,}) and @samp{cl-lib} (@ref * Custom Commands:: Extending `loopy' with personal commands. * Comparing to @code{cl-loop}:: Why `loopy' instead of `cl-loop'. * Translating to and from @samp{cl-loop}:: Converting `cl-loop' to `loopy', and vice versa. -* Macro Argument and Loop Command Index:: -* Variable Index:: -* Concept Index:: +* Index of Concepts:: +* Index of Variables:: +* Index of Functions and Macros:: +* Index of Special Macro Arguments:: +* Index of Loop Commands:: @detailmenu --- The Detailed Node Listing --- @@ -82,7 +84,7 @@ Accumulation Control Flow -* Conditionals:: Choosing if commands should run. +* Conditional Execution:: Choosing if commands should run. * Skipping Cycles:: Immediately beginning the next iteration. * Early Exit:: Leaving the loop early, with or without returning values. @@ -115,6 +117,7 @@ Loopy is a library for looping and iteration, with supplemental features for destructuring. Upfront, the features provided are summarized below. They are described thoroughly later in this document. +@findex loopy @table @asis @item @code{loopy} A macro similar to @code{cl-loop}. Unlike @code{cl-loop}, @code{loopy} uses @@ -135,23 +138,34 @@ parenthetical forms instead of keyword ``clauses''. (collect odds i)) (finally-return odds evens)) @end lisp +@end table +@findex loopy-iter +@table @asis @item @code{loopy-iter} A macro similar to Common Lisp's Iterate macro (@ref{The @code{loopy-iter} Macro}). -Unlike Common Lisp's @code{loop}, the @code{iterate} macro allows the embedding of its +Unlike Common Lisp's @code{loop}, the @code{iterate} macro allows one to embed its looping constructs inside of arbitrary code. To be clear, @code{loopy-iter} is not a port of @code{iterate} to Emacs Lisp. @lisp ;; => ((1 2 3) (-3 -2 -1) (0)) (loopy-iter (numbering i -3 3) + ;; Normal `pcase' macro, not a `loopy' feature: (pcase i + ;; `collecting' is a `loopy' feature: ((pred cl-plusp) (collecting positives i)) ((pred cl-minusp) (collecting negatives i)) (_ (collecting other i))) (finally-return positives negatives other)) @end lisp +@end table +@findex loopy-let +@findex loopy-setq +@findex loopy-lambda +@findex loopy-ref +@table @asis @item @code{loopy-let*}, @code{loopy-setq}, @code{loopy-lambda}, and @code{loopy-ref} Destructuring macros that can be used outside of @code{loopy} and @code{loopy-iter} @@ -175,6 +189,19 @@ by @samp{cl-lib}. e 10) (list my-list my-vector))) @end lisp + +@item A @code{pcase} pattern +Loopy provides a @code{pcase} pattern, called @samp{loopy}, which +can be used in macros like @code{pcase-lambda} and @code{pcase-let}. Some of the +destructuring macros provided by Loopy are implemented using this @code{pcase} +macro. + +@lisp +;; => (1 2 3 (:k1 4) 4) +(pcase-let (((loopy (a b c &rest d &key k1)) + '(1 2 3 :k1 4))) + (list a b c d k1)) +@end lisp @end table @@ -250,8 +277,8 @@ and @code{while} loops. @item One can declare which accumulations variables should be optimized (and so treated as user-inaccessible during the loop). This can make accumulation -into multiple named variables much faster. As needed, can make Loopy optimize -for at-start accumulations or at-end accumulations. +into multiple named variables much faster. As needed, one can make Loopy +optimize for at-start accumulations or at-end accumulations. @lisp ;; Expands into the efficient `push'-`nreverse' idiom, not @@ -286,11 +313,12 @@ local to the loop, add code that runs before/after the loop, and/or set the ultimate return value of the macro. For convenience and clarity, expressions that generate code in the loop body are -called ``loop commands'' (@ref{Loop Commands}). Expressions that generate code around -the loop are called ``special macro arguments'' or just ``macro arguments'' as -opposed to ``loop commands'' (@ref{Special Macro Arguments}). +called @dfn{loop commands} (@ref{Loop Commands}). Expressions that generate code +around the loop are called @dfn{special macro arguments} or just +@dfn{macro arguments} as opposed to @dfn{loop commands} +(@ref{Special Macro Arguments}). -``Loop commands'' are the main building blocks of the @code{loopy} macro, such as the +Loop commands are the main building blocks of the @code{loopy} macro, such as the command @samp{list} in the expression @samp{(list i '(1 2 3))}. A command inserts code into the loop body, but can also perform additional setup like initializing variables. Many commands set a condition for ending the loop. In the case of @@ -300,10 +328,10 @@ elements, the loop is forced to end. In general, a loop ends when any looping condition required by a loop command evaluates to @code{nil}. If no conditions are needed, then the loop runs infinitely -until a early-exit command is reached (@ref{Early Exit, , Exiting the Loop Early}). +until an early-exit command is reached (@ref{Early Exit, , Exiting the Loop Early}). The default return value of the loop is @code{nil}. Other return values must be -stated explicitly, as in one of the early-exit commands or part of the +stated explicitly, as in one of the early-exit commands or as part of the @samp{finally-return} macro argument, or come from accumulating loop commands using an implied accumulation variable (@ref{Accumulation, , Accumulation Commands}). @@ -323,7 +351,11 @@ command, and will signal an error if that fails. These special macro arguments are always processed before loop commands, regardless of the order of the arguments passed to @code{loopy}. -@findex named +For clarity, some special macro arguments have multiple names. For example, the +special macro argument @samp{with} can also be written as @samp{init}, because the +argument @samp{with} is used to initialized variables. + +@pindex named @table @asis @item @samp{named} or just a symbol Name the loop. This also names the @code{cl-block} @@ -347,9 +379,9 @@ which contains the loop. This can be of the form @samp{(named NAME)} or just @end lisp @end table -@findex with -@findex let* -@findex init +@pindex with +@pindex let* +@pindex init @table @asis @item @samp{with}, @samp{let*}, @samp{init} Declare variables before the loop, in order. This @@ -363,19 +395,12 @@ can use destructuring (@ref{Basic Destructuring}). (b (1+ a))) ; Set `b' to 1+1=2. (list i '(1 2 3)) ; Bind `i' to elements of the list. (collect (+ i a b))) ; Collect sum of `a', `b', and each `i' into a list. - -;; => 16 -(loopy - (let* (my-sum 10)) ; Bind `my-sum' to 10. - (list i '(1 2 3)) ; Bind `i' to elements of the list. - (sum my-sum i) ; Set `my-sum' to `i' + `my-sum'. - (finally-return my-sum)) ; Return the value of `my-sum'. @end lisp @end table -@findex without -@findex no-with -@findex no-init +@pindex without +@pindex no-with +@pindex no-init @table @asis @item @samp{without}, @samp{no-with}, @samp{no-init} Variables that @code{loopy} should not try to @@ -384,7 +409,7 @@ in a @code{let}-like form, but that isn’t always desired. @lisp ;; Without `without', `loopy' would try to initialize `a' to nil, which would -;; overwrite the value of 5 above. +;; shadow the variable `a' bound by the `let' form. ;; => (5 4 3 2 1) (let ((a 5)) @@ -392,20 +417,13 @@ in a @code{let}-like form, but that isn’t always desired. (until (zerop a)) ; Leave loop when `a' equals 0. (collect a) ; Collect the value of `a' into a list. (set a (1- a)))) ; Set `a' to the value of `(1- a)'. - -;; => (5 4 3 2 1) -(let ((a 5)) - (loopy (no-init a) - (while (not (zerop a))) - (collect a) - (set a (1- a)))) @end lisp @end table -@findex before -@findex before-do -@findex initially-do -@findex initially +@pindex before +@pindex before-do +@pindex initially-do +@pindex initially @table @asis @item @samp{before-do}, @samp{before}, @samp{initially-do}, @samp{initially} Run Lisp expressions @@ -418,20 +436,13 @@ before the loop starts, after variables are initialized. (cl-incf b)) ; Add 1 to `b'. (list i '(1 2 3)) ; Set `i' to each element in the list. (collect (+ i a b))) ; Collect each sum into a list. - -;; => (1 2 3) -(loopy (with (a 1)) - ;; Message before the loop starts: - (initially (message "Starting loop...")) - (list i '(1 2 3)) - (collect i)) @end lisp @end table -@findex after-do -@findex after -@findex else-do -@findex else +@pindex after-do +@pindex after +@pindex else-do +@pindex else @table @asis @item @samp{after-do}, @samp{after}, @samp{else-do}, @samp{else} Run Lisp expressions after the loop @@ -440,30 +451,18 @@ following a @code{for} or @code{while} loop. Unlike @code{progn}, the return va expressions do not affect the return value of the macro. @lisp -;; Messages that no odd number was found: -;; => nil -(loopy (list i '(2 4 6 8)) - (when (cl-oddp i) - (do (message "Odd number found.")) - (return t)) ; Make the loop return `t'. - (after-do - (message "No odd number found.") - ;; The macro already return `nil' by default, - ;; but one can still use `cl-return' to be more explicit. - (cl-return nil))) - ;; Messages that an odd number was found: ;; => t (loopy (list i '(2 4 5 8)) (when (cl-oddp i) (do (message "Odd number found.")) (return t)) - (else (message "No odd number found."))) + (after-do (message "No odd number found."))) @end lisp @end table -@findex finally-do -@findex finally +@pindex finally-do +@pindex finally @table @asis @item @samp{finally-do}, @samp{finally} Run Lisp expressions after the loop exits, always. @@ -486,7 +485,7 @@ return value of the macro. @end lisp @end table -@findex finally-return +@pindex finally-return @table @asis @item @samp{finally-return} Return a value, regardless of how the loop completes. @@ -507,8 +506,8 @@ Specifying multiple values is the same as returning a list of those values. @end lisp @end table -@findex finally-protect -@findex finally-protected +@pindex finally-protect +@pindex finally-protected @table @asis @item @samp{finally-protect}, @samp{finally-protected} Wrap the loop in @code{unwind-protect} @@ -540,8 +539,8 @@ unplanned stops of the loop's execution. @end lisp @end table -@findex flag -@findex flags +@pindex flag +@pindex flags @table @asis @item @samp{flag}, @samp{flags} Options that change the behavior of @code{loopy} (@ref{Using Flags}). @@ -570,8 +569,8 @@ information. @end lisp @end table -@findex accum-opt -@findex opt-accum +@pindex accum-opt +@pindex opt-accum @table @asis @item @samp{accum-opt}, @samp{opt-accum} Accumulation variables whose use should be @@ -602,7 +601,7 @@ accumulations. @end table -@findex wrap +@pindex wrap @table @asis @item @samp{wrap} A list of forms in which to wrap the loop itself (that is, not @@ -705,7 +704,7 @@ You should keep in mind that commands are evaluated in order. This means that attempting something like the below example might not do what you expect, as @samp{i} is assigned a value from the list after collecting @samp{i} into @samp{coll}. -@float Listing,org3151e6b +@float Listing,org18d32d9 @lisp ;; => (nil 1 2) (loopy (collect coll i) @@ -716,7 +715,7 @@ is assigned a value from the list after collecting @samp{i} into @samp{coll}. @end float For convenience and understanding, the same command might have multiple names, -called @dfn{aliases}. Similary, the @samp{array} command has the alias +called @dfn{aliases}. For example, the @samp{array} command has the alias @samp{string}, because the @samp{array} command can be used to iterate through the elements of an array or string@footnote{Strings being a kind of array. See @ref{Sequences Arrays Vectors,,,elisp,} for more.}. You can define custom aliases using the @@ -753,10 +752,11 @@ such as in @samp{array|string}. @samp{VAR} is an unquoted symbol that will be used as a variable name, such as @samp{i} in @samp{(list i my-list)}. @item -@samp{FUNC} is a quoted Lisp function name, such as @code{#'my-func} or @code{'my-func}, a -variable whose value is a function, or a @code{lambda} expression. +@samp{FUNC} is an expression that evaluates to a function or symbol naming a +function, such as @code{#'my-func}, @code{'my-func}, a variable whose value is a +function, or a @code{lambda} expression. @item -@samp{NAME} is an unquoted name of a loop (or, more accurately, of a @samp{cl-block}). +@samp{NAME} is an unquoted name of a loop (or, more accurately, of a @code{cl-block}). @item @samp{EXPR} is a single Lisp expression, such as @samp{(+ 1 2)}, @samp{'(1 2 3)}, @samp{my-var}, or @samp{(some-function my-var)}. @samp{EXPRS} means multiple expressions. Really, we @@ -874,6 +874,7 @@ commands, such as @samp{set} and @samp{list}. Destructuring in accumulation com (@ref{Sequence Reference Iteration}) works slightly differently, and is described more in those sections. +@cindex pcase pattern loopy In addition to what can be done in loop commands, several features are available for using Loopy's destructuring outside of @code{loopy} loops (@ref{Destructuring Macros}), including the @code{pcase} pattern @samp{loopy}. @@ -887,7 +888,7 @@ the flag @samp{dash} provided by the package @samp{loopy-dash}. Below are two examples of destructuring in @code{cl-loop} and @code{loopy}. -@float Listing,org7bf92c8 +@float Listing,org4212921 @lisp ;; => (1 2 3 4) (cl-loop for (i . j) in '((1 . 2) (3 . 4)) @@ -902,7 +903,7 @@ Below are two examples of destructuring in @code{cl-loop} and @code{loopy}. @caption{Destructuring values in a list.} @end float -@float Listing,org0853253 +@float Listing,org8ea6554 @lisp ;; => (1 2 3 4) (cl-loop for elem in '((1 . 2) (3 . 4)) @@ -921,35 +922,75 @@ Below are two examples of destructuring in @code{cl-loop} and @code{loopy}. You can use destructured assignment by passing an unquoted sequence of symbols -as the @samp{VAR} argument of a loop command. Loopy supports destructuring lists and -arrays (which includes strings and vectors). +as the @samp{VAR} argument of a loop command. Loopy supports destructuring lists, +arrays (which includes strings and vectors), and generic sequences as defined by +the @samp{seq.el} library. + @itemize @item To destructure lists, use a list, as in @samp{(a b c)}. + @item To destructure arrays, use a vector, as in @samp{[a b c]}. + @item To destructure sequences generically using @samp{seq.el} (mainly via @code{seq-elt} and @code{seq-drop}), use a vector or a list whose first element is @samp{&seq}, as in @samp{[&seq a b c]} and @samp{(&seq a b c)}. @end itemize -This sequence of symbols can be shorter than the destructured sequence, @emph{but not -longer}. If shorter, the unassigned elements of the destructured sequence are -simply ignored. + +The sequence of symbols can receive less values than there are in the sequence, +@emph{but not more}. If less, the remainder of the sequence is simply ignored. If +more, than an error is signalled. Note that this differs from @code{seq-let}, which +can bind unfound values to @code{nil} in some cases, as seen below. + +@lisp +;; Ignores third element of the vector: +;; +;; => ((1 4) (2 5)) +(loopy (list [a b] '([1 2 3] [4 5 6])) + (collect as a) + (collect bs b) + (finally-return as bs)) + +;; Signals an error: +(loopy (list [a b c] '([1 2] [3 4])) + (collect as a) + (collect bs b) + (collect cs c) + (finally-return as bs cs)) + +;; Does not signal an error: +;; +;; (1 2 nil) +(seq-let (a b c) + [1 2] + (list a b c)) + +;; Signals an error: +(loopy (list (&seq a b c) '([1 2] [3 4])) + (collect as a) + (collect bs b) + (collect cs c) + (finally-return as bs cs)) +@end lisp The content of this destructuring sequence is similar to @samp{cl-lib}, and is @example -POSITIONAL-VARIABLES -&optional OPTIONAL-VARIABLES -&rest REST-VARIABLE -&key KEY-VARIABLES [&allow-other-keys] -&map MAP-VARIABLES -&aux AUXILLIARY-VARIABLES +[&seq] +[&whole WHOLE-VARIABLE] +[POSITIONAL-VARIABLES] +[&optional OPTIONAL-VARIABLES] +[&rest REST-VARIABLE] +[&key KEY-VARIABLES [&allow-other-keys]] +[&map MAP-VARIABLES] +[&aux AUXILLIARY-VARIABLES] @end example -in which at least one of the above constructs must be provided. +in which at least one of the above constructs that binds variables must be +provided. @lisp ;; => (1 2 3 @@ -1060,7 +1101,7 @@ When destructuring lists, one can also use dotted notation, as in a CL @code{lambda} list. These variables can themselves be sequences to be further destructured. -When used after optional values, the @samp{&rest} value is the subsequence starting +When used after optional values, the @samp{&rest} value is the sub-sequence starting at the index after any possible optional values, even when those optional values are not actually present. If the sequence is not long enough, then the sub-sequence is empty. @@ -1337,7 +1378,7 @@ sequence @samp{(KEY VAR)} or @samp{[KEY VAR]}, in which @samp{VAR} itself can be a sequence @item -@samp{(VAR)} or @samp{[VAR]}, in which @samp{VAR} is a symbol +@samp{(VAR)} or @samp{[VAR]}, in which @samp{VAR} must be a symbol @item a symbol @samp{VAR} @@ -1411,8 +1452,8 @@ and @code{seq-drop}. This form is less efficient than destructuring a sequence an array or as a list, when applicable. Sequences destructured using @samp{&seq} can still use @samp{&whole}, @samp{&optional}, -@samp{&rest}, and @samp{&map}. However, lists destructured using @samp{&seq} cannot be -destructured using @samp{&key}. +@samp{&rest}, and @samp{&map}. However, note that because the type of the destructured +sequence is not known ahead of time, @samp{&key} cannot be used with @samp{&seq}. @lisp ;; => ((0 1 2 nil nil) @@ -1427,19 +1468,19 @@ destructured using @samp{&key}. @section Generic Evaluation These generic commands are for settings values and running sub-commands or -sub-expressions during the loop. These commands do not affect macro's return -value and do no affect how the loop iterates. +sub-expressions during the loop. These commands do not affect the macro's +return value and do no affect how the loop iterates. -@findex do +@tindex do @table @asis @item @samp{(do EXPRS)} Evaluate multiple Lisp expressions, like a @samp{progn}. You cannot include arbitrary code in the loop body in @code{loopy}. Trying to do so will result in errors, as the macro will attempt to interpret such code as -a command. +a loop command. -To use loopy commands in arbitrary code, use the macro @code{loopy-iter} instead +To use loop commands in arbitrary code, use the macro @code{loopy-iter} instead (@ref{The @code{loopy-iter} Macro}). @lisp @@ -1448,7 +1489,7 @@ To use loopy commands in arbitrary code, use the macro @code{loopy-iter} instead @end lisp @end table -@findex command-do +@tindex command-do @table @asis @item @samp{(command-do [CMDS])} Evaluate multiple loop commands, as if in a @@ -1471,11 +1512,12 @@ expressions. Currently, this command is only useful when used within the @end lisp @end table -@findex set -@findex setting +@tindex set +@tindex setting @table @asis @item @samp{(set VAR EXPR [EXPRS])} -Bind @samp{VAR} to each @samp{EXPR} in order. Once the last +Bind @samp{VAR} to each @samp{EXPR}, in order. In the n-th +cycle of the loop, bind @samp{VAR} to the n-th @samp{EXPR}. Once the last @samp{EXPR} is reached, it is used repeatedly for the rest of the loop. This command also has the aliases @samp{setting}. @@ -1501,9 +1543,9 @@ argument @samp{without} (@ref{Special Macro Arguments}). @end lisp @end table -@findex set-prev -@findex setting-prev -@findex prev-set +@tindex set-prev +@tindex setting-prev +@tindex prev-set @table @asis @item @samp{(set-prev VAR VAL &key back)} Bind @samp{VAR} to a value @samp{VAL} from a previous @@ -1562,14 +1604,14 @@ This command also has the aliases @samp{setting-prev} and, for typo tolerance, @node Iteration @section Iteration -Iteration commands bind local variables and determine when the loop ends. If no -command sets an ending condition, then the loop runs forever. Infinite loops -can be exited by using early-exit commands (@ref{Early Exit}) or boolean -commands (@ref{Checking Conditions}). +@dfn{Iteration commands} bind local variables and determine when the loop +ends. If no command sets an ending condition, then the loop runs forever. +Infinite loops can be exited by using early-exit commands +(@ref{Early Exit}) or boolean commands (@ref{Checking Conditions}). -Iteration commands must occur in the top level of the @code{loopy} form or in a -sub-loop command (@ref{Sub-Loops}). Using them elsewhere and trying to do something -like the below example will signal an error. +Iteration commands must occur in the top level of the macro or in the top level +of a sub-loop command (@ref{Sub-Loops}). Using them elsewhere and trying to do +something like the below example will signal an error. @lisp ;; Signals an error: @@ -1692,10 +1734,10 @@ loop. This restriction allows for producing more efficient code. @node Generic Iteration @subsection Generic Iteration -@findex cycle -@findex cycling -@findex repeat -@findex repeating +@tindex cycle +@tindex cycling +@tindex repeat +@tindex repeating @table @asis @item @samp{(cycle|repeat [VAR] EXPR)} Run the loop for @samp{EXPR} iterations. @@ -1737,8 +1779,8 @@ This command also has the aliases @samp{cycling} and @samp{repeating}. @end lisp @end table -@findex iter -@findex iterating +@tindex iter +@tindex iterating @table @asis @item @samp{(iter [VAR] EXPR &key close yield-result)} Iterate through the values @@ -1843,14 +1885,14 @@ values. One option for working around this is to use the generic command @node Numeric Iteration @subsection Numeric Iteration -For iterating through numbers, there is the general @samp{numbers} command, and its -variants @samp{numbers-up} and @samp{numbers-down}. +For iterating through numbers, there is the @samp{numbers} command and its +more limited variants @samp{numbers-up} and @samp{numbers-down}. -@findex num -@findex nums -@findex number -@findex numbering -@findex numbers +@tindex num +@tindex nums +@tindex number +@tindex numbering +@tindex numbers @table @asis @item @samp{(numbers|nums VAR &key KEYS)} Iterate through numbers. @samp{KEYS} is one or @@ -1913,8 +1955,8 @@ end. To specify the step size, one can use the keyword @samp{:by}. Except when @samp{:test} is given, the value for @samp{:by} must be positive. Other keyword arguments -(@samp{:upfrom}, @samp{:downfrom}, @samp{:upto}, @samp{:downto}, @samp{:above}, and @samp{:below}) control -whether the variable is incremented or decremented. +(that is, @samp{:upfrom}, @samp{:downfrom}, @samp{:upto}, @samp{:downto}, @samp{:above}, and @samp{:below}) +control whether the variable is incremented or decremented. @lisp ;; => (1 3 5) @@ -1936,8 +1978,8 @@ whether the value should be increasing or decreasing when using the @samp{:by} keyword, one can use the keywords @samp{:downfrom}, @samp{:downto}, @samp{:upfrom}, @samp{:upto}, @samp{:above}, and @samp{:below}. The keywords @samp{:from} and @samp{:to} don't by themselves specify a direction, and they can be used without conflict with the keyword -arguments that do. Using arguments that contradict one another will signal -an error. +arguments that do specify a direction. Using arguments that contradict one +another will signal an error. @lisp ;; => (3 2 1) @@ -1994,7 +2036,7 @@ a non-nil value if the loop should continue, such as @code{#'<=}. The function receives @samp{VAR} as the first argument and the final value as the second argument, as in @code{(funcall TEST VAR FINAL-VAL)}. @samp{test} can only be used with @samp{from} and @samp{to}; it cannot be used with keywords that already describe a -direction and ending condition. To match the behavior of @code{cl-loop}, the +direction and an ending condition. To match the behavior of @code{cl-loop}, the default testing function is @code{#'<=}. When @samp{test} is given, @samp{by} can be negative. As there is no default end value when @samp{test} is given, @samp{to} must also be given. @@ -2028,8 +2070,9 @@ If you prefer using positional arguments to keyword arguments, you can use the commands @samp{numbers-up} and @samp{numbers-down} to specify directions. These commands are simple wrappers of the above @samp{numbers} command. -@findex numbers-down -@findex numbering-down +@tindex nums-down +@tindex numbers-down +@tindex numbering-down @table @asis @item @samp{(numbers-down|nums-down VAR START [END] &key by)} Equivalent to @samp{(numbers @@ -2048,8 +2091,9 @@ This command also has the aliases @samp{numsdown} and @samp{numbering-down}. @end lisp @end table -@findex numbers-up -@findex numbering-up +@tindex nums-up +@tindex numbers-up +@tindex numbering-up @table @asis @item @samp{(numbers-up|nums-up VAR START [END] &key by)} Equivalent to @samp{(numbers VAR @@ -2137,10 +2181,10 @@ source sequences. @end lisp -@findex array -@findex string -@findex arraying -@findex stringing +@tindex array +@tindex string +@tindex arraying +@tindex stringing @table @asis @item @samp{(array|string VAR EXPR [EXPRS] &key KEYS)} Loop through the @@ -2150,9 +2194,9 @@ elements are characters. This command also has the aliases @samp{arraying} and @samp{stringing}. @samp{KEYS} is one or several of @samp{from}, @samp{upfrom}, @samp{downfrom}, @samp{to}, @samp{upto}, -@samp{downto}, @samp{above}, @samp{below}, @samp{by}, and @samp{index}. @samp{index} names the variable -used to store the index being accessed. For others, see the @samp{numbers} -command. +@samp{downto}, @samp{above}, @samp{below}, @samp{by}, @samp{test}, and @samp{index}. @samp{index} names the +variable used to store the index being accessed. For the others, see the +@samp{numbers} command. If multiple arrays are given, then the elements of these arrays are distributed into an array of lists. In that case, the above keywords apply to @@ -2182,9 +2226,9 @@ this new, resulting array of lists. @end lisp @end table -@findex cons -@findex conses -@findex consing +@tindex cons +@tindex conses +@tindex consing @table @asis @item @samp{(cons|conses VAR EXPR &key by)} Loop through the cons cells of @samp{EXPR}. @@ -2205,13 +2249,13 @@ This command also has the alias @samp{consing}. @end lisp @end table -@findex list -@findex listing -@findex each +@tindex list +@tindex listing +@tindex each @table @asis @item @samp{(list|each VAR EXPR [EXPRS] &key by)} Loop through each element of the -list @samp{EXPR}. Optionally, update the list using @samp{by} instead of @samp{cdr}. +list @samp{EXPR}. Optionally, move through the list using @samp{by} instead of @samp{cdr}. This command also has the alias @samp{listing}. @@ -2238,16 +2282,16 @@ command. @end lisp @end table -@findex map -@findex map-pairs -@findex mapping -@findex mapping-pairs +@tindex map +@tindex map-pairs +@tindex mapping +@tindex mapping-pairs @table @asis @item @samp{(map|map-pairs VAR EXPR &key unique)} Iterate through the dotted key-value pairs of map @samp{EXPR}, using the function @code{map-pairs} from the @samp{map.el} library. This library generalizes working with association lists (``alists''), property -lists (``plists''), hash-tables, and vectors. +lists (``plists''), hash tables, and vectors. This command also has the aliases @samp{mapping} and @samp{mapping-pairs}. @@ -2302,8 +2346,8 @@ very large maps. @end lisp Depending on how a map is created, a map might contain a key multiple times. -Currently, the function @code{map-pairs} returns such keys. By default, the -@code{loopy} command @samp{map-pairs} ignores such duplicate keys. This is for two +Currently, the function @code{map-pairs} returns such keys. However, by default, +the @code{loopy} command @samp{map-pairs} ignores such duplicate keys. This is for two reasons: @enumerate @item @@ -2340,8 +2384,8 @@ Again, this can be disabled by setting @samp{unique} to nil. @end lisp @end table -@findex sequence -@findex sequencing +@tindex sequence +@tindex sequencing @table @asis @item @samp{(sequence VAR EXPR [EXPRS] &key KEYS)} Loop through the sequence @@ -2407,15 +2451,15 @@ resulting sequence of distributed elements. @end lisp @end table -@findex seq -@findex seqing +@tindex seq +@tindex seqing @table @asis @item @samp{(seq VAR EXPR [EXPRS] &key KEYS)} -For generic a sequence implementing the -features of the library @samp{seq.el}, loop through the generic sequence @samp{EXPR}, -binding @samp{VAR} to the elements of the sequence. Because it is more generic, -@samp{seq} can be slower than the @samp{sequence} command, which in turn is somewhat -less efficient than the @samp{list} and @samp{array} commands. +For a generic sequence which implements +the features of the library @samp{seq.el}, loop through the generic sequence +@samp{EXPR}, binding @samp{VAR} to the elements of the sequence. Because it is more +generic, @samp{seq} can be slower than the @samp{sequence} command, which in turn is +somewhat less efficient than the @samp{list} and @samp{array} commands. If multiple generic sequences are given, then these keyword arguments apply to the resulting generic sequence of distributed elements. @@ -2452,8 +2496,8 @@ infinite sequences, consider using the @samp{stream} command. @end lisp @end table -@findex stream -@findex streaming +@tindex stream +@tindex streaming @table @asis @item @samp{(stream VAR EXPR &key by)} Iterate through the elements for the stream @@ -2476,8 +2520,8 @@ This command also has the alias @samp{streaming}. @end lisp @end table -@findex substream -@findex substreaming +@tindex substream +@tindex substreaming @table @asis @item @samp{(substream VAR EXPR &key by length)} Iterate through the sub-streams of @@ -2555,16 +2599,16 @@ same keywords as the @samp{numbers} command (@ref{Numeric Iteration}) for workin the index and choosing a range of the sequence elements through which to iterate. -@findex sequence-index -@findex sequencing-index -@findex seq-index -@findex seqing-index -@findex array-index -@findex arraying-index -@findex list-index -@findex listing-index -@findex string-index -@findex stringing-index +@tindex sequence-index +@tindex sequencing-index +@tindex seq-index +@tindex seqing-index +@tindex array-index +@tindex arraying-index +@tindex list-index +@tindex listing-index +@tindex string-index +@tindex stringing-index @table @asis @item @samp{(sequence-index VAR EXPR &key KEYS)} Iterate through the indices of @samp{EXPR}. @@ -2680,10 +2724,10 @@ sequence elements through which to iterate. In addition to those keywords, they also have an @samp{index} keyword, which names the variable used to store the accessed index during the loop. -@findex array-ref -@findex string-ref -@findex arraying-ref -@findex stringing-ref +@tindex array-ref +@tindex string-ref +@tindex arraying-ref +@tindex stringing-ref @table @asis @item @samp{(array-ref|string-ref VAR EXPR &key KEYS)} Loop through the elements of @@ -2717,8 +2761,8 @@ command. @end lisp @end table -@findex list-ref -@findex listing-ref +@tindex list-ref +@tindex listing-ref @table @asis @item @samp{(list-ref VAR EXPR &key by)} Loop through the elements of the list @samp{EXPR}, @@ -2754,8 +2798,8 @@ This command also has the aliases @samp{listing-ref}. @end lisp @end table -@findex map-ref -@findex mapping-ref +@tindex map-ref +@tindex mapping-ref @table @asis @item @samp{(map-ref VAR EXPR &key key unique)} Loop through the values of map @samp{EXPR}, @@ -2811,8 +2855,8 @@ duplicate's value. @end lisp @end table -@findex sequence-ref -@findex sequencing-ref +@tindex sequence-ref +@tindex sequencing-ref @table @asis @item @samp{(sequence-ref VAR EXPR &key KEYS)} Loop through the elements of the @@ -2846,8 +2890,8 @@ variable used to store the index being accessed. For others, see the @end lisp @end table -@findex seq-ref -@findex seqing-ref +@tindex seq-ref +@tindex seqing-ref @table @asis @item @samp{(seq-ref VAR EXPR &key KEYS)} Loop through the elements of the generic @@ -2899,9 +2943,9 @@ detect when to leave the loop, it does not work with infinite sequences. @node Accumulation @section Accumulation -Accumulation commands are used to accumulate or aggregate values into a -variable, such as creating a list of values or summing the elements in a -sequence. +@dfn{Accumulation commands} are used to accumulate or aggregate values into +a variable, such as creating a list of return values or summing the elements of +a sequence. Unlike iteration commands, you can refer to the same accumulation variable in multiple accumulation commands if needed. @@ -2953,15 +2997,17 @@ be overridden by using the the @samp{return} and @samp{return-from} loop command @lisp ;; => (1 2 3) -(cl-loop for i from 1 to 3 collect i) +(cl-loop for i from 1 to 3 + collect i) ;; => (1 2 3) -(loopy (numbers i :from 1 :to 3) (collect i)) +(loopy (numbers i :from 1 :to 3) + (collect i)) @end lisp @vindex loopy-result Unlike @code{cl-loop}, Loopy uses a default accumulation variable, which is named -@code{loop-result}. This variable can be used in the @samp{after-do}, @samp{finally-do}, and +@code{loopy-result}. This variable can be used in the @samp{after-do}, @samp{finally-do}, and @samp{finally-return} special macro arguments. @lisp @@ -3074,13 +3120,13 @@ conflict, use the @samp{with} special macro argument, as noted above. (finally-return my-accum)) @end lisp -By default, one must specify separate accumulation variables to be able to -accumulate into separate values. This can make accumulation slower, because -@code{loopy} ensures that named accumulation variables (excluding the previously -mentioned @code{loopy-result}) have the correct value during the loop. For example, -@code{loopy} will construct named accumulation variables containing lists in the -correct order, instead of using the more efficient @code{push}-@code{nreverse} idiom. -This behavior can be disabled by optimizing accumulations using the @samp{accum-opt} +One must specify separate accumulation variables to be able to accumulate into +separate values. This can make accumulation slower, because @code{loopy} ensures +that named accumulation variables (excluding the previously mentioned +@code{loopy-result}) have the correct value during the loop. For example, @code{loopy} +will construct named accumulation variables containing lists in the correct +order, instead of using the more efficient @code{push}-@code{nreverse} idiom. This +behavior can be disabled by optimizing accumulations using the @samp{accum-opt} special macro argument (@ref{Optimizing Accumulations}). Below are examples of an optimized accumulation and an un-optimized @@ -3159,10 +3205,10 @@ is more complex and uses a slower way of building the accumulated list. @end lisp @quotation Warning -You should not try to access implied (or optimized) accumulation results (for -example, @code{loopy-result}) while the loop is running. Implied results are only -required to be correct after the loop ends (before code in @samp{else-do} is run), -allowing for more efficient code. +In general, you should not try to access implied (or optimized) accumulation +results (for example, @code{loopy-result}) while the loop is running. Implied +results are only required to be correct after the loop ends (before code in +@samp{else-do} is run), allowing for more efficient code. Furthermore, because using a @samp{return} or @samp{return-from} command overrides implied return values, using these commands can prevent implied accumulation results @@ -3219,7 +3265,7 @@ need not be quoted. An alternative way to specify the variable into which to accumulate values. One would normally just give @samp{VAR} as the first argument of the loop command, but if you wish, you can use this keyword -argument for a more @code{cl-loop}-like syntax. +argument for a syntax more like @code{cl-loop}. As all accumulation commands support this keyword, it is not listed in any command definition. @@ -3250,7 +3296,8 @@ closer to the optional @samp{testfn} argument used by @samp{seq} (for example, i @code{seq-contains-p}). There are two important differences: @enumerate @item -Tests default to @code{equal}, like in other Emacs Lisp libraries, not @code{eql}. +The default test function is @code{equal}, like in other Emacs Lisp libraries, +not @code{eql}. @item The first argument is the existing value or sequence and the second argument is the tested value. This is the @emph{opposite} of the order used by @@ -3278,9 +3325,10 @@ argument is the tested value. This is the @emph{opposite} of the order used by @table @asis @item @samp{key} A one-argument function that transforms both the tested value and -the value from sequence used by the @samp{test} keyword. +the elements of the sequence passed to the function given by the @samp{test} +keyword. -The keyword @samp{key} is useful to avoid applying a transforming function to the +The keyword @samp{key} is useful to avoid applying a transformation to the tested value more than once when searching through a long sequence, as would be done if it were called explicitly in @samp{test}. @@ -3303,16 +3351,17 @@ be done if it were called explicitly in @samp{test}. @end table -The arguments to the @samp{test} and @samp{key} parameters can be quoted functions or -variables, just like when using @code{cl-union}, @code{cl-adjoin}, and so on. @code{loopy} -knows how to expand efficiently for either case. +The arguments to the @samp{test} and @samp{key} parameters can be literal quoted functions +or expressions which evaluate to functions, just like when using @code{cl-union}, +@code{cl-adjoin}, and so on. @code{loopy} knows how to expand efficiently for either +case. @node Generic Accumulation @subsection Generic Accumulation Generic accumulation commands are more explicit uses of the accumulation variable. They are very similar to updating a variable's value -using the @samp{set} command and exist for situation not covered by the other +using the @samp{set} command and exist for situations not covered by the other accumulation commands. @itemize @@ -3329,8 +3378,8 @@ accumulate and (2) the accumulation variable, in that order. The commands are described in more detail below. -@findex reduce -@findex reducing +@tindex reduce +@tindex reducing @table @asis @item @samp{(reduce VAR EXPR FUNC)} Reduce @samp{EXPR} into @samp{VAR} by @samp{FUNC}, like in @@ -3389,8 +3438,8 @@ order. @end lisp @end table -@findex accumulate -@findex accumulating +@tindex accumulate +@tindex accumulating @table @asis @item @samp{(accumulate|accumulating VAR EXPR FUNC)} Accumulate the result of applying @@ -3435,8 +3484,8 @@ quoted. This alias is intended to help users remember argument order. @end lisp @end table -@findex set-accum -@findex setting-accum +@tindex set-accum +@tindex setting-accum @table @asis @item @samp{(set-accum VAR EXPR)} Set the accumulation variable @samp{VAR} to the @@ -3478,8 +3527,8 @@ unsafe. Numeric accumulation work on numbers, such as by repeatedly adding or multiplying values together. -@findex count -@findex counting +@tindex count +@tindex counting @table @asis @item @samp{(count VAR EXPR)} Count the number of times that @samp{EXPR} evaluates to a @@ -3495,10 +3544,10 @@ This command also has the alias @samp{counting}. @end lisp @end table -@findex max -@findex maxing -@findex maximize -@findex maximizing +@tindex max +@tindex maxing +@tindex maximize +@tindex maximizing @table @asis @item @samp{(maximize|max VAR EXPR)} Repeatedly set @samp{VAR} to the greater of the values @@ -3515,10 +3564,10 @@ This command also has the aliases @samp{maximizing} and @samp{maxing}. @end lisp @end table -@findex min -@findex minimize -@findex minnning -@findex minimizing +@tindex min +@tindex minimize +@tindex minnning +@tindex minimizing @table @asis @item @samp{(minimize|min VAR EXPR)} Repeatedly set @samp{VAR} to the lesser of the values @@ -3535,8 +3584,8 @@ This command also has the aliases @samp{minimizing} and @samp{minning}. @end lisp @end table -@findex multiply -@findex multiplying +@tindex multiply +@tindex multiplying @table @asis @item @samp{(multiply VAR EXPR)} Repeatedly set @samp{VAR} to the product of @@ -3552,8 +3601,8 @@ This command also has the alias @samp{multiplying}. @end lisp @end table -@findex sum -@findex summing +@tindex sum +@tindex summing @table @asis @item @samp{(sum VAR EXPR)} Repeatedly set @samp{VAR} to the sum of the values of @samp{EXPR} @@ -3575,8 +3624,8 @@ This command also has the alias @samp{summing}. Sequence accumulation commands are used to join lists (such as @samp{union} and @samp{append}) and to collect items into lists (such as @samp{collect} and @samp{adjoin}). -@findex adjoin -@findex adjoining +@tindex adjoin +@tindex adjoining @table @asis @item @samp{(adjoin VAR EXPR &key at test key)} Repeatedly add @samp{EXPR} to @samp{VAR} if it @@ -3587,28 +3636,31 @@ This command also has the alias @samp{adjoining}. Unlike @code{cl-adjoin} and like the other accumulation commands, this command defaults to adjoining @samp{EXPR} to the end of @samp{VAR}, not the beginning. +Unlike Common Lisp's Iterate, Loopy's @samp{adjoin} command intentionally does +not provide a @samp{:result-type} keyword. Instead, manipulate the accumulation +variable (by default, @code{loopy-result}) directly, such as with @code{seq-into}. + @lisp ;; => ((1 . 1) (1 . 2) (2 . 3)) (loopy (list i '((1 . 1) (1 . 2) (1 . 2) (2 . 3))) (adjoin i)) -;; Coerced to a vector /after/ the loop ends. +;; No `:result-type', use `seq-into' on `loopy-result': +;; ;; => [1 2 3 4] (loopy (list i '(1 2 3 3 4)) - (adjoin my-var i) - (when (vectorp my-var) - (return 'is-vector)) - (finally-return my-var)) + (adjoin i) + (finally-return (seq-into loopy-result 'vector))) -;; => [4 3 2 1] +;; => (4 3 2 1) (loopy (list i '(1 2 3 3 4)) (adjoin my-var i :at 'start) (finally-return my-var)) @end lisp @end table -@findex append -@findex appending +@tindex append +@tindex appending @table @asis @item @samp{(append VAR EXPR &key at)} Repeatedly concatenate @samp{EXPR} to @samp{VAR}, as if @@ -3628,8 +3680,8 @@ This command also has the alias @samp{appending}. @end lisp @end table -@findex collect -@findex collecting +@tindex collect +@tindex collecting @table @asis @item @samp{(collect VAR EXPR &key at)} Collect the value of @samp{EXPR} into @@ -3637,6 +3689,11 @@ the list @samp{VAR}. By default, elements are added to the end of the list. This command also has the alias @samp{collecting}. +Unlike Common Lisp's Iterate, Loopy's @samp{collect} command intentionally does +not provide a @samp{:result-type} keyword. Instead, manipulate the accumulation +variable (by default, @code{loopy-result}) directly, such as with @code{seq-into} or +@code{cl-coerce}. + @lisp ;; => '(1 2 3) (loopy (list i '(1 2 3)) @@ -3665,8 +3722,8 @@ This command also has the alias @samp{collecting}. @end lisp @end table -@findex concat -@findex concating +@tindex concat +@tindex concating @table @asis @item @samp{(concat VAR EXPR &key at)} Repeatedly @code{concat} the value of @samp{EXPR} onto @@ -3688,8 +3745,8 @@ This command also has the alias @samp{concating}. @end lisp @end table -@findex nconc -@findex nconcing +@tindex nconc +@tindex nconcing @table @asis @item @samp{(nconc VAR EXPR &key at)} Repeatedly and @emph{destructively} concatenate the @@ -3719,8 +3776,8 @@ apply wherever that value is used (@ref{Self-Evaluating Forms,,,elisp,}). @end lisp @end table -@findex nunion -@findex nunioning +@tindex nunion +@tindex nunioning @table @asis @item @samp{(nunion VAR EXPR &key test at key)} Repeatedly and @emph{destructively} insert @@ -3747,8 +3804,8 @@ This command also has the alias @samp{nunioning}. @end lisp @end table -@findex prepend -@findex prepending +@tindex prepend +@tindex prepending @table @asis @item @samp{(prepend VAR EXPR)} Repeatedly concatenate @samp{EXPR} onto the front of @samp{VAR}, @@ -3774,10 +3831,10 @@ convenience. @end lisp @end table -@findex push -@findex pushing -@findex push-into -@findex pushing-into +@tindex push +@tindex pushing +@tindex push-into +@tindex pushing-into @table @asis @item @samp{(push-into|push VAR EXPR)} Collect the value of @samp{EXPR} into a list, adding @@ -3797,8 +3854,8 @@ convenience. @end lisp @end table -@findex union -@findex unioning +@tindex union +@tindex unioning @table @asis @item @samp{(union VAR EXPR &key test at key)} Repeatedly insert into @samp{VAR} the @@ -3825,8 +3882,8 @@ This command also has the alias @samp{unioning}. @end lisp @end table -@findex vconcat -@findex vconcating +@tindex vconcat +@tindex vconcating @table @asis @item @samp{(vconcat VAR EXPR &key at)} Repeatedly concatenate the value of @samp{EXPR} @@ -3850,8 +3907,8 @@ This command also has the alias @samp{vconcating}. @node Other Accumulation Commands @subsection Other Accumulation Commands -@findex find -@findex finding +@tindex find +@tindex finding @table @asis @item @samp{(find VAR EXPR TEST &key ON-FAILURE)} If the expression @samp{TEST} is non-nil, @@ -3924,21 +3981,22 @@ Implied accumulation variables are not required to always be in the correct order, so commands using such variables can produce more efficient code. @lisp -;; Similar in efficiency to the below: +;; Similar in efficiency to the below `dotimes': +;; ;; => (2 3 4 5 6 7 8 9 10 11 12 13 ...) -(loopy (list i (number-sequence 1 1000)) +(loopy (numbers i :to 1000) (collect (1+ i))) ;; => (2 3 4 5 6 7 8 9 10 11 12 13 ...) (let (result) - (dolist (i (number-sequence 1 1000)) + (dotimes (i 1000) (push (1+ i) result)) (nreverse result)) @end lisp The situation becomes more complex when commands place values at both sides of a -sequence. In that case, @code{loopy} keeps track of the beginning @emph{and} the end of -the sequence. @code{loopy} @emph{does not} merely append to the end of the accumulating +sequence. In that case, Loopy keeps track of the beginning @emph{and} the end of +the sequence. Loopy @emph{does not} merely append to the end of the accumulating list, since that would be much slower for large lists. @lisp @@ -3961,8 +4019,8 @@ In such cases, @code{loopy} will naively optimize placing values at whichever si the sequences appears to be more used. In the example below, note that even though the commands to insert values at the front of the list are never actually run, @code{loopy} will still optimize for frontal insertions. Here, @code{loopy} simply -counts that 2 commands seem to place values at the front of the list while only -1 command seems to place values at the end. +counts that 2 commands seem to place values at the front of the list and that +only 1 command seems to place values at the end. @lisp ;; This code optimizes for insertions at the front of the list: @@ -3979,8 +4037,8 @@ optimizations (@ref{Special Macro Arguments}). With it, you can (1) treat an ex variable as if it were implicit and optionally (2) specify which side of a sequence you expect to use more. The arguments passed to @samp{accum-opt} are either symbols (such as @code{loopy-result}) or lists of a symbol and a position. To be -clear, use of the variable @code{loopy-result} is always at least naively optimized -in the manner described above. +clear, @code{loopy-result}, when used as an implied variable, is always at least +naively optimized in the manner described above. In the example below, see that @enumerate @@ -4045,7 +4103,7 @@ create conflicting initial values for the implicit return value. @end quotation -@findex always +@tindex always @table @asis @item @samp{(always [VAR] EXPR &key into)} Check the result of the condition @samp{EXPR}. @@ -4096,7 +4154,7 @@ remain @code{t}. (always (and (> i 5) "hello")))) @end lisp -@findex never +@tindex never @table @asis @item @samp{(never [VAR] EXPR &key into)} Check the condition @samp{EXPR}. If the @@ -4119,9 +4177,9 @@ and the loop is exited. @quotation Note Unlike the @samp{always} command, @samp{never} does not store any information in the -variable until it ends the loop. Therefore, @samp{never} does not affect the -loop's implicit return value when using the @samp{always} command so long as the -conditions of @samp{never} are always @code{nil}. +variable until it ends the loop. Therefore, so long as the conditions of +@samp{never} are always @code{nil}, @samp{never} does not affect the loop's implicit return +value when using the @samp{always} command. Be aware, though, that this behavior depends on @samp{always} and @samp{never} using the same variable. @@ -4145,7 +4203,7 @@ the same variable. (never nil)) @end lisp -@findex thereis +@tindex thereis @table @asis @item @samp{(thereis [VAR] EXPR &key into)} Check the result of the condition @samp{EXPR}. @@ -4184,20 +4242,30 @@ If the value of the variable is non-@code{nil}, the loop exits. @node Control Flow @section Control Flow +This section describes: +@itemize +@item +how to conditionally execute loop commands +@item +how to skip loop cycles +@item +how to leave the loop early, with or without forcing a return value +@end itemize + @menu -* Conditionals:: Choosing if commands should run. +* Conditional Execution:: Choosing if commands should run. * Skipping Cycles:: Immediately beginning the next iteration. * Early Exit:: Leaving the loop early, with or without returning values. @end menu -@node Conditionals -@subsection Conditionals +@node Conditional Execution +@subsection Conditional Execution -Conditional commands in @code{loopy} can take multiple sub-commands, and work like -their Lisp counterparts. There is therefore no need for an @samp{and} command as -used in @code{cl-loop}. +@dfn{Conditional execution commands} in @code{loopy} can take multiple +sub-commands, and work like their Lisp counterparts. There is therefore no need +for an @samp{and} command as used in @code{cl-loop}. -@findex cond +@tindex cond @table @asis @item @samp{(cond [(EXPR CMDS) [...]])} Run the commands @samp{CMDS} following the first @@ -4215,7 +4283,7 @@ form from normal Emacs Lisp. @end lisp @end table -@findex if +@tindex if @table @asis @item @samp{(if EXPR CMDS)} Run the first command if @samp{EXPR} is non-nil. Otherwise, @@ -4233,7 +4301,7 @@ form from normal Emacs Lisp. @end lisp @end table -@findex when +@tindex when @table @asis @item @samp{(when EXPR CMDS)} Run @samp{CMDS} only if @samp{EXPR} is non-nil. This is the @@ -4251,7 +4319,7 @@ Run @samp{CMDS} only if @samp{EXPR} is non-nil. This is the @end lisp @end table -@findex unless +@tindex unless @table @asis @item @samp{(unless EXPR CMDS)} Run @samp{CMDS} only if @samp{EXPR} is nil. This is the @code{loopy} @@ -4272,10 +4340,15 @@ version of the @code{unless} macro from normal Emacs Lisp. @node Skipping Cycles @subsection Skipping Cycles -@findex skip -@findex continue -@findex skipping -@findex continuing +@dfn{Skip commands} are used to skip iteration cycles in the loop, +immediately jumping to the next iteration. They are equivalent to the +@code{continue} statement used by Python and other languages. For this reason, they +have aliases that use the word ``continue'' instead of the word ``skip''. + +@tindex skip +@tindex continue +@tindex skipping +@tindex continuing @table @asis @item @samp{(skip|continue)} Skip the remaining commands and continue to the next loop @@ -4291,10 +4364,10 @@ This command also has the aliases @samp{skipping} and @samp{continuing}. @end lisp @end table -@findex skip-from -@findex continue-from -@findex skipping-from -@findex continuing-from +@tindex skip-from +@tindex continue-from +@tindex skipping-from +@tindex continuing-from @table @asis @item @samp{(skip-from|continue-from NAME)} Skip the remaining commands and continue @@ -4316,20 +4389,20 @@ This command also has the aliases @samp{skipping-from} and @samp{continuing-from @node Early Exit @subsection Early Exit +@dfn{Early-exit commands} are used to leave the loop before it completes. The loop is contained in a @code{cl-block}, which can be exited by the function @code{cl-return-from}. Indeed, the @samp{return} and @samp{return-from} commands described -below are just wrappers around that function. - -As with the @samp{finally-return} special macro argument, passing multiple return -values to those commands will return a list of those values. If no value is -given, @code{nil} is returned. +below are just wrappers around that function. As with the @samp{finally-return} +special macro argument, passing multiple return values to @samp{return} and +@samp{return-from} will return a list of those values. If no value is given, @code{nil} +is returned. In Loopy, implied accumulation variables can be modified a final time after the -loop exits in order to finalize their values. For example, if an accumulated -list is built in reverse, then it will be reversed into the correct order after -the loop completes. Loopy has ``return'' commands for (1) immediately returning a -value from the loop without finalizing values and ``leave'' commands for (2) -leaving the loop without forcing a return value, allowing values to be +loop exits in order to finalize their values. For example, if an optimized +accumulated list is built in reverse, then it will be reversed into the correct +order after the loop completes. Loopy has ``return'' commands for immediately +returning a value from the loop without finalizing values and ``leave'' commands +for leaving the loop without forcing a return value, allowing values to be finalized. @lisp @@ -4375,7 +4448,7 @@ affect the return value of the loop. As noted in @ref{Special Macro Arguments}, the special macro argument @samp{finally-return} overrides the return value of the loop, including values that would have been -returned by any @samp{return} commands. +returned by any ``return'' commands. @lisp ;; => 22 @@ -4395,8 +4468,8 @@ returned by any @samp{return} commands. @end lisp -@findex leave -@findex leaving +@tindex leave +@tindex leaving @table @asis @item @samp{(leave)} Leave the current loop without forcing a return value. @@ -4412,12 +4485,12 @@ This command also has the alias @samp{leaving}. @end lisp @end table -@findex leave-from -@findex leaving-from +@tindex leave-from +@tindex leaving-from @table @asis -@item @samp{(leave-from|leaving-from NAME)} -Leave the loop @samp{NAME} without forcing a -return value. This command is equivalent to @samp{(at NAME (leave))} (@ref{Sub-Loops}). +@item @samp{(leave-from NAME)} +Leave the loop @samp{NAME} without forcing a return value. +This command is equivalent to @samp{(at NAME (leave))} (@ref{Sub-Loops}). This command also has the alias @samp{leaving-from}. @@ -4433,8 +4506,8 @@ This command also has the alias @samp{leaving-from}. @end lisp @end table -@findex return -@findex returning +@tindex return +@tindex returning @table @asis @item @samp{(return [EXPRS])} Return from the current loop without finalizing values, @@ -4451,8 +4524,8 @@ This command also has the alias @samp{returning}. @end lisp @end table -@findex return-from -@findex returning-from +@tindex return-from +@tindex returning-from @table @asis @item @samp{(return-from NAME [EXPRS])} Return from the loop @samp{NAME}, returning @@ -4470,11 +4543,12 @@ This command also has the alias @samp{returning-from}. @end lisp @end table -@findex while +@tindex while @table @asis @item @samp{(while COND)} Leave the loop once @samp{COND} is false, without forcing a -return value. @samp{(while COND)} is the same as @samp{(until (not COND))}. +return value. @samp{(while COND)} is the same as @samp{(until (not COND))} +and @samp{(unless COND (leave))}. @lisp ;; => (1 2 3 4) @@ -4491,11 +4565,12 @@ return value. @samp{(while COND)} is the same as @samp{(until (not COND))}. @end lisp @end table -@findex until +@tindex until @table @asis @item @samp{(until COND)} Leave the loop once @samp{COND} is true, without forcing a return -value. @samp{(until COND)} is the same as @samp{(while (not COND))}. +value. @samp{(until COND)} is the same as @samp{(while (not COND))} +and @samp{(when COND (leave))}. @lisp ;; => (1 2 3 4) @@ -4523,14 +4598,14 @@ during the expansion of outer loops. @item The @samp{at} command, which controls the named outer loop that commands interact with. For example, it can control to which loop an implied accumulation -variable is scoped and so for which loop that variable is used as an implied -return value. +variable is scoped, which determines which loop uses that variable as an +implied return value. @end enumerate In the example below, the arguments of the @samp{do} command are inserted into the -loop body literally, so by the time the inner loop expands, the outer loop has -already been expanded into normal Emacs Lisp code, and so the inner macro cannot -find any outer Loopy loop named ``outer''. +loop body literally, so by the time the inner loop expands, the outer loop might +have already been expanded into normal Emacs Lisp code, in which case the inner +macro would not find any outer Loopy loop named ``outer''. @lisp ;; Can signal an error or not work as expected: @@ -4566,11 +4641,11 @@ used literally, and is not guaranteed to be able to affect macro expansion. The commands are described in more detail below. -@findex loopy command +@tindex loopy @table @asis @item @samp{(loopy [SPECIAL-MACRO-ARGUMENTS or CMDS])} Use the @code{loopy} macro as a -command. +loop command. @lisp ;; => (1 11 2 12 3 13 4 14) @@ -4585,11 +4660,11 @@ command. @end lisp @end table -@findex loopy-iter command +@tindex loopy-iter @table @asis @item @samp{(loopy-iter [SPECIAL-MACRO-ARGUMENTS or CMDS or LISP-EXPRS])} Use the -@code{loopy-iter} macro as a command (@ref{The @code{loopy-iter} Macro}). +@code{loopy-iter} macro as a loop command (@ref{The @code{loopy-iter} Macro}). This feature can only be used after first loading the library @samp{loopy-iter}. @@ -4609,12 +4684,13 @@ This feature can only be used after first loading the library @samp{loopy-iter}. @end lisp @end table -@findex at +@tindex at @table @asis @item @samp{(at LOOP-NAME [CMDS])} -Parse commands with respect to @samp{LOOP-NAME}. For -example, a @samp{leave} subcommand would exit the loop @samp{LOOP-NAME}, and an -accumulation command would create a variable in that super-loop. +Parse commands with respect to the super-loop +@samp{LOOP-NAME}. For example, a @samp{leave} subcommand would exit the super-loop +@samp{LOOP-NAME}, and an accumulation command would create a variable in that +super-loop. If one did not use @samp{at} in the below example, then the accumulation would be local to the sub-loop and the return value of the loop @samp{outer} would be @code{nil}. @@ -4724,7 +4800,7 @@ Create destructured references to the fields in a sequence via @code{cl-symbol-macrolet}. Do not confuse this with the behavior of @code{cl-letf}, which temporarily binds those places to a value. -This macro uses the destructuring found in the sequence reference iteration +This macro uses the destructuring found in the sequence-reference iteration commands (@ref{Sequence Reference Iteration}). There are some limitations to this functionality in Emacs Lisp, which are described in that section. @@ -4760,14 +4836,18 @@ provided by Emacs). However, while @code{loopy} and @code{loopy-iter} were infl @lisp (require 'loopy-iter) ; <- Must `require' to load feature. -;; => ((1 2 3) (-3 -2 -1) (0)) -(loopy-iter (accum-opt positives negatives other) - (numbering i :from -3 :to 3) - (pcase i - ((pred cl-plusp) (collecting positives i)) - ((pred cl-minusp) (collecting negatives i)) - (_ (collecting other i))) - (finally-return positives negatives other)) +;; => ((-9 -8 -7 -6 -5 -4 -3 -2 -1) +;; (0) +;; (1 2 3 4 5 6 7 8 9 10 11)) +(loopy-iter (accum-opt positives negatives zeroes) + (numbering i :from -10 :to 10) + ;; Normal `let' and `pcase', not Loopy constructs: + (let ((var (1+ i))) + (pcase var + ((pred cl-plusp) (collecting positives var)) + ((pred cl-minusp) (collecting negatives var)) + ((pred zerop) (collecting zeroes var)))) + (finally-return negatives zeroes positives)) ;; => (1 2 3) (loopy-iter (listing elem '(1 2 3)) @@ -4850,16 +4930,20 @@ using the @code{let*} special form. This method recognizes all commands and their aliases in the user option @code{loopy-aliases}. -@float Listing,org0bc3841 +@float Listing,orge784fba @lisp -;; => ((1 2 3) (-3 -2 -1) (0)) +;; => ((-9 -8 -7 -6 -5 -4 -3 -2 -1) +;; (0) +;; (1 2 3 4 5 6 7 8 9 10 11)) (loopy-iter (arg accum-opt positives negatives other) - (for numbers i :from -3 :to 3) - (pcase i - ((pred cl-plusp) (accum collect positives i)) - ((pred cl-minusp) (accum collect negatives i)) - (_ (accum collect other i))) - (arg finally-return positives negatives other)) + (for numbers i :from -10 :to 10) + ;; Normal `let' and `pcase', not Loopy constructs: + (let ((var (1+ i))) + (pcase var + ((pred cl-plusp) (accum collect positives var)) + ((pred cl-minusp) (accum collect negatives var)) + ((pred zerop) (accum collect zeroes var)))) + (arg finally-return positives negatives zeroes)) @end lisp @caption{The first example, but now using keyword symbols.} @end float @@ -5682,8 +5766,8 @@ variables needed for destructuring for said command. @item @samp{loopy--pre-conditions} Expressions that determine if the @samp{while} loop runs/continues, such as whether a list still has elements in it. If there is -more than one expression, than all expressions are used in an @code{and} special -form. +more than one expression, than all expressions are used in the Emacs Lisp +special form @code{and}. @end table @vindex loopy--main-body @@ -5891,10 +5975,12 @@ To implement a custom loop-body command, Loopy needs two pieces of information: @item The keyword that names your command @item -The parsing function that can turn uses of your command into instructions. +The parsing function that can transform occurences of your command into +instructions. @end enumerate -Importantly, your custom commands cannot share a name. +Importantly, as with Emacs Lisp functions, there can only be one command with a +given name. For example, say that you're tired of typing out @samp{(do (message "Hello, %s %s" PERSONAL-NAME FAMILY-NAME))} and would prefer to instead use @samp{(greet @@ -5903,6 +5989,7 @@ main body, so the definition of the parsing function is quite simple. @lisp (require 'cl-lib) +(require 'macroexp) (cl-defun my-loopy-greet-command-parser ((_ personal-name &optional family-name)) @@ -5943,7 +6030,7 @@ After that, you can use your custom command in the loop body. (greet (car name) (cadr name))) @end lisp -By running @kbd{M-x pp-macroexpand-last-sexp RET} on the above expression, +By running @kbd{M-x pp-macroexpand-last-sexp @key{RET}} on the above expression, you can see that it expands to do what we want, as expected. @lisp @@ -6058,9 +6145,9 @@ something like: The best way to satisfy the second requirement is to set the initial value of the accumulation variable to @code{t} and to make that accumulation variable the -implied return value of the loop. As described in [BROKEN LINK: early-exit], leaving a loopy -early (such as via the @samp{leave} command) does not change the implied return value -of a loop. The logic of the command would be: +implied return value of the loop. As described in @ref{Early Exit}, +leaving a loopy early (such as via the @samp{leave} command) does not change the +implied return value of a loop. The logic of the command would be: @enumerate @item Initialize the accumulation variable to @code{t}. @@ -6251,16 +6338,17 @@ These alternative command parsers are listed in If you would like to see more examples, consider reading through the source code of @file{loopy-commands.el}, which contains the code of all of the built-in -loop commands. You can easily find this file using @kbd{M-x find-library loopy-commands RET}. +loop commands. You can easily find this file using @kbd{M-x find-library loopy-commands @key{RET}}. @node Comparing to @code{cl-loop} @chapter Comparing to @code{cl-loop} @code{loopy} is a better version of @code{cl-loop}, judging by the following: + @itemize @item -It as fast or faster than @code{cl-loop}, though some optimizations are more -explicit. +It generally as fast or faster than @code{cl-loop}, though some optimizations are +more explicit. @item Loop commands are always evaluated in order. @@ -6291,7 +6379,7 @@ the author) clearer naming scheme than @code{cl-loop}'s @samp{for} clauses. @end lisp @item -Taking influence from CL's Iterate, several @code{loopy} commands are more +Taking influence from Common Lisp's Iterate, several @code{loopy} commands are more featureful than their @code{cl-loop} counterparts. @lisp @@ -6357,7 +6445,7 @@ For the commands operating on hash tables, see also the generic iteration command @samp{map-pairs}, which works generically on hash tables, association lists (``alists''), property lists (``plists''), and vectors. -@multitable {aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa} {aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa} +@multitable {aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa} {aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa} @headitem @code{cl-loop} @tab @code{loopy} @item @samp{for VAR from EXPR1 to EXPR2 by EXPR3} @@ -6389,7 +6477,7 @@ command @samp{map-pairs}, which works generically on hash tables, association li @item @samp{for VAR being the key-seqs of KEYMAP} @tab None so far. @item @samp{for VAR being the overlays [of BUFFER]} -@tab None so far. Use @code{overlay-lists}. +@tab @samp{(list VAR (with-current-buffer BUFFER (car (overlay-lists))))} @item @samp{for VAR being the intervals [of BUFFER]} @tab None so far. @item @samp{for VAR being the frames} @@ -6492,19 +6580,29 @@ special form @code{cond}. @tab @samp{(return EXPR)} as a loop command @end multitable -@node Macro Argument and Loop Command Index -@chapter Macro Argument and Loop Command Index +@node Index of Concepts +@chapter Index of Concepts -@printindex fn +@printindex cp -@node Variable Index -@chapter Variable Index +@node Index of Variables +@chapter Index of Variables @printindex vr -@node Concept Index -@chapter Concept Index +@node Index of Functions and Macros +@chapter Index of Functions and Macros -@printindex cp +@printindex fn + +@node Index of Special Macro Arguments +@chapter Index of Special Macro Arguments + +@printindex pg + +@node Index of Loop Commands +@chapter Index of Loop Commands + +@printindex tp @bye