首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >向量的析构

向量的析构
EN

Stack Overflow用户
提问于 2020-05-19 02:06:14
回答 3查看 168关注 0票数 3

我正在使用一个外部库中的函数,返回一个由四个数字组成的向量,并且我希望像使用destructuring-bind一样直接访问这些值。请看这个无意义的示例:

代码语言:javascript
复制
(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))

如果我将vector coerce到一个list中,这是可能的,因为性能在这里不是问题,所以它可能是好的。但我想知道有没有更简单的方法?

EN

回答 3

Stack Overflow用户

发布于 2020-05-19 03:07:08

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

代码语言:javascript
复制
(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))))

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

代码语言:javascript
复制
(with-aref (w x y z) vec
  (setf w (+ x y z)))

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

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

  • x代表(x nil nil)
  • (x o)(x o nil)(x nil o),具体取决于选项o是数字还是符号(在宏扩展时)。

此外,我们可能希望自动忽略nil标识符、空符号||或以下划线开头的符号(例如__var)。

下面是标准化函数:

代码语言:javascript
复制
(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))))))))))

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

代码语言:javascript
复制
(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数。我们希望索引从上次使用的索引开始增加,或者由用户显式给出:

代码语言:javascript
复制
(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)))

例如:

代码语言:javascript
复制
(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))

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

代码语言:javascript
复制
(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)))

例如:

代码语言:javascript
复制
(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中组合在一起

代码语言:javascript
复制
(defun parse-indices (indices)
  (multiple-value-bind (normalized ignored) (normalize indices)
    (values (rekind (renumber normalized))
            ignored)))

最后,宏如下所示:

代码语言:javascript
复制
(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))))))))

例如:

代码语言:javascript
复制
(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)))))

上面的宏展开式如下:

代码语言:javascript
复制
(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))

它会产生以下结果

代码语言:javascript
复制
#(0 1 (2 3 4 7 0 1) 3 4 5 6 7 8 9 10)
票数 7
EN

Stack Overflow用户

发布于 2020-05-19 18:12:19

coredump的回答很好。这是它的一个变体,它绑定变量而不是访问器,还允许您有选择地指定索引。所以

代码语言:javascript
复制
(with-vector-elements ((a 3) b) x
  ...)

例如,将a绑定到(aref x 3)的结果,将b绑定到(aref x 4)的结果。

只有当你打算(a)不写回向量和(b)大量使用绑定,所以你想要避免很多可能的arefs (我不认为编译器在没有一些相当强的假设的情况下通常不能优化)时,这才是真正有用的。

代码语言:javascript
复制
(defmacro with-vector-elements ((&rest indices) vector &body forms)
  (let ((canonical-indices
         (loop with i = 0
               for index in indices
               collect (etypecase index
                         (symbol
                          (prog1 
                              `(,index ,i)
                            (incf i)))
                         (cons
                          (destructuring-bind (var idx) index
                            (assert (and (symbolp var) 
                                         (typep idx '(and fixnum (integer 0))))
                                (var idx) "Invalid index spec")
                            (prog1
                                index
                              (setf i (1+ idx))))))))
        (vname (gensym "V")))
    `(let ((,vname ,vector))
       (let ,(loop for (var index) in canonical-indices
                   collect `(,var (aref ,vname ,index)))
         ,@forms))))
票数 4
EN

Stack Overflow用户

发布于 2020-05-22 16:00:15

还有一个名为metabang-bind的包--昵称为bind --其中的函数bind可以处理更多的解构情况:

代码语言:javascript
复制
(ql:quickload :metabang-bind)
(in-package :metabang-bind)

(bind ((#(a b c) #(1 2 3)))
  (list a b c))
;; => (1 2 3)

如果不使用in-package,您可以像bind:bind一样调用该函数。你可以将函数bind粗略地看作是一个destructuring-let* (类似于clojure的let,但是在语法上不是那么清晰,但是可以理解,因为它还必须处理结构和类以及values)。它可以处理的所有其他用例都描述为here

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61876099

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档