|
From: <ap...@us...> - 2025-12-01 06:23:24
|
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Maxima CAS".
The branch, dodier-iterate-over-hash-tables-take-2 has been created
at 93a890f26c1ed97facd712f1a44b980e4afd0b2c (commit)
- Log -----------------------------------------------------------------
commit 93a890f26c1ed97facd712f1a44b980e4afd0b2c
Author: Robert Dodier <rob...@so...>
Date: Sun Nov 30 22:22:12 2025 -0800
Documentation for iterating over key-value pairs of hash table;
also documentation for iterating over arguments of a general expression,
and expand documentation for iterating over elements of a list.
diff --git a/doc/info/Program.texi b/doc/info/Program.texi
index 13bcd99b6..e4350ecc2 100644
--- a/doc/info/Program.texi
+++ b/doc/info/Program.texi
@@ -311,6 +311,10 @@ form of the @code{do} statements maxima supports is:
unless @var{condition} do @var{body}}
@item
@code{for @var{variable} in @var{list} do @var{body}}
+@item
+@code{for @var{variable} in @var{expr} do @var{body}}
+@item
+@code{for @var{variable} in @var{hash_table} do @var{body}}
@end itemize
If the loop is expected to generate a list as output the command
@@ -318,7 +322,7 @@ If the loop is expected to generate a list as output the command
@xref{Performance considerations for Lists}.
@var{initial_value}, @var{increment}, @var{limit}, and @var{body} can be any
-expression. @var{list} is a list. If the increment is 1 then "@code{step 1}"
+expression. If the increment is 1 then "@code{step 1}"
may be omitted; As always, if @code{body} needs to contain more than one command
these commands can be specified as a comma-separated list surrounded
by parenthesis or as a @mrefdot{block}
@@ -560,39 +564,126 @@ be returned as the value of the @code{do}. The @code{block} is exited and this
value of the @code{do} is returned as the value of the @code{block} because the
@code{do} is the last statement in the block.)
-One other form of the @code{do} is available in Maxima. The syntax is:
+A @code{for} loop can also iterate over the contents of lists, general expressions, and hash tables, as follows.
@example
for @var{variable} in @var{list} @var{end_tests} do @var{body}
+
+for @var{variable} in @var{expr} @var{end_tests} do @var{body}
+
+for @var{variable} in @var{hash_table} @var{end_tests} do @var{body}
@end example
-The elements of @var{list} are any expressions which will successively
-be assigned to the @code{variable} on each iteration of the
-@var{body}. The optional termination tests @var{end_tests} can be
-used to terminate execution of the @code{do}; otherwise it will
-terminate when the @var{list} is exhausted or when a @code{return} is
-executed in the @var{body}. (In fact, @code{list} may be any
-non-atomic expression, and successive parts are taken.)
+In each case,
+@var{body} is evaluated once for @var{variable} assigned
+each element of @var{list},
+each argument of @var{expr},
+or each key-value pair of @var{hash_table},
+respectively.
+If present,
+@var{end_tests} terminate the loop if any test evaluates to @code{true};
+otherwise the loop terminates when the elements of @var{list},
+the arguments of @var{expr},
+or the key-value pairs of @var{hash_table}
+are exhausted,
+or when @code{return} is executed in @var{body}.
+
+@var{hash_table} may be an undeclared array,
+either as a symbol property (created with @code{use_fast_arrays} equal to @code{false})
+or a value (created with @code{use_fast_arrays} equal to @code{true}),
+or a hash table created by @code{make_array(hashed, ...)}.
+
+Values are assigned to @var{variable} using the general assignment operator @code{":"}.
+Therefore any assignment to a symbol (not a subscripted expression) which is possible via @code{":"}
+is also permissible for iterating over the contents of expressions.
+In particular,
+destructuring assignments are recognized;
+these may be useful to work with the key-value pairs of a hash table.
+
+Examples:
+
+A @code{for} loop can iterate over the elements of a list.
@c ===beg===
-@c for f in [log, rho, atan] do ldisp(f(1))$
-@c ev(%t3,numer);
+@c for f in [log, rho, atan] do ldisp (f(1)) $
@c ===end===
@example maxima
@group
-(%i1) for f in [log, rho, atan] do ldisp(f(1))$
+(%i1) for f in [log, rho, atan] do ldisp (f(1)) $
(%t1) 0
(%t2) rho(1)
%pi
-(%t3) ---
+(%t3) ───
4
+@end group
+@end example
+
+A @code{for} loop can iterate over the arguments of a general expression.
+@c ===beg===
+@c e: a + 1/2 + %pi*2;
+@c for v in e do ldisp (v) $
+@c ===end===
+@example maxima
+@group
+(%i1) e: a + 1/2 + %pi*2;
+ 1
+(%o1) a + 2 %pi + ─
+ 2
+(%i2) for v in e do ldisp (v) $
+(%t2) a
+
+(%t3) 2 %pi
+
+ 1
+(%t4) ─
+ 2
@end group
+@end example
+
+A @code{for} loop can iterate over the key-value pairs of hash tables.
+In this case, the hash table is an undeclared array.
+
+@c ===beg===
+@c (hh["foo"]: 444, hh["bar"]: 222, hh["baz"]: 777) $
+@c for p in hh do ldisp (p);
+@c ===end===
+@example maxima
+@group
+(%i1) (hh["foo"]: 444, hh["bar"]: 222, hh["baz"]: 777) $
+
+(%i2) for p in hh do ldisp (p);
+(%t2) [[bar], 222]
+
+(%t3) [[baz], 777]
+
+(%t4) [[foo], 444]
+
+(%o4) done
+@end group
+@end example
+
+Destructuring assignments are recognized;
+these may be useful to work with the key-value pairs of a hash table.
+
+@c ===beg===
+@c (hh["foo"]: 444, hh["bar"]: 222, hh["baz"]: 777) $
+@c for [[k], v] in hh do disp (printf (false, "Key ~a --> value ~a", k, v));
+@c ===end===
+@example maxima
@group
-(%i4) ev(%t3,numer);
-(%o4) 0.7853981633974483
+(%i1) (hh["foo"]: 444, hh["bar"]: 222, hh["baz"]: 777) $
+
+(%i2) for [[k], v] in hh do disp (printf (false, "Key ~a --> value ~a", k, v));
+ Key bar --> value 222
+
+ Key baz --> value 777
+
+ Key foo --> value 444
+
+(%o2) done
@end group
@end example
commit fd048bb89333049a38d9026d7324997c507b9d00
Author: Robert Dodier <rob...@so...>
Date: Sat Nov 29 10:43:06 2025 -0800
Additional tests for iterating over hash tables; one loop variable, no destructuring.
diff --git a/tests/rtest6.mac b/tests/rtest6.mac
index 531dad995..4ca02d4bf 100644
--- a/tests/rtest6.mac
+++ b/tests/rtest6.mac
@@ -253,6 +253,18 @@ true;
a: 100;
100;
+block ([L: []], for a in aa1 do push ([a[2], a[1]], L), sort (L));
+[[111, ["foo"]], [222, ["bar"]], [333, ["baz"]], [444, ["quux"]]];
+
+a;
+100;
+
+block ([L: []], for b in aa1 do push ([b[2], b[1]], L), sort (L));
+[[111, ["foo"]], [222, ["bar"]], [333, ["baz"]], [444, ["quux"]]];
+
+b;
+b;
+
block ([L: []], for [a, b] in aa1 do push ([b, a], L), sort (L));
[[111, ["foo"]], [222, ["bar"]], [333, ["baz"]], [444, ["quux"]]];
@@ -273,6 +285,18 @@ block ([L: []], for [[a], b] in aa1 do push ([b, a], L), sort (L));
?hash\-table\-p (aa2);
true;
+block ([L: []], for a in aa2 do push ([a[2], a[1]], L), sort (L));
+[[%e, [jj, 543]], [%phi, [cc, 1099]], [%pi, [xx, 99]]];
+
+a;
+100;
+
+block ([L: []], for b in aa2 do push ([b[2], b[1]], L), sort (L));
+[[%e, [jj, 543]], [%phi, [cc, 1099]], [%pi, [xx, 99]]];
+
+b;
+b;
+
block ([L: []], for [a, b] in aa2 do push ([b, a], L), sort (L));
[[%e, [jj, 543]], [%phi, [cc, 1099]], [%pi, [xx, 99]]];
@@ -302,6 +326,14 @@ true;
[b, a];
[-2, 100];
+block ([L: []], for b in aa3 do push ([b[1], b[2]], L), sort (L));
+[[["fgh", 3*u*v, giraffe], rhino*elephant],
+ [["glorble", 7*x, emu], 6/cricket],
+ [["mno", "pqr", 1 - 2*u], bessel_j (2, 1 - sqrt(x))]];
+
+b;
+-2;
+
block ([L: []], for [b, a] in aa3 do push ([b, a], L), sort (L));
[[["fgh", 3*u*v, giraffe], rhino*elephant],
[["glorble", 7*x, emu], 6/cricket],
@@ -340,6 +372,12 @@ true;
(f: 199, g: 200);
200;
+block ([L: []], for g in hh1 do push ([g[2], g[1]], L), sort (L));
+[[cos(w), [3 - y]], [tan(x), [1/(1 + z)]], [sin(z), [1 + x]]];
+
+g;
+200;
+
block ([L: []], for [f, g] in hh1 do push ([g, f], L), sort (L));
[[cos(w), [3 - y]], [tan(x), [1/(1 + z)]], [sin(z), [1 + x]]];
@@ -366,6 +404,14 @@ true;
0);
0;
+block ([L: []], for y in hh2 do push ([y[1], y[2]], L), sort (L));
+[[[456, cos(w)], sin(u) - cos(v)],
+ [[678, "blurf"], 876 - mumble],
+ [[987, "sdf"], 777*zxy]];
+
+y;
+y;
+
block ([L: []], for [x, y] in hh2 do push ([x, y], L), sort (L));
[[[456, cos(w)], sin(u) - cos(v)],
[[678, "blurf"], 876 - mumble],
@@ -396,6 +442,14 @@ true;
0);
0;
+block ([L: []], for q in hh3 do push ([q[1], q[2]], L), sort (L));
+[[[asdfg, 1 - rewq, 2*treww], sin(poiur) - cos(kjghds)],
+ [[ewq, 2 - jdssaaq, 3*cxmmz], sqrt(ytre)],
+ [[hgf, fds, jhg], cos(trwq)]];
+
+q;
+q;
+
block ([L: []], for [p, q] in hh3 do push ([p, q], L), sort (L));
[[[asdfg, 1 - rewq, 2*treww], sin(poiur) - cos(kjghds)],
[[ewq, 2 - jdssaaq, 3*cxmmz], sqrt(ytre)],
@@ -431,6 +485,12 @@ false;
arrays;
[bb1];
+block ([L: []], for v in bb1 do push ([v[2], v[1]], L), sort (L));
+[[123, ["mumble"]], [234, ["blarf"]], [345, ["blarg"]], [999, ["blurf"]]];
+
+v;
+v;
+
block ([L: []], for [u, v] in bb1 do push ([v, u], L), sort (L));
[[123, ["mumble"]], [234, ["blarf"]], [345, ["blarg"]], [999, ["blurf"]]];
@@ -454,6 +514,12 @@ arrays;
w: 321;
321;
+block ([L: []], for w in bb2 do push ([w[1], w[2]], L), sort (L));
+[[["hgfedc", 3 - blurf], tan(n*%pi)], [["pqr", 1 + %pi], sin(k*%pi)], [["zxy", 2*%pi], cos(m*%pi)]];
+
+w;
+321;
+
block ([L: []], for [v, w] in bb2 do push ([v, w], L), sort (L));
[[["hgfedc", 3 - blurf], tan(n*%pi)], [["pqr", 1 + %pi], sin(k*%pi)], [["zxy", 2*%pi], cos(m*%pi)]];
@@ -480,6 +546,14 @@ block ([L: []], for [[v1, v2], w] in bb2 do push ([v1, v2, w], L), sort (L));
arrays;
[bb1, bb2, bb3];
+block ([L: []], for v in bb3 do push ([v[1], v[2]], L), sort (L));
+[[[hjk, "mumble", cos(y) - x], cot(a - b)],
+ [[uvw, "qwerty", foo(z) + q], sec(2*b - a)],
+ [[zxy, "blah", cos(a) + cos(b)], tan(a + b)]];
+
+v;
+v;
+
block ([L: []], for [w, v] in bb3 do push ([w, v], L), sort (L));
[[[hjk, "mumble", cos(y) - x], cot(a - b)],
[[uvw, "qwerty", foo(z) + q], sec(2*b - a)],
commit 2ca5be5d9f7110c1be52660b4e9c99352f79ca21
Author: Robert Dodier <rob...@so...>
Date: Fri Nov 28 21:17:11 2025 -0800
Extend "for" loop syntax to iterate over hash tables.
The basic syntax is: for [k, v] in hh do ...
where hh is a undeclared array, "fast" array (i.e., a hash table created
with use_fast_arrays: true), or created by make_array(hashed, ...).
[k, v] is a key/value pair. The pairs are processed in the order
of keys as returned by arrayinfo.
k is always a list, even if the hash table has only one key
and therefore the list has exactly one element.
Destructuring assignment is recognized, e.g.:
for [[k], v] in hh1 do ...
for [[k1, k2], v] in hh2 do ...
for [[k1, k2, k3], v] in hh3 do ...
where hh1, hh2, and hh3 are hash tables which have,
respectively, one, two, and three keys.
Destructuring assignment also applies to the existing loop syntax
for iterating over lists, e.g.:
for [x, y] in [[11, 12], [13, 9], [19, 29]] do ...
This is a freebie -- it would be more work to apply destructuring
assignment only to hash tables, so I let it stand.
There is one bug of which I'm aware: when destructuring assignment is
applied and one of the loop variables is restricted by an ASSIGN helper
to non-symbol values (e.g., linel, which has an ASSIGN helper to
restrict assigned values to integers), *and* the variable is unbound
in the Lisp sense (i.e., BOUNDP returns NIL) before starting the
iteration, then there is an error at the end of the iteration.
From what I can tell, there are no such variables in Maxima, and it's
not easy to create one. Such a variable can be created by Lisp
programming (to create an assignment helper and attach it via the
ASSIGN property), or the following Maxima incantation:
define_variable (foo, 1111, integer);
remvalue (foo);
foo has an ASSIGN property (created by define_variable) and it is
unbound by remvalue. Now the following loop triggers the bug:
for [foo] in [[11], [22], [33]] do print (foo);
From reading the reference manual, it appears that mode_declare plus
mode_checkp, and followed by remvalue, might also work, although it
appears mode_checkp has no effect, from what I can tell.
I will devote time to fixing the bug, but it seems like a not too
serious problem, given the obscurity of the way to trigger it.
diff --git a/src/mlisp.lisp b/src/mlisp.lisp
index e9a0bcdc0..8bdf53d20 100644
--- a/src/mlisp.lisp
+++ b/src/mlisp.lisp
@@ -448,6 +448,18 @@ is EQ to FNNAME if the latter is non-NIL."
;; option variable $errormsg is used as a local variable in a block.
(defvar *$errormsg-value* nil)
+(defun symbol-values-in (expr)
+ (if (atom expr)
+ (if (symbolp expr)
+ (if (boundp expr)
+ ;; Do not take the actual value of $errormsg. It is
+ ;; always NIL at this point, but the value which
+ ;; is stored in *$errormsg-value*.
+ (if (eq expr '$errormsg) *$errormsg-value* (symbol-value expr))
+ munbound)
+ expr)
+ (cons (car expr) (mapcar 'symbol-values-in (cdr expr)))))
+
(defun mbind-doit (lamvars fnargs fnname)
"Makes a new frame where the variables in the list LAMVARS are bound
to the corresponding elements in FNARGS. Note that these elements are
@@ -471,16 +483,9 @@ wrapper for this."
(cons (ncons fnname) lamvars))
(cons '(mlist) fnargs)))))
(let ((var (car vars)))
- (if (not (symbolp var))
+ (when (not (every 'symbolp (cdr ($listofvars var))))
(merror (intl:gettext "Only symbols can be bound; found: ~M") var))
- (let ((value (if (boundp var)
- (if (eq var '$errormsg)
- ;; Do not take the actual value of $errormsg. It is
- ;; always NIL at this point, but the value which
- ;; is stored in *$errormsg-value*.
- *$errormsg-value*
- (symbol-value var))
- munbound)))
+ (let ((value (symbol-values-in var)))
(mset var (car args))
(psetq bindlist (cons var bindlist)
mspeclist (cons value mspeclist))))))
@@ -493,7 +498,7 @@ wrapper for this."
;; At this point store the value of $errormsg in a global. The macro
;; with-$error sets the value of $errormsg to NIL, but we need the
;; actual value in the routine mbind-doit.
- (setq *$errormsg-value* $errormsg)
+ (setq *$errormsg-value* (if (boundp '$errormsg) $errormsg munbound))
(unwind-protect
(prog1
(with-$error (mbind-doit lamvars fnargs fnname))
@@ -522,13 +527,33 @@ wrapper for this."
(finish-output)
(values))
+(defun munbind-makunbound (var)
+ (makunbound var)
+ (setf $values (delete var $values :count 1 :test #'eq)))
+
(defun munbind (vars)
- (dolist (var (reverse vars))
- (cond ((eq (car mspeclist) munbound)
- (makunbound var)
- (setf $values (delete var $values :count 1 :test #'eq)))
- (t (let ((munbindp t)) (mset var (car mspeclist)))))
- (setq mspeclist (cdr mspeclist) bindlist (cdr bindlist))))
+ (let ((foo))
+ (dolist (var (reverse vars))
+ ;; This is a bit of a mess. We should avoid assigning MUNBOUND
+ ;; and instead call MUNBIND-MAKUNBOUND, because some variables
+ ;; cannot be assigned MUNBOUND due to declarations or custom setters.
+ ;; It is easy to detect these cases when VAR is just a symbol,
+ ;; but I don't see a straightforward way to handle it when VAR is
+ ;; a nonatomic expression, so the code for the non-symbol branch
+ ;; will fail if the expression contains a symbol for which
+ ;; MUNBOUND cannot be assigned.
+ (if (symbolp var)
+ (if (eq (car mspeclist) munbound)
+ (munbind-makunbound var)
+ (mset var (car mspeclist)))
+ (progn
+ (let ((munbindp t))
+ (mset var (car mspeclist)))
+ (mapcar (lambda (x) (push x foo)) (cdr ($listofvars var)))))
+ (setq mspeclist (cdr mspeclist) bindlist (cdr bindlist)))
+ (dolist (var foo)
+ (when (and (boundp var) (eq (symbol-value var) munbound))
+ (munbind-makunbound var)))))
;;This takes the place of something like
;; (DELETE (ASSOC (NCONS VAR) $DEPENDENCIES) $DEPENDENCIES 1)
@@ -2229,11 +2254,31 @@ wrapper for this."
(merror (intl:gettext "do loop: illegal 'return': ~M") (car val)))
(t (return (car val))))))))
+(defun key-value-pairs-given-key-lists (x key-lists)
+ (mapcar (lambda (l) (list '(mlist) l (mfuncall '$arrayapply x l))) key-lists))
+
+(defun key-value-pairs-for-hashed-array (x)
+ (let*
+ ((1-d-hash-table (gethash 'dim1 x))
+ (info (mfuncall '$arrayinfo x))
+ (keys-raw (cdddr info))
+ (key-lists (if 1-d-hash-table (mapcar (lambda (y) (cons '(mlist) (list y))) keys-raw) keys-raw)))
+ (key-value-pairs-given-key-lists x key-lists)))
+
+(defun key-value-pairs-for-undeclared-array (x)
+ (let*
+ ((info (mfuncall '$arrayinfo x))
+ (key-lists (cdddr info)))
+ (key-value-pairs-given-key-lists x key-lists)))
+
(defmspec mdoin (form)
(setq form (cdr form))
(funcall #'(lambda (mdop my-var set test action)
(setq set (if ($atom (setq set (format1 (meval (cadr form)))))
- (merror (intl:gettext "do loop: 'in' argument must be a nonatomic expression; found: ~M") set)
+ (cond
+ ((hash-table-p set) (key-value-pairs-for-hashed-array set))
+ ((safe-mget set 'hashar) (key-value-pairs-for-undeclared-array set))
+ (t (merror (intl:gettext "do loop: atomic 'in' argument must be a hashed or undeclared array; found: ~M") set)))
(margs set))
test (list '(mor)
(if (car (cddddr form))
diff --git a/tests/rtest6.mac b/tests/rtest6.mac
index ac4fe99c1..531dad995 100644
--- a/tests/rtest6.mac
+++ b/tests/rtest6.mac
@@ -228,3 +228,370 @@ chain weld bucket;
subst (f = "foo", f(x));
foo(x);
+
+/* tests for iterating over hash tables
+ * verify both value hash tables ("fast arrays" or created by make_array)
+ * and named hash tables ("undeclared arrays").
+ */
+
+kill (all);
+done;
+
+/* value hash tables: "fast arrays" */
+
+use_fast_arrays: true;
+true;
+
+/* one key */
+
+(aa1["baz"]: 333, aa1["bar"]: 222, aa1["quux"]: 444, aa1["foo"]: 111, 0);
+0;
+
+?hash\-table\-p (aa1);
+true;
+
+a: 100;
+100;
+
+block ([L: []], for [a, b] in aa1 do push ([b, a], L), sort (L));
+[[111, ["foo"]], [222, ["bar"]], [333, ["baz"]], [444, ["quux"]]];
+
+[a, b];
+[100, b];
+
+block ([L: []], for [[a], b] in aa1 do push ([b, a], L), sort (L));
+[[111, "foo"], [222, "bar"], [333, "baz"], [444, "quux"]];
+
+[a, b];
+[100, b];
+
+/* two keys */
+
+(aa2[xx, 99]: %pi, aa2[cc, 1099]: %phi, aa2[jj, 543]: %e, 0);
+0;
+
+?hash\-table\-p (aa2);
+true;
+
+block ([L: []], for [a, b] in aa2 do push ([b, a], L), sort (L));
+[[%e, [jj, 543]], [%phi, [cc, 1099]], [%pi, [xx, 99]]];
+
+[a, b];
+[100, b];
+
+(a2: 300, b: -2);
+-2;
+
+block ([L: []], for [[a1, a2], b] in aa2 do push ([b, a2, a1], L), sort (L));
+[[%e, 543, jj], [%phi, 1099, cc], [%pi, 99, xx]];
+
+[a1, a2, b];
+[a1, 300, -2];
+
+/* three keys */
+
+(aa3["mno", "pqr", 1 - 2*u]: bessel_j (2, 1 - sqrt(x)),
+ aa3["fgh", 3*u*v, giraffe]: rhino*elephant,
+ aa3["glorble", 7*x, emu]: 6/cricket,
+ 0);
+0;
+
+?hash\-table\-p (aa3);
+true;
+
+[b, a];
+[-2, 100];
+
+block ([L: []], for [b, a] in aa3 do push ([b, a], L), sort (L));
+[[["fgh", 3*u*v, giraffe], rhino*elephant],
+ [["glorble", 7*x, emu], 6/cricket],
+ [["mno", "pqr", 1 - 2*u], bessel_j (2, 1 - sqrt(x))]];
+
+[b, a];
+[-2, 100];
+
+block ([L: []], for [[a1, a2, b], a] in aa3 do push ([a1, a2, b, a], L), sort (L));
+[["fgh", 3*u*v, giraffe, rhino*elephant],
+ ["glorble", 7*x, emu, 6/cricket],
+ ["mno", "pqr", 1 - 2*u, bessel_j (2, 1 - sqrt(x))]];
+
+[a1, a2, b, a];
+[a1, 300, -2, 100];
+
+arrays;
+[];
+
+(reset (use_fast_arrays), kill (all));
+done;
+
+/* value hash tables: created by make_array */
+
+/* one key */
+
+(hh1: make_array (hashed, 1), 0);
+0;
+
+?hash\-table\-p (hh1);
+true;
+
+(hh1[1 + x]: sin(z), hh1[3 - y]: cos(w), hh1[1/(1 + z)]: tan(x), 0);
+0;
+
+(f: 199, g: 200);
+200;
+
+block ([L: []], for [f, g] in hh1 do push ([g, f], L), sort (L));
+[[cos(w), [3 - y]], [tan(x), [1/(1 + z)]], [sin(z), [1 + x]]];
+
+[f, g];
+[199, 200];
+
+block ([L: []], for [[f], g] in hh1 do push ([g, f], L), sort (L));
+[[cos(w), 3 - y], [tan(x), 1/(1 + z)], [sin(z), 1 + x]];
+
+[f, g];
+[199, 200];
+
+/* two keys */
+
+(hh2: make_array (hashed, 1, 1), 0);
+0;
+
+?hash\-table\-p (hh2);
+true;
+
+(hh2[678, "blurf"]: 876 - mumble,
+ hh2[987, "sdf"]: 777*zxy,
+ hh2[456, cos(w)]: sin(u) - cos(v),
+ 0);
+0;
+
+block ([L: []], for [x, y] in hh2 do push ([x, y], L), sort (L));
+[[[456, cos(w)], sin(u) - cos(v)],
+ [[678, "blurf"], 876 - mumble],
+ [[987, "sdf"], 777*zxy]];
+
+[x, y];
+[x, y];
+
+block ([L: []], for [[x1, x2], y] in hh2 do push ([x1, x2, y], L), sort (L));
+[[456, cos(w), sin(u) - cos(v)],
+ [678, "blurf", 876 - mumble],
+ [987, "sdf", 777*zxy]];
+
+[x1, x2, y];
+[x1, x2, y];
+
+/* three keys */
+
+(hh3: make_array (hashed, 1, 1, 1), 0);
+0;
+
+?hash\-table\-p (hh3);
+true;
+
+(hh3[hgf, fds, jhg]: cos(trwq),
+ hh3[asdfg, 1 - rewq, 2*treww]: sin(poiur) - cos(kjghds),
+ hh3[ewq, 2 - jdssaaq, 3*cxmmz]: sqrt(ytre),
+ 0);
+0;
+
+block ([L: []], for [p, q] in hh3 do push ([p, q], L), sort (L));
+[[[asdfg, 1 - rewq, 2*treww], sin(poiur) - cos(kjghds)],
+ [[ewq, 2 - jdssaaq, 3*cxmmz], sqrt(ytre)],
+ [[hgf, fds, jhg], cos(trwq)]];
+
+[p, q];
+[p, q];
+
+block ([L: []], for [[p1, p2, p3], q] in hh3 do push ([p1, p2, p3, q], L), sort (L));
+[[asdfg, 1 - rewq, 2*treww, sin(poiur) - cos(kjghds)],
+ [ewq, 2 - jdssaaq, 3*cxmmz, sqrt(ytre)],
+ [hgf, fds, jhg, cos(trwq)]];
+
+[p1, p2, p3, q];
+[p1, p2, p3, q];
+
+arrays;
+[];
+
+kill (all);
+done;
+
+/* named hash tables: "undeclared arrays" */
+
+/* one key */
+
+(bb1["mumble"]: 123, bb1["blarf"]: 234, bb1["blurf"]: 999, bb1["blarg"]: 345, 0);
+0;
+
+?hash\-table\-p (bb1);
+false;
+
+arrays;
+[bb1];
+
+block ([L: []], for [u, v] in bb1 do push ([v, u], L), sort (L));
+[[123, ["mumble"]], [234, ["blarf"]], [345, ["blarg"]], [999, ["blurf"]]];
+
+[u, v];
+[u, v];
+
+block ([L: []], for [[u], v] in bb1 do push ([v, u], L), sort (L));
+[[123, "mumble"], [234, "blarf"], [345, "blarg"], [999, "blurf"]];
+
+[u, v];
+[u, v];
+
+/* two keys */
+
+(bb2["pqr", 1 + %pi]: sin(k*%pi), bb2["zxy", 2*%pi]: cos(m*%pi), bb2["hgfedc", 3 - blurf]: tan(n*%pi), 0);
+0;
+
+arrays;
+[bb1, bb2];
+
+w: 321;
+321;
+
+block ([L: []], for [v, w] in bb2 do push ([v, w], L), sort (L));
+[[["hgfedc", 3 - blurf], tan(n*%pi)], [["pqr", 1 + %pi], sin(k*%pi)], [["zxy", 2*%pi], cos(m*%pi)]];
+
+[v, w];
+[v, 321];
+
+v2: 432;
+432;
+
+block ([L: []], for [[v1, v2], w] in bb2 do push ([v1, v2, w], L), sort (L));
+[["hgfedc", 3 - blurf, tan(n*%pi)], ["pqr", 1 + %pi, sin(k*%pi)], ["zxy", 2*%pi, cos(m*%pi)]];
+
+[v1,v2, w];
+[v1, 432, 321];
+
+/* three keys */
+
+(bb3[zxy, "blah", cos(a) + cos(b)]: tan(a + b),
+ bb3[hjk, "mumble", cos(y) - x]: cot(a - b),
+ bb3[uvw, "qwerty", foo(z) + q]: sec(2*b - a),
+ 0);
+0;
+
+arrays;
+[bb1, bb2, bb3];
+
+block ([L: []], for [w, v] in bb3 do push ([w, v], L), sort (L));
+[[[hjk, "mumble", cos(y) - x], cot(a - b)],
+ [[uvw, "qwerty", foo(z) + q], sec(2*b - a)],
+ [[zxy, "blah", cos(a) + cos(b)], tan(a + b)]];
+
+[w, v];
+[321, v];
+
+block ([L: []], for [[v1, v2, w], v] in bb3 do push ([v1, v2, w, v], L), sort (L));
+[[hjk, "mumble", cos(y) - x, cot(a - b)],
+ [uvw, "qwerty", foo(z) + q, sec(2*b - a)],
+ [zxy, "blah", cos(a) + cos(b), tan(a + b)]];
+
+
+[v1, v2, w, v];
+[v1, 432, 321, v];
+
+/* additional tests for MUNBIND */
+
+kill (all);
+done;
+
+(a: "foo", b: "bar", c: "baz", 0);
+0;
+
+(blurf (a, b, c, d, e, f) := (a + b + c)*(d + e + f),
+ blurf (1, 2, 3, 4, 5, 6));
+90;
+
+[a, b, c, d, e, f];
+["foo", "bar", "baz", d, e, f];
+
+(x1: 123,
+ mumble (a, b, [L]) := (a + b) * lsum (x1, x1, L),
+ mumble (7, 8, 9, 10, 11));
+450;
+
+[a, b, L, x1];
+["foo", "bar", L, 123];
+
+(harrumph (b, 'c, d) := b*c*d,
+ harrumph (111, x1, %pi));
+111*x1*%pi;
+
+[b, c, d];
+["bar", "baz", d];
+
+(blarg[x, b] := x^b,
+ blarg[1 - u, 1 - k]);
+(1 - u)^(1 - k);
+
+[b, x];
+["bar", x];
+
+(y: 999,
+ hurfgh[c](x, y) := (x + y)*c,
+ hurfgh[n](4, 2*w));
+n*(2*w + 4);
+
+[c, x, y];
+["baz", x, 999];
+
+block ([L: []], for b: 10 thru 20 do push (b, L), L);
+[20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10];
+
+[L, b];
+[L, "bar"];
+
+sum (b^2, b, 3, 5);
+50;
+
+b;
+"bar";
+
+product (c/2, c, 6, 8);
+42;
+
+c;
+"baz";
+
+'sum (b^2, b, 3, 5);
+'sum (b^2, b, 3, 5);
+
+b;
+"bar";
+
+'product (c/2, c, 6, 8);
+'product (c/2, c, 6, 8);
+
+c;
+"baz";
+
+block ([d, c, b, a: 111], b: 2, c: 3, a*b*c*d);
+666*d;
+
+[a, b, c, d];
+["foo", "bar", "baz", d];
+
+lambda ([c, f, g, b], (c + f)*(g + b))(x1, 1 - u, %e, %pi);
+(124 - u)*(%e + %pi);
+
+[b, c, f, g];
+["bar", "baz", f, g];
+
+lambda ([a,'b], a^b)(11, x1);
+11^x1;
+
+[a, b, x1];
+["foo", "bar", 123];
+
+lambda ([a,b,[c]], lsum(c1^2, c1, c)^(a^b))(%pi, %e, x1, y, u);
+(u^2 + 1013130)^(%pi^%e);
+
+[a, b, c, c1];
+["foo", "bar", "baz", c1];
-----------------------------------------------------------------------
hooks/post-receive
--
Maxima CAS
|