vector - 向量的解构

标签 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))

如果我 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/

    相关文章:

    macos - 在 Mac 上使用什么 Common Lisp 环境?

    clojure - 如何指定混合 map ?

    javascript - 解构对象参数,还要引用参数作为对象?

    c++ - 编译器能内联这个方法吗?

    LISP MAKE-PATHNAME : Illegal :DIRECTORY argument

    macros - Common Lisp 中的 reader 宏有哪些限制

    javascript - 销毁对象的嵌套数组 javascript

    C++ vector 值不断变化?

    c++ - 没有匹配的成员函数调用 'push_back' ,共享指针 vector

    c++ - 如何使用 dfs O(n) 打印图形的 MaxPath?