eb9bf5de0279

Add (if|when|unless)-first-(time|iteration) sugar macros
[view raw] [browse files]
author Steve Losh <steve@stevelosh.com>
date Mon, 12 Jul 2021 22:03:01 -0400
parents 8a7cfee11dea
children 55c796d79111
branches/tags (none)
files DOCUMENTATION.markdown src/iterate.lisp src/package.lisp

Changes

--- a/DOCUMENTATION.markdown	Mon May 24 01:18:26 2021 -0400
+++ b/DOCUMENTATION.markdown	Mon Jul 12 22:03:01 2021 -0400
@@ -455,6 +455,28 @@
 
 Perform `body` `n` times.
 
+### `DO-VECTOR` (macro)
+
+    (DO-VECTOR (VAR-OR-VARS VECTOR)
+      &BODY
+      BODY)
+
+Iterate over `vector`, performing `body` with `var-or-vars` bound.
+
+  `var-or-vars` can be one of the following:
+
+  * `value-symbol`
+  * `(value-symbol)`
+  * `(index-symbol value-symbol)`
+
+  Successive elements of `vector` will be bound to `value-symbol` while `body`
+  is executed.  If `index-symbol` is given, the current index will be bound to
+  it.
+
+  Returns `nil`.
+
+  
+
 ### `GATHERING` (macro)
 
     (GATHERING
@@ -1112,9 +1134,11 @@
 
 Graph `data` with gnuplot using `commands`.
 
-  `data` must be an alist of `(identifier . data)` pairs.  `identifier` must be
-  a string of the form `$foo`.  `data` must be a sequence of sequences of data
-  or a 2D array of data.
+  `data` must be an alist of `(identifier . data)` pairs.
+
+  Each `identifier` must be a string of the form `$foo`.  Each `data` must be
+  one of the following: a sequence of sequences of data points, an alist of data
+  points, or a 2D array of data points.
 
   `commands` must be a string or a sequence of strings.
 
@@ -1138,7 +1162,8 @@
 
   `identifier` must be a string of the form `$foo`.
 
-  `data` must be a sequence of sequences of data or a 2D array of data.
+  `data` must be one of the following: a sequence of sequences of data points,
+  an alist of data points, or a 2D array of data points.
 
   Must be called from inside `with-gnuplot`.
 
@@ -1154,9 +1179,21 @@
 
   
 
+### `PLOT` (function)
+
+    (PLOT DATA &KEY (STYLE :LINESPOINTS) (FILE plot.pdf))
+
+Plot `data` with gnuplot.
+
+  Convenience wrapper around the gnuplot functions.  This is only intended for
+  REPL-driven experimentation — if you want any customization you should use the
+  gnuplot interface instead.
+
+  
+
 ### `WITH-GNUPLOT` (macro)
 
-    (WITH-GNUPLOT
+    (WITH-GNUPLOT OPTIONS
       &BODY
       BODY)
 
@@ -1442,6 +1479,35 @@
 
 Custom `iterate` drivers and clauses.
 
+### `IF-FIRST-ITERATION` (macro)
+
+    (IF-FIRST-ITERATION THEN ELSE)
+
+Evaluate `then` if this clause is executed on the first iteration, otherwise `else`.
+
+  This is similar to from iterate's built-in `if-first-time`, but slightly different:
+
+  * `if-first-time` evaluates `then` the first time the clause is evaluated,
+    even if that happens on a subsequent iteration.
+  * `if-first-iteration` evaluates `then` only if the clause is evaluated on
+    the first iteration.
+
+  Example:
+
+    (iterate
+      (for i :from 1 :to 4)
+      (collect (cons i (when (evenp i)
+                         (list
+                           (if-first-time :first-time :later-time)
+                           (if-first-iteration :first-iter :later-iter))))))
+    ; =>
+    ; ((1)
+    ;  (2 :FIRST-TIME :LATER-ITER)
+    ;  (3)
+    ;  (4 :LATER-TIME :LATER-ITER))
+
+  in that it will only evaluate `then` on the first iteration of the loop, 
+
 ### `MACROEXPAND-ITERATE` (function)
 
     (MACROEXPAND-ITERATE CLAUSE)
@@ -1469,6 +1535,30 @@
 
   
 
+### `UNLESS-FIRST-ITERATION` (macro)
+
+    (UNLESS-FIRST-ITERATION EXPR)
+
+Sugar for `(if-first-iteration nil expr)`.
+
+### `UNLESS-FIRST-TIME` (macro)
+
+    (UNLESS-FIRST-TIME EXPR)
+
+Sugar for `(if-first-time nil expr)`.
+
+### `WHEN-FIRST-ITERATION` (macro)
+
+    (WHEN-FIRST-ITERATION EXPR)
+
+Sugar for `(if-first-iteration expr nil)`.
+
+### `WHEN-FIRST-TIME` (macro)
+
+    (WHEN-FIRST-TIME EXPR)
+
+Sugar for `(if-first-time expr nil)`.
+
 ## Package `LOSH.LISTS`
 
 Utilities for operating on lists.
--- a/src/iterate.lisp	Mon May 24 01:18:26 2021 -0400
+++ b/src/iterate.lisp	Mon Jul 12 22:03:01 2021 -0400
@@ -1061,3 +1061,54 @@
                                              (aref ,reg-start% ,i)
                                              (aref ,reg-end% ,i))))))))))))))))
 
