尝试使宏类似于 defparameter、defvar 但宏仅 returns 一个 s 表达式

Trying to make macro similar to defparameter, defvar but macro only returns an s-expression

我正在尝试制作宏来定义类似于 defparameterdefvar 的各种对象。 defregion1 宏起作用:在执行时它定义了一个对象为 region 的变量。但是,defregion2 只是 returns 一个必须手动执行的表达式。这是代码:

(defclass location ()
  ((x
    :initarg :x
    :accessor x)
   (y
    :initarg :y
    :accessor y)))

(defclass region ()
  ((x :initarg :x
      :accessor x)
   (y :initarg :y
      :accessor y)
   (w :initarg :w
      :accessor w)
   (h :initarg :h
      :accessor h)))

(defmacro deflocation (var x y)
  `(defparameter ,var `(make-instance 'location :x ,x :y ,y)))


(defmacro defregion1 (var x y w h)
  `(defparameter ,(intern (symbol-name var))
       (make-instance 'region :x ,x :y ,y :w ,w :h ,h)))

(defmacro defregion2 (var l1 l2)
  `(with-slots ((x1 x) (y1 y))
       ,l1
     (with-slots ((x2 x) (y2 y))
         ,l2
       `(defparameter ,(intern (symbol-name ,var))
           (make-instance 'region
                          :x ,x1 :y ,y1 :w (- ,x2 ,x1) :h (- ,y2 ,y1))))))

defregion1的输出:

(defregion1 *test-reg1* 1 2 3 4) 

=> *test-reg1*

deferegion2的输出:

(deflocation *l1* 20 30)
(deflocation *l2* 50 60)
(defregion2 '*test-reg2* *l1* *l2*)

=> (DEFPARAMETER *TEST-REG2*
     (MAKE-INSTANCE 'REGION :X 20 :Y 30 :W (- 50 20) :H (- 60 30)))

我希望*test-reg2*也成为一个变量。这里有什么问题?

你有两个嵌套的反引号。

但是你的宏也是由内而外的:你真的想要 defparameter 在顶层,所以像这样会更好:

(defmacro defregion2 (var l1 l2)
  `(defparameter ,(intern (symbol-name ,var)) 
     (with-slots ((x1 x) (y1 y))
         ,l1
       (with-slots ((x2 x) (y2 y))
           ,l2
         (make-instance 'region :x x1 :y y1 :w (- x2 x1) :h (- y2 y1))))))

你也确定你想要这个有点奇怪的实习生吗?要做的是将您提供的符号的名称作为参数并将其保存在当前包中。例如

(defregion2 x:*foo* ...)

会在当前包中生成符号*foo*,而不是给x:*foo*赋值。 (当然,如果当前包 x,那么这一切都会变成相同的东西)。

我怀疑你可能想要

(defmacro defregion2 (var l1 l2)
  `(defparameter ,var
     (with-slots ((x1 x) (y1 y))
         ,l1
       (with-slots ((x2 x) (y2 y))
           ,l2
         (make-instance 'region :x x1 :y y1 :w (- x2 x1) :h (- y2 y1))))))

您的代码也可能不卫生,因为它将变量(真正的符号宏)与任何 l2 可见的名称绑定:它会更安全,因为

(defmacro defregion2 (var l1 l2)
  `(defparameter ,var
     (let ((l1 ,l1) (l2 ,l2))
       (with-slots ((x1 x) (y1 y))
           l1
         (with-slots ((x2 x) (y2 y))
             l2
           (make-instance 'region :x x1 :y y1 :w (- x2 x1) :h (- y2 y1)))))))

正如您从扩展中看到的那样,这现在是安全的:

(defregion2 *thing* 
            (expression-involving x1 x2)
            (another-expression-involving x1 x2))

扩展到

(defparameter *thing*
  (let ((l1 (expression-involving x1 x2))
        (l2 (another-expression-involving x1 x2)))
    (with-slots ((x1 x) (x2 y)) l1
      (with-slots ((x2 x) (y2 y)) l2
        (make-instance 'region :x x1 :y y1 :w (- x2 x1) :h (- y2 y1))))))

可以看到(another-expression-involving x1 ...)里面的x1不是with-slots绑定的那个。