打印 ::keyword 时抑制命名空间

Suppress namespace when printing ::keyword

写的时候

 (println '(:keyword1 :keyword2))

我得到以下输出:

(:keyword1 :keyword2)

没关系。

但是当我写

(println '(::keyword1 ::keyword2))

我明白了

(:namespace/keyword1 :namespace/keyword2)

我想得到

(::keyword1 ::keyword2)

可能吗?我只是用很多关键字进行符号计算,我需要打印它们。

我试过这个:https://clojuredocs.org/clojure.pprint/*print-suppress-namespaces* 但没有用。

感谢您的任何建议。

双冒号的目的是将当前命名空间插入到关键字中。单冒号表示法的目的是创建一个没有名称空间的关键字。因此,问

毫无意义

"How can I add a namspace and then pretend that it isn't there?"

最佳答案是决定是否要为关键字添加名称空间,然后选择适当的技术。

Clojure 中的打印机制基于多方法。可以利用它来更改命名空间关键字的打印方式。不过,改变关键字的打印方式通常并不明智。

用于产生人类消费输出的多种方法称为print-method. It takes two arguments - the object to print and the output Writer. The dispatch is performed on the type of the object being printed, roughly speaking. Thus it is possible to re-define the print-method for clojure.lang.Keyword type. The default implementation,该方法可作为参考。

还需要查明关键字是否具有名称空间组件 - 这就是我们确定是否应该打印一个或两个冒号的方法。 Keyword class 中的 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

问得好,我也觉得这种行为很烦人。如果你想要一个相对局部的修复,这里有一个宏会临时改变关键字的打印行为并抑制命名空间限定的映射符号,这在 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