多方法比协议慢,应该尝试使用协议来解决问题,即使使用多方法提供了更灵活的解决方案。
那么cond
和多方法的情况如何呢?它们可用于解决同样的问题但我的猜测是多方法与cond
相比具有巨大的性能开销。如果是这样,我为什么要使用multimethod而不是cond
?
答案 0 :(得分:7)
多种方法允许开放式扩展;其他人可以在任意表达式上扩展你的多方法调度。 Cond表达式被其他人甚至您自己的代码关闭以扩展。
如果你只是想对条件逻辑采取行动,那么cond就是你要走的路。如果您想要进行更复杂的调度,或者对具有不同行为的多种类型的数据应用函数,那么多方法可能更合适。
答案 1 :(得分:6)
为什么要在测量时担心?
以下是使用criterium库的基准示例。 Cond
和Multi-methods
代码取自http://blog.8thlight.com/myles-megyesi/2012/04/26/polymorphism-in-clojure.html。
警告这只是比较multimethod
和cond
效果的基准测试示例。下面的结果 - 显示cond
的效果优于multimethod
,不能在实践中推广到各种用法。您可以将此基准测试方法用于您自己的代码。
;; cond (defn convert-cond [data] (cond (nil? data) "null" (string? data) (str "\"" data "\"") (keyword? data) (convert-cond (name data)) :else (str data))) (bench (convert-cond "yolo")) Evaluation count : 437830380 in 60 samples of 7297173 calls. Execution time mean : 134.822430 ns Execution time std-deviation : 1.134226 ns Execution time lower quantile : 133.066750 ns ( 2.5%) Execution time upper quantile : 137.077603 ns (97.5%) Overhead used : 1.893383 ns Found 2 outliers in 60 samples (3.3333 %) low-severe 2 (3.3333 %) Variance from outliers : 1.6389 % Variance is slightly inflated by outliers ;; multimethod (defmulti convert class) (defmethod convert clojure.lang.Keyword [data] (convert (name data))) (defmethod convert java.lang.String [data] (str "\"" data "\"")) (defmethod convert nil [data] "null") (defmethod convert :default [data] (str data)) (bench (convert "yolo")) Evaluation count : 340091760 in 60 samples of 5668196 calls. Execution time mean : 174.225558 ns Execution time std-deviation : 1.824118 ns Execution time lower quantile : 170.841203 ns ( 2.5%) Execution time upper quantile : 177.465794 ns (97.5%) Overhead used : 1.893383 ns nil
答案 2 :(得分:3)
为了跟进@AlexMiller评论,我尝试使用更多随机数据进行基准测试,并添加了协议实现(还添加了一个类型 - 整数 - 用于不同的方法)。
(defprotocol StrConvert
(to-str [this]))
(extend-protocol StrConvert
nil
(to-str [this] "null")
java.lang.Integer
(to-str [this] (str this))
java.lang.String
(to-str [this] (str "\"" this "\""))
clojure.lang.Keyword
(to-str [this] (to-str (name this)))
java.lang.Object
(to-str [this] (str this)))
data
包含10000个随机整数序列,随机转换为String
,nil
,keyword
或vector
。
(let [fns [identity ; as is (integer)
str ; stringify
(fn [_] nil) ; nilify
#(-> % str keyword) ; keywordize
vector] ; vectorize
data (doall (map #(let [f (rand-nth fns)] (f %))
(repeatedly 10000 (partial rand-int 1000000))))]
;; print a summary of what we have in data
(println (map (fn [[k v]] [k (count v)]) (group-by class data)))
;; multimethods
(c/quick-bench (dorun (map convert data)))
;; cond-itionnal
(c/quick-bench (dorun (map convert-cond data)))
;; protocols
(c/quick-bench (dorun (map to-str data))))
结果是data
包含:
([clojure.lang.PersistentVector 1999] [clojure.lang.Keyword 1949]
[java.lang.Integer 2021] [java.lang.String 2069] [nil 1962])
我肯定会建议@DanielCompton:设计比每个方法看起来对的纯粹表现更重要,至少在这个例子中。