2

如何设置变量以指向列表单元格?

我正在尝试编写一个宏来将列表中的所有值乘以一个值。这是我目前拥有的:

(defmacro scale (areas scale)
  `(dotimes (n (list-length ,areas))
     (setf (nth n ,areas) (* (nth n ,areas) ,scale)))) 

我担心这不是最有效的做事方式,因为我两次查找第 n 个单元格。我宁愿设置一个变量指向第 n 个单元格,以便setf可以修改该单元格的值并*可以在其计算中使用该单元格的值。

更好的是使用dolist并将变量设置为单元格引用。这是可能吗?

虽然我在这里,但当您有一个单元格时,是否也可以获取列表中的下一个单元格。有点像迭代器,这样我就可以执行以下操作:

(let ((area (car areas))
  (loop while area do
    (setf area (* area scale))
    (setf area (next area))))

但我不知道如何区分设置指针或设置引用单元格的值。

我希望我有道理:)

4

1 回答 1

6

第一个代码示例的主要问题不是它nth两次找到单元格,而是它确实使用nth了。而不是获取nth 单元格,而是获取下一个单元格,它是cdr前一个单元格的。

你不需要宏,所以让我们实现一个函数。在列表的每个 cons 单元格上执行操作的方法有很多种:

(defun scale (areas scale)
  (do ((tail areas (cdr tail)))
      ((endp tail))
      (setf (car tail)
            (* (car tail) scale))))

(defun scale (areas scale)
  (loop for tail on areas
        do (setf (car tail)
                 (* (car tail) scale))))

(defun scale (areas scale)
  (mapl (lambda (cell)
          (setf (car cell)
                (* (car cell) scale)))
        areas))

有一个替代方案不涉及对car每个单元的显式操作:

(defun scale (areas scale)
  (map-into areas
            (lambda (area)
              (* area scale))
            areas))

作为奖励,这是一个类似dolist- 的宏,它使主体中“变量”的修改传播到列表中:

(defmacro dolistref ((var list &optional result) &body body)
  (let ((tail (gensym "TAIL"))
        (head (gensym "HEAD")))
    `(let ((,head ,list))
       (symbol-macrolet ((,var (car ,tail)))
         (do ((,tail ,head (cdr ,tail)))
             ((endp ,tail) ,result)
           ,@body)))))

;; usage example
(let ((a (list 1 2 3)))
  (dolistref (item a a)
    (incf item))) ;; => (2 3 4)
于 2013-02-20T19:23:31.870 回答