我正在使用来自外部库的函数返回四个数字的向量,我想直接访问这些值,就像使用 destructuring-bind
一样。 .看这个毫无意义的例子:
(defun a-vector ()
(vector 1 2 3 4))
(defun a-list ()
(list 1 2 3 4))
(destructuring-bind (a b c d)
(a-list)
(format t "~D ~D ~D ~D~%" a b c d))
(destructuring-bind (a b c d)
(coerce (a-vector) 'list)
(format t "~D ~D ~D ~D~%" a b c d))
如果我
coerce
vector
变成 list
这是可能的,因为性能在这里不是问题,所以可能没问题。但我想知道是否有更简单的方法?
最佳答案
您可以将变量绑定(bind)到每个单元格,如下所示:
(defmacro with-aref ((&rest indices) array &body body)
(let ((a (gensym)))
`(let ((,a ,array))
(symbol-macrolet
,(loop
for n from 0
for i in indices
collect (list i `(aref ,a ,n)))
,@body))))
您可以按如下方式使用它:
(with-aref (w x y z) vec
(setf w (+ x y z)))
通过更多的工作,您还可以支持索引和不同类别的访问器。假设每个绑定(bind)都是一个三元组
(i n k)
在哪里 i
是一个标识符,n
一个数字(或 nil),表示数字索引和 k
是 :place
, :value
或无; :place
将符号与 symbol-macrolet
绑定(bind), :value
只需将其与 let
绑定(bind)即可.首先,让我们通过提供快捷方式来帮助用户:
x
代表 (x nil nil)
(x o)
要么代表(x o nil)
或 (x nil o)
, 取决于选项 o
是一个数字或一个符号(在宏展开时)。 此外,我们可能希望自动忽略
nil
标识符,空符号||
或以下划线开头的符号(例如 _
、 _var
)。这是归一化函数:
(defun normalize-index (index)
(flet ((ret (i n k)
(let ((ignored (or (null i)
(string= i "")
(char= #\_ (char (string i) 0)))))
(list (if ignored (gensym) i) n k ignored))))
(let ((index (alexandria:ensure-list index)))
(typecase index
(null (ret nil nil nil))
(cons (destructuring-bind (i &optional n (k nil kp)) index
(if kp
(ret i n k)
(etypecase n
(symbol (ret i nil n))
((integer 0) (ret i n nil))))))))))
我们可以将此规范化应用于索引列表,并跟踪被忽略的符号:
(defun normalize (indices)
(loop
for i in indices
for norm = (normalize-index i)
for (index number kind ignore) = norm
collect norm into normalized
when ignore
collect index into ignored
finally (return (values normalized ignored))))
然后,我们照顾
nil
标准化条目中的数字。我们希望索引从上次使用的索引增加,或者由用户明确给出:(defun renumber (indices)
(loop
for (v n k) in indices
for next = nil then (1+ index)
for index = (or n next 0)
collect (list v index k)))
例如:
(renumber (normalize '(a b c)))
((A 0 NIL) (B 1 NIL) (C 2 NIL))
(renumber (normalize '((a 10) b c)))
((A 10 NIL) (B 11 NIL) (C 12 NIL))
(renumber (normalize '((a 10) (b 3) c)))
((A 10 NIL) (B 3 NIL) (C 4 NIL))
我们对我们绑定(bind)的变量做同样的事情:
(defun rekind (indices)
(loop
for (v n k) in indices
for next = nil then kind
for kind = (or k next :place)
collect (list v n kind)))
例如:
(rekind (normalize '(a b c)))
((A NIL :PLACE) (B NIL :PLACE) (C NIL :PLACE))
(rekind (normalize '(a (b :value) c)))
((A NIL :PLACE) (B NIL :VALUE) (C NIL :VALUE))
最后,所有这些步骤都合并到
parse-indices
中。 :(defun parse-indices (indices)
(multiple-value-bind (normalized ignored) (normalize indices)
(values (rekind (renumber normalized))
ignored)))
最后,宏如下:
(defmacro with-aref ((&rest indices) array &body body)
(multiple-value-bind (normalized ignored) (parse-indices indices)
(labels ((ignored (b) (remove-if-not #'ignoredp (mapcar #'car b)))
(ignoredp (s) (member s ignored)))
(loop
with a = (gensym)
for (i n k) in normalized
for binding = `(,i (aref ,a ,n))
when (eq k :value) collect binding into values
when (eq k :place) collect binding into places
finally (return
`(let ((,a ,array))
(let ,values
(declare (ignore ,@(ignored values)))
(symbol-macrolet ,places
(declare (ignore ,@(ignored places)))
,@body))))))))
例如:
(let ((vec (vector 0 1 2 3 4 5 6 7 8 9 10)))
(prog1 vec
(with-aref ((a 2) (b :value) c _ _ d (e 0) (f 1)) vec
(setf a (list a b c d e f)))))
上面的宏扩展为:
(LET ((VEC (VECTOR 0 1 2 3 4 5 6 7 8 9 10)))
(LET ((#:G1898 VEC))
(LET ((#:G1901 VEC))
(LET ((B (AREF #:G1901 3))
(C (AREF #:G1901 4))
(#:G1899 (AREF #:G1901 5))
(#:G1900 (AREF #:G1901 6))
(D (AREF #:G1901 7))
(E (AREF #:G1901 0))
(F (AREF #:G1901 1)))
(DECLARE (IGNORE #:G1899 #:G1900))
(SYMBOL-MACROLET ((A (AREF #:G1901 2)))
(DECLARE (IGNORE))
(LET* ((#:G19011902 #:G1901)
(#:NEW1 (LIST (AREF #:G1901 2) B C D E F)))
(FUNCALL #'(SETF AREF) #:NEW1 #:G19011902 2)))))
#:G1898))
它产生以下结果
#(0 1 (2 3 4 7 0 1) 3 4 5 6 7 8 9 10)
关于vector - 向量的解构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61876099/