我编写了以下宏,它定义了带有自定义字符串表示的记录。
(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)
。要以此格式获取字符串表示,我需要做出哪些更改?
此外,我应该对上述代码进行哪些更改,以使其更具惯用性?
答案 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 ")")))))