尝试使宏类似于 defparameter、defvar 但宏仅 returns 一个 s 表达式
Trying to make macro similar to defparameter, defvar but macro only returns an s-expression
我正在尝试制作宏来定义类似于 defparameter
和 defvar
的各种对象。 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
绑定的那个。
我正在尝试制作宏来定义类似于 defparameter
和 defvar
的各种对象。 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
绑定的那个。