+
+(defmacro WHEN-FIRST-TIME (expr)
+  "Sugar for `(if-first-time expr nil)`."
+  `(if-first-time ,expr nil))
+
+(defmacro UNLESS-FIRST-TIME (expr)
+  "Sugar for `(if-first-time nil expr)`."
+  `(if-first-time nil ,expr))
+
+
+(defmacro IF-FIRST-ITERATION (then else)
+  "Evaluate `then` if this clause is executed on the first iteration, otherwise `else`.
+
+  This is similar to from iterate's built-in `if-first-time`, but slightly different:
+
+  * `if-first-time` evaluates `then` the first time the clause is evaluated,
+    even if that happens on a subsequent iteration.
+  * `if-first-iteration` evaluates `then` only if the clause is evaluated on
+    the first iteration.
+
+  Example:
+
+    (iterate
+      (for i :from 1 :to 4)
+      (collect (cons i (when (evenp i)
+                         (list
+                           (if-first-time :first-time :later-time)
+                           (if-first-iteration :first-iter :later-iter))))))
+    ; =>
+    ; ((1)
+    ;  (2 :FIRST-TIME :LATER-ITER)
+    ;  (3)
+    ;  (4 :LATER-TIME :LATER-ITER))
+
+  in that it will only evaluate `then` on the first iteration of the loop, "
+  (with-gensyms (first-iteration)
+    `(progn
+       (with ,first-iteration = t)
+       (after-each (setf ,first-iteration nil))
+       (if ,first-iteration
+         ,then
+         ,else))))
+
+(defmacro WHEN-FIRST-ITERATION (expr)
+  "Sugar for `(if-first-iteration expr nil)`."
+  `(if-first-iteration ,expr nil))
+
+(defmacro UNLESS-FIRST-ITERATION (expr)
+  "Sugar for `(if-first-iteration nil expr)`."
+  `(if-first-iteration nil ,expr))
+
--- a/src/package.lisp	Mon May 24 01:18:26 2021 -0400
+++ b/src/package.lisp	Mon Jul 12 22:03:01 2021 -0400
@@ -308,13 +308,14 @@
     :collect-frequencies
     :collect-hash
     :collect-set
+    :concatenating
     :cycling
-    :concatenating
     :end
     :every-nth
     :finding-all
     :finding-first
     :for-nested
+    :if-first-iteration
     :in-array
     :in-hashset
     :in-lists
@@ -341,6 +342,10 @@
     :test
     :then
     :timing
+    :unless-first-iteration
+    :unless-first-time
+    :when-first-iteration
+    :when-first-time
     :within-radius
 
     ))