2012-01-28 31 views
2

我正在编写一个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?

回答

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,因为有相当不错的两种解释常见的使用情况。根据这些信息,最好在你的方案中使用一个协议。

+0

感谢您展示如何做到这一点。我喜欢这里的multimethods的简单性。我可能只需要根据它是seq或字符串还是其他任何地方来改变格式,所以我可能会避免对接口进行硬编码。 – devth 2012-01-31 00:08:56

1

(我是小白,但)它看起来像一个协议将是最适合的:

http://clojure.org/protocols

然后你就可以定义不同的格式实现每个数据类型你想支持。

1

我相信你的问题是显示一个相对于你真正需要完成的简化案例。对于一般的解决方案,我同意协议是一个体面的方法。

你问过关于multimethods的问题,但遇到麻烦的是调度功能。 defmulti需要一个调度函数,这个函数将被调用到实函数的参数上。调度函数必须返回一个值,然后可以使用该值来选择将调用哪个方法实现。

问题是,你发什么?要集合类型区分,你最终的东西是这样的:

(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 interop一起工作,它们不包括恰巧实现Seqable的用户定义类型...总之,他们是抽象的细分。

任何时候你都会试图派发类名,无论是来自Clojure,Java还是第三方库,你都应该总是达到扩展类型。

+1

对于我自己的学习 - 是否有理由拒绝Clojure内置的特殊分层系统,并记录在http://clojure.org/multimethods上? (你没有提到它,并且似乎假设在具体类上派遣,似乎正在将Java读回到Clojure中。) – 2012-01-30 15:41:21

+1

最初的问题是使用文字作为数据结构。如果有方法在{}和[]之上构建临时层次结构,我不知道它。 我在指出,使用多方法必然会吸引Clojure数据结构下的实现类(用Java编写)。这就是为什么我不喜欢这种多方法。 – mtnygard 2012-01-31 18:18:45

相关问题