【问题标题】:Suppress namespace when printing ::keyword打印 ::keyword 时禁止命名空间
【发布时间】:2015-11-01 13:51:35
【问题描述】:
【问题讨论】:
标签:
printing
clojure
keyword
suppress
【解决方案1】:
Clojure 中的打印机器基于多种方法。可以利用它来更改命名空间关键字的打印方式。不过,改变关键字的打印方式可能并不明智。
用于产生供人类消费的输出的多方法称为print-method。它需要两个参数 - 要打印的对象和输出 Writer。粗略地说,分派是根据正在打印的对象的类型执行的。因此可以为clojure.lang.Keyword 类型重新定义print-method。该方法的default implementation可以作为参考。
还需要确定关键字是否具有命名空间组件——这是我们可以确定是否应该打印一个或两个冒号的方法。 Keyword 类中的 getNamespace 方法将完成这项工作。在这一点上,我们对 Clojure 实现细节的了解太深了。风险自负。
(import 'clojure.lang.Keyword)
;=> clojure.lang.Keyword
(import 'java.io.Writer)
;=> java.io.Writer
(defmethod print-method Keyword [^Keyword k, ^Writer w]
(if (.getNamespace k)
(.write w (str "::" (name k)))
(.write w (str k))))
;=> #object[clojure.lang.MultiFn 0x24243e9f "clojure.lang.MultiFn@24243e9f"]
(println ::foo)
;=> ::foo
;=> nil
【解决方案2】:
问得好,我也觉得这种行为很烦人。如果你想要一个相对本地的修复,这里有一个宏,它会暂时改变关键字的打印行为并抑制命名空间限定的映射表示法,这在pprint 中很烦人。请注意,这些更改将在宏主体期间的所有线程中可见,而且这将打印关键字比正常方法慢得多。然而,大多数时候这不是问题。
如果限定关键字的命名空间在当前命名空间中有别名,它将打印具有该别名的关键字。
(defn short-keyword-print [^clojure.lang.Keyword kw, ^Writer w]
(if-let [ns-str (.getNamespace kw)]
(if-let [ns (find-ns (symbol ns-str))]
(if (= ns *ns*)
(.write w (str "::" (name kw)))
(if-let [ns-alias (some #(when (= ns (val %)) (key %)) (ns-aliases *ns*))]
(.write w (str "::" ns-alias "/" (name kw)))
(.write w (str "::?/" (name kw)))))
(.write w (str "::?/" (name kw))))
(.write w (str ":" (name kw)))))
(defmacro with-short-keys [& body]
`(binding [*print-namespace-maps* false]
(let [old-kw-method# (get (methods print-method) clojure.lang.Keyword)]
(. ~(with-meta `print-method {:tag 'clojure.lang.MultiFn})
addMethod clojure.lang.Keyword short-keyword-print)
(try
~@body
(finally
(. ~(with-meta `print-method {:tag 'clojure.lang.MultiFn})
addMethod clojure.lang.Keyword old-kw-method#))))))
然后:
(require '[clojure.set :as set])
(with-short-keys
(dorun
(map println
[:x ; unqualified
::x ; qualified with this namespace
::set/x ; qualified with aliased namespace
:clojure.set/x ; qualified with aliased namespace, but not using the alias
:clojure.data/x ; qualified with real but un-aliased namespace
:some-other-namespace/x ; qualified with unreal un-aliased namespace
])))
打印出来:
:x ; unqualified
::x ; qualified with this namespace
::set/x ; qualified with aliased namespace
::set/x ; qualified with aliased namespace, but not using the alias
::?/x ; qualified with real but un-aliased namespace
::?/x ; qualified with unreal un-aliased namespace
【解决方案3】:
双冒号的目的是将当前命名空间插入到关键字中。单冒号表示法的目的是创建一个没有命名空间的关键字。因此,问是没有意义的,
“如何添加一个命名空间,然后假装它不存在?”
最好的答案是决定是否要为关键字添加命名空间,然后选择适当的技术。