向量的解构

Mar*_*ann 3 vector common-lisp destructuring

我正在使用来自外部库的函数返回四个数字的向量,我想直接访问这些值,就像使用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))
Run Code Online (Sandbox Code Playgroud)

如果我coercevectorlist这是可能的,并且表现在这里是没有问题的,这是可能的罚款。但我想知道是否有更简单的方法?

cor*_*ump 7

您可以将变量绑定到每个单元格,如下所示:

(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))))
Run Code Online (Sandbox Code Playgroud)

您可以按如下方式使用它:

(with-aref (w x y z) vec
  (setf w (+ x y z)))
Run Code Online (Sandbox Code Playgroud)

通过更多的工作,您还可以支持索引和不同类别的访问器。假设每个绑定都是一个三元组(i n k),其中i是一个标识符,n一个代表数字索引的数字(或 nil),k要么是:place:value要么是 nil;:place将符号与绑定symbol-macrolet:value只需将其与 绑定let

首先,让我们通过提供快捷符号来帮助用户:

  • 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))))))))))
Run Code Online (Sandbox Code Playgroud)

我们可以将此规范化应用于索引列表,并跟踪被忽略的符号:

(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))))
Run Code Online (Sandbox Code Playgroud)

然后,我们处理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)))
Run Code Online (Sandbox Code Playgroud)

例如:

(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))
Run Code Online (Sandbox Code Playgroud)

我们对我们绑定的变量类型做同样的事情:

(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)))
Run Code Online (Sandbox Code Playgroud)

例如:

(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))
Run Code Online (Sandbox Code Playgroud)

最后,所有这些步骤组合在一起parse-indices

(defun parse-indices (indices)
  (multiple-value-bind (normalized ignored) (normalize indices)
    (values (rekind (renumber normalized))
            ignored)))
Run Code Online (Sandbox Code Playgroud)

最后,宏如下:

(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))))))))
Run Code Online (Sandbox Code Playgroud)

例如:

(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)))))
Run Code Online (Sandbox Code Playgroud)

以上宏展开为:

(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))
Run Code Online (Sandbox Code Playgroud)

它产生以下结果

#(0 1 (2 3 4 7 0 1) 3 4 5 6 7 8 9 10)
Run Code Online (Sandbox Code Playgroud)