【问题标题】:Suppress namespace when printing ::keyword打印 ::keyword 时禁止命名空间
【发布时间】:2015-11-01 13:51:35
【问题描述】:

当我写作时

 (println '(:keyword1 :keyword2))

我得到以下输出:

(:keyword1 :keyword2)

没关系。

但是当我写的时候

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

我明白了

(:namespace/keyword1 :namespace/keyword2)

我想得到

(::keyword1 ::keyword2)

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

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

感谢您的建议。

【问题讨论】:

    标签: 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】:

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

        “如何添加一个命名空间,然后假装它不存在?”

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-09-13
          • 1970-01-01
          • 2015-05-04
          • 1970-01-01
          • 2021-04-06
          • 2019-10-27
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多