CLOS 中的 with-slots 宏的教学示例是什么?

What is a didatic example of the with-slots macro in CLOS?

Common Lisp HyperSpec covers with-slots 宏。但是,这个例子很难掌握。

是否有关于它的更简单、更具教学意义的示例?

是的。 2003年的这个(伟大的)tutorial有一个几何领域的好。

创建一个 class 来表示 3 维点:

(defclass point ()
    (x y z))

创建一个变量来实例化 class 和一个函数来设置值:

(defvar my-point
  (make-instance 'point))

(defun set-point-values (point x y z)
  (setf (slot-value point 'x) x
        (slot-value point 'y) y
        (slot-value point 'z) z))

在 REPL 中,执行:

CL-USER 17 > (set-point-values my-point 3 4 12)
12

现在,想一想compute the distance点之间的函数。一种蛮力方式是:

(defun brute-force-distance-from-origin (point)
  (let ((x (slot-value point 'x))
        (y (slot-value point 'y))
        (z (slot-value point 'z)))
    (sqrt (+ (* x x)
             (* y y)
             (* z z)))))

使用 with-slots 宏:

(defun distance-from-origin (point)
  (with-slots (x y z) point (sqrt (+ (* x x)
                                     (* y y)
                                     (* z z)))))

调用 REPL 中的函数按预期工作:

CL-USER> (distance-from-origin my-point)
13.0

首先要更好地理解with-slots, one should look into defclass

No reader or writer functions are defined by default; their generation must be explicitly requested. However, slots can always be accessed using slot-value.

这意味着,除非有特殊要求,defclass不要为插槽创建任何访问器:

> (defclass point ()
  (x y))

(let ((new-point (make-instance 'point)))
  (setf (point-x new-point) 1))

Error: The function (COMMON-LISP:SETF COMMON-LISP-USER::POINT-X) is undefined.

在这种情况下,必须使用 slot-value 函数来访问或修改槽值。

(defclass point ()
  (x y))

(let ((new-point (make-instance 'point)))
  (setf (slot-value new-point 'x) 1))

显然,当有多个slot需要更新时,代码会变得有点繁琐:

(defmethod translate ((point-instance point) delta-x delta-y)
  (setf (slot-value point-instance 'x)
        (+ (slot-value point-instance 'x) delta-x))
  (setf (slot-value point-instance 'y)
        (+ (slot-value point-instance 'y) delta-y)))

因此,with-slots macro 可以使代码更易于阅读:

(defmethod translate ((point-instance point) delta-x delta-y)
  (with-slots (x y) point-instance
    (setf x (+ x delta-x))
    (setf y (+ y delta-y))))