格式化输出字符串取决于数据类型

时间:2012-01-28 21:50:58

标签: clojure

我正在编写一个clojure函数来将各种数据类型格式化为字符串。

我天真的解决方案:

(defn p [d]
       (cond
        (vector? d) (str "vector: " d)
        (list? d) (str "list: " d)))

#'user/p
user> (p [1 2 3])
"vector: [1 2 3]"
user> (p '(1 2 3))
"list: (1 2 3)"

我之前没有使用过多种方法。我这是一个很好的用途,还是有另一种方法来避免使用cond的臭味?

3 个答案:

答案 0 :(得分:5)

我会按照@rodnaph的建议,定义格式协议并将其扩展到您需要的类型:

(defprotocol Format
  (fmt [this]))

(extend-protocol Format
  clojure.lang.IPersistentVector
    (fmt [this] (str "vector:" this))
  clojure.lang.IPersistentList
    (fmt [this] (str "list:" this)))

但是我不知道哪个会有更好的性能,多方法或协议扩展。

多方法定义可能如下所示:

(defmulti fmt class)

(defmethod fmt 
  clojure.lang.IPersistentVector [this]
    (str "vector:" this))
(defmethod fmt 
  clojure.lang.IPersistentList [this]
    (str "list:" this))

编辑:您可能想要检查this question about protocols vs multimethods,因为很好地解释了两者的常见用例。根据该信息,最好在您的方案中使用协议。

答案 1 :(得分:1)

(我是一个菜鸟但是)看起来协议最适合这个:

http://clojure.org/protocols

然后,您可以为要支持的每种数据类型定义不同的格式化实现。

答案 2 :(得分:1)

我确信你的问题显示的是与你真正需要完成的事情相关的简化案例。对于一般解决方案,我同意协议是一种不错的方法。

你问过多方法,但是你遇到的麻烦就是调度功能。 defmulti接受一个调度函数,该函数将在实函数的参数上调用。 dispatch函数必须返回一个值,然后可以使用该值来选择将调用哪个方法实现。

麻烦的是,你发什么?要区分集合类型,您最终会得到类似的结果:

(defmulti stringify class)
(defmethod stringify clojure.lang.PersistentVector [v] ...)
(defmethod stringify clojure.lang.PersistentArrayMap [m] ...)
;;; More dispatching on concrete class names

好吧,只要你看到代码中出现特定的clojure.lang类名,就应该关闭所有类型的警钟。这些太具体了......如果Clojure核心库发生变化,它们将会破坏,它们将无法与Java互操作非常干净地工作,它们不包括碰巧实现Seqable的用户定义类型......简而言之,它们是抽象的细分。

每当你想要发送类名时,无论是来自Clojure,Java还是第三方库,你都应该总是到达extend-type。