1

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

(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)要获得这种格式的字符串表示形式,我需要进行哪些更改?

另外,我应该对上述代码进行哪些更改(如果有的话)以使其更惯用?

4

2 回答 2

4

只需替换~rname'~rname- 你想要实际的符号Foo,而不是它在当前范围内的值,即 class user.Foo

您也有(apply concat)错误(interpose ", ")的顺序 - 您将被", "视为一个序列,并添加\,\space字符而不是单个字符串。一旦你交换了它,很明显你想要mapcat而不是(apply concat (map ...)). 所以我会用类似的东西替换你的整个 let-block:

`(str '~rname
      "(" ~@(interpose ", "
                       (mapcat (fn [arg] [(str arg ": ") arg])
                               args))
      ")")

或者,如果您真的想提高性能,您可以~(str "(" rname)在编译时而不是在运行时进行计算。

但请注意,从 Clojure 1.3 开始,记录的打印方式与此非常相似,因此,如果您只是为了方便调试而这样做,请不要打扰。如果您这样做是因为您.toString稍后会使用 ' 的输出...那么,编写该代码的人感到羞耻,因为.toString主要用于调试内容。

于 2012-07-10T07:01:16.093 回答
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 ")")))))
于 2012-07-10T06:58:21.257 回答