Reputation: 6189
I want to access arrays with the indices being in a list. Let's call this utility arefl
, and it's defined as follows:
(arefl array '(x y z ...)) equals (aref array x y z ...)
My goal is to create some generic functions that operate on matrices of any size.
I have failed to implement such a thing with macros and just aref
. The closest thing that I have is:
(defmacro arefl (array is)
"Access array by a list of indices"
`(apply #'aref (cons ,array ,is)))
which works, and actually it also works with (setf (arefl array is) value)
but the compiler, at least sbcl
, throws a warning telling me that I'm redefining setf
for (I guess) apply
. The warning is:
; in: DEFUN (SETF AREFL**)
; (SETF (APPLY #'AREF ARRAY (REDUCE 'CONS ARGS :FROM-END T)) NEW-VALUE)
; --> LET* MULTIPLE-VALUE-BIND LET APPLY MULTIPLE-VALUE-CALL
; ==>
; #'(SETF AREF)
;
; caught STYLE-WARNING:
; defining as a SETF function a name that already has a SETF macro:
; (SETF AREF)
;
; compilation unit finished
; caught 1 STYLE-WARNING condition
--
Is there a better way? I'm looking for an implementation that works well with setf
and does not need a call to another function like apply
nor does do any cons
Upvotes: 1
Views: 134
Reputation: 85883
First, though I recognize that you said
I'm looking for an implementation that works well with
setf
and does not need a call to another function likeapply
nor does do anycons
however, you can simply use apply 'aref
here, and you don't need to do any cons
ing, since only apply
's final argument needs to be a list. That means that all the following are equivalent:
(aref array 0 1)
(apply 'aref (list array 0 1))
(apply 'aref array (list 0 1))
(apply 'aref array 0 (list 1))
(apply 'aref array 0 1 '())
Most importantly, if you want to avoid calling cons
, it means that you can do
(apply 'aref array indices)
You can use setf
with this too (although you will have to use #'array
, and not 'array
):
(setf (apply #'aref array indices) new-value)
Since apply
works here, you just need to make your aref*
and (setf aref*)
functions (to be analogous with list*
):
(defun aref* (array &rest args)
(apply 'aref array (reduce 'cons args :from-end t)))
(defun (setf aref*) (new-value array &rest args)
(setf (apply #'aref array (reduce 'cons args :from-end t)) new-value))
The (reduce 'cons args :from-end t)
in those is used to support spreadable argument list designators, which are what apply
uses. Using this idiom, you can pass exactly the same kinds of arguments to (aref* ...)
that you could use in (apply #'aref ...)
. That might be a bit more complex than the use cases that you've described, but it means that rather than having to specifically describe what sorts of arguments aref*
takes, you can simply say (like the documentation for apply
does), that aref*
's args are a spreadable argument list designator, and that aref*
applies aref
to the args.
Upvotes: 2
Reputation: 14325
Ok, define-setf-expander
is overkill for this.
(defun arefl (array list)
(apply #'aref array list))
(defun (setf arefl) (x array list)
(setf (apply #'aref array list) x))
See "APPLY Forms as Places": http://clhs.lisp.se/Body/05_abe.htm
Upvotes: 2