Skip to content
This repository was archived by the owner on Dec 13, 2025. It is now read-only.

Commit adfa68c

Browse files
committed
object-accessors: with-named-array-references
1 parent b434f83 commit adfa68c

File tree

5 files changed

+71
-9
lines changed

5 files changed

+71
-9
lines changed

README.md

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1707,8 +1707,46 @@ As with `with-accessors` you can provide names which are different than the acce
17071707

17081708
There is absolutely nothing special about `with-object-accessors`: it's just the obvious thing you would write using `symbol-macrolet`. Its only reason to exist is so that it *does* exist: versions of it no longer have to be endlessly rewritten. It is careful to evaluate the object only once, so `(with-object-accessors (car cdr) (cons 1 2) ...)` would work, say.
17091709

1710+
Additionally, `object-accessors` now provides a macro for named access to array elements. This is `with-named-array-references`. It has independent origin from `with-object-accessors` and takes its arguments in the opposite order. That's slightly unfortunate. Its syntax is
1711+
1712+
```lisp
1713+
(with-named-array-references (<array> [<type>]) ((<name> <index> ...) ...)
1714+
...)
1715+
```
1716+
1717+
The type declaration applies to the binding established for the array. As an example:
1718+
1719+
```lisp
1720+
(with-named-array-references (state (array double-float (*))) ((x 0) (y 1) (vx 2) (vy 3))
1721+
(incf x vx)
1722+
(incf y vy)
1723+
state)
1724+
```
1725+
1726+
will update some 2d state-vector for an object.
1727+
1728+
Note that the indices are not cached by this macro. If you have a vector representing lots of object states you might want to define something like:
1729+
1730+
```lisp
1731+
(defmacro with-state-slots-at ((v n) &body decls/forms)
1732+
;; Named access to the state at n in the state vector
1733+
(with-names (<xi> <yi> <vxi> <vyi>)
1734+
`(let* ((,<xi> (* ,n 4))
1735+
(,<yi> (+ ,<xi> 1))
1736+
(,<vxi> (+ ,<yi> 1))
1737+
(,<vyi> (+ ,<vxi> 1)))
1738+
(declare (type array-index ,<xi> ,<yi> ,<vxi> ,<vyi>)
1739+
(ignorable ,<xi> ,<yi> ,<vxi> ,<vyi>))
1740+
(with-named-array-references (,v state-vector) ((x ,<xi>) (y ,<yi>)
1741+
(vx ,<vxi>) (vy ,<vyi>))
1742+
(declare (ignorable x y vx vy))
1743+
,@decls/forms))))
1744+
```
1745+
1746+
This avoids recomputing the indices each time.
1747+
17101748
### Package, module
1711-
`object-accessors` lives in `org.tfeb.hax.object-accessors` and provides `:org.tfeb.hax.object-accessors`.
1749+
`object-accessors` depends on `utilities`, lives in `org.tfeb.hax.object-accessors` and provides `:org.tfeb.hax.object-accessors`.
17121750

17131751
## Decomposing iteration: `simple-loops`
17141752
Like a lot of people I have mixed feelings about `loop`. For a long time I thought that, well, if I wasn't going to use `loop`, I'd need some other elaborate iteration system, although perhaps one which was more principled and extensible such as Richard C Waters' [Series](https://github.com/tfeb/series "Series")[^14]. And I am sure the CL community has invented other tools while I've not been watching.

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
10.1.0
1+
10.2.0

object-accessors.lisp

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,16 @@
99
;;; bug fixes.
1010
;;;
1111

12+
#+org.tfeb.tools.require-module
13+
(org.tfeb.tools.require-module:needs
14+
(:org.tfeb.hax.utilities :compile t))
15+
1216
(defpackage :org.tfeb.hax.object-accessors
1317
(:use :cl)
14-
(:export #:with-object-accessors))
18+
(:use :org.tfeb.hax.utilities)
19+
(:export
20+
#:with-object-accessors
21+
#:with-named-array-references))
1522

1623
(in-package :org.tfeb.hax.object-accessors)
1724

@@ -24,19 +31,32 @@ For instance:
2431
(with-oject-accessors ((kar car) cdr) (cons 1 2)
2532
... kar ... cdr ...)
2633
"
27-
(let ((itn (make-symbol "IT")))
28-
`(let ((,itn ,object))
34+
(with-names (<it>)
35+
`(let ((,<it> ,object))
2936
(symbol-macrolet
3037
,(mapcar (lambda (s)
3138
(typecase s
3239
(symbol
33-
`(,s (,s ,itn)))
40+
`(,s (,s ,<it>)))
3441
(cons
3542
(unless (and (= (list-length s) 2)
3643
(symbolp (first s)))
3744
(error "bad accessor spec ~A" s))
38-
`(,(first s) (,(second s) ,itn)))
45+
`(,(first s) (,(second s) ,<it>)))
3946
(t
4047
(error "bad accessor spec ~A" s))))
4148
accessor-specs)
4249
,@decls/forms))))
50+
51+
(defmacro with-named-array-references ((array &optional (type 'array)) (&rest refs)
52+
&body decls/forms)
53+
;; Arguments are in the other order than WITH-OBJECT-ACCESSORS: I'm
54+
;; uncomfortable about that.
55+
"Provide named accessors for elements of an array"
56+
(with-names (<a>)
57+
`(let ((,<a> ,array))
58+
(declare (type ,type ,<a>))
59+
(symbol-macrolet ,(mapcar (lambda (ref)
60+
`(,(first ref) (aref ,<a> ,@(rest ref))))
61+
refs)
62+
,@decls/forms))))

org.tfeb.hax.asd

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828
:depends-on ("collecting" "iterate"))
2929
(:file "stringtable"
3030
:depends-on ("collecting" "iterate"))
31-
(:file "object-accessors")
31+
(:file "object-accessors"
32+
:depends-on ("utilities"))
3233
(:file "utilities"
3334
:depends-on ("collecting" "iterate"))
3435
(:file "simple-loops"
@@ -65,7 +66,8 @@
6566
:depends-on ("org.tfeb.hax" "parachute")
6667
:pathname "test/"
6768
:components
68-
((:file "test-binding")
69+
((:file "test-utilities")
70+
(:file "test-binding")
6971
(:file "test-iterate")
7072
(:file "test-collecting")
7173
(:file "test-simple-loops")

org.tfeb.hax.object-accessors.asd

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,7 @@
1414
"MIT"
1515
:homepage
1616
"https://github.com/tfeb/tfeb-lisp-hax"
17+
:depends-on
18+
"utilities"
1719
:components
1820
((:file "object-accessors")))

0 commit comments

Comments
 (0)