在编写宏时获取原始符号名称

时间:2012-07-10 06:26:56

标签: macros clojure lisp

我编写了以下宏,它定义了带有自定义字符串表示的记录。

(defmacro defrecord*
  [rname args]
  `(defrecord ~rname [~@args]
     Object
     (toString [_]
       ~(let [kvs (->> args
                       (map (fn [arg] [(str arg ": ") arg]))
                       (interpose ", ")
                       (apply concat))]
          `(str ~rname "(" ~@kvs ")")))))

然而,toString返回的内容并不是我所期待的。

(defrecord* Foo [bar baz]) 
(.toString (Foo. 3 4))

> "class user.Foo(bar: 3, baz: 4)"

在这种情况下,我希望我的toString返回Foo(bar: 3, baz: 4)。要以此格式获取字符串表示,我需要做出哪些更改?

此外,我应该对上述代码进行哪些更改,以使其更具惯用性?

2 个答案:

答案 0 :(得分:4)

只需将~rname替换为'~rname - 您需要实际符号Foo,而不是当前范围中的值,即user.Foo类。

您的(apply concat)(interpose ", ")顺序错误 - 您将", "视为序列,并添加\,和{{1} }而不是单个字符串。交换后,很明显您需要\space而不是mapcat。所以我用以下内容替换你的整个let-block:

(apply concat (map ...))

或者如果你真的想要研究这种性能,你可以在编译时而不是在运行时计算`(str '~rname "(" ~@(interpose ", " (mapcat (fn [arg] [(str arg ": ") arg]) args)) ")")

请注意,从Clojure 1.3开始,记录的打印方式与此非常相似,所以如果您只是这样做以简化调试,请不要打扰。如果您正在执行此操作,因为您稍后会使用~(str "(" rname)的输出...那么,对于编写该代码的人来说,羞耻,因为.toString是主要用于调试内容。

答案 1 :(得分:2)

只需将~rname更改为~(name rname)

(defmacro defrecord*
  [rname args]
  `(defrecord ~rname [~@args]
     Object
     (toString [_]
       ~(let [kvs (->> args
                       (map (fn [arg] [(str arg ": ") arg]))
                       (interpose ", ")
                       (apply concat))]
          `(str ~(name rname) "(" ~@kvs ")")))))