【问题标题】:Trying to make macro similar to defparameter, defvar but macro only returns an s-expression试图使宏类似于 defparameter,defvar 但宏只返回一个 s 表达式
【发布时间】:2020-07-21 11:39:57
【问题描述】:

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

(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* 也成为一个变量。这里有什么问题?

【问题讨论】:

    标签: lisp common-lisp


    【解决方案1】:

    你有两个嵌套的反引号。

    但是您的宏也是由内而外的:您真的希望 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绑定的那个。

    【讨论】:

    • 谢谢。至于intern,它一直在评估未绑定的变量,所以我试图修复它。但这似乎是嵌套反引号的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-16
    • 1970-01-01
    • 2019-04-29
    • 1970-01-01
    • 2020-06-22
    • 1970-01-01
    相关资源
    最近更新 更多