首页 > 解决方案 > 创建一个宏以返回一个 reify(java 接口),但在 clojure 中提供了带引号的表达式

问题描述

我想要实现的是在宏内部实现一个带有 reify 的抽象类,但是应该在扩展时间返回的表达式将提供给引用的宏:

(defmacro a-printable
[body]
`(reify Printable
 (print [this g# pf# page#]
  (if (= page# 0)
   (do
    ~body ;; the supplied part
    (Printable/PAGE_EXISTS))
   (Printable/NO_SUCH_PAGE)))

(def exp '(do (.translate g (.getImageableX pf) (.getImageableY pf))
              (.drawString g "foo" 10 10))) ;; the form to pass
(a-printable exp)

问题是在我传递的表达式中,我想使用在宏和 reify 内部定义的自动生成的变量g#, pf#。我尝试添加带引号的表达式, (ns-resolve *ns* g) (ns-resolve *ns* pf)但不缺少,我不确定在宏内部是否已解决。gisjava.awt.Graphics是一个抽象类,ispfjava.awt.print.PageFormat带有构造函数的普通类。有谁知道如何实现这一目标,或者让我转向正确的方向?

标签: clojure

解决方案


我相信诀窍是,如果您不想在宏中使用命名空间符号,您可以在它们前面加上~', 例如~'g. 然后我对您的宏进行了以下其他修改:

  1. body为参数加上前缀&以使其可变长度。
  2. 将body拼接成宏
  3. (Printable/PAGE_EXISTS)删除and周围的括号(Printable/NO_SUCH_PAGE):这些是您要返回的静态变量值,而不是函数调用。

这是固定宏的样子:

(defmacro a-printable
  [& body]
  `(reify Printable
     (print [~'this ~'g ~'pf ~'page]
       (if (= ~'page 0)
         (do
           ~@body ;; Splice it!
           Printable/PAGE_EXISTS)
         Printable/NO_SUCH_PAGE))))

这就是您创建实例的方式。请注意,我不需要将参数包装到宏中do

(def p (a-printable
        (.translate g (.getImageableX pf) (.getImageableY pf))
        (.drawString g "foo" 10 10)))

但是请注意:我不确定引入新符号是否是一种好习惯,例如pfand g,但我找不到提到为什么这是一种不好的做法的参考资料。有一些方法可以在不求助于宏的情况下实现与此问题中所要求的类似的事情。使用宏的版本也长不了多少:

(defn a-printable-fn [body-fn]
  (reify Printable
    (print [this g pf page]
      (if (= ~'page 0)
        (do
          (body-fn this g pf page)
          Printable/PAGE_EXISTS)
        Printable/NO_SUCH_PAGE))))


(def p (a-printable-fn
        (fn [this g pf page]
          (.translate g (.getImageableX pf) (.getImageableY pf))
          (.drawString g "foo" 10 10))))

推荐阅读