如何编写基于所有参数类型分发的Clojure多方法
解决Clojure多方法按所有参数类型分发的问题
要实现根据所有传入参数的共同类型来分发的多方法,核心是编写一个能计算所有参数最具体公共类型的dispatch函数,再结合Clojure的类型层级来匹配对应的method。
步骤1:编写辅助函数计算公共类型
首先需要一个函数,接收一组类型,返回它们最具体的公共超类型(即最接近所有类型的父类型):
(defn common-type [types] (let [; 获取一个类型的所有超类型(包括自身) all-supers (fn [t] (cons t (supers t))) ; 将每个类型的超类型转为集合 supers-sets (map (comp set all-supers) types) ; 求所有超类型集合的交集,得到公共超类型 common-supers (apply clojure.set/intersection supers-sets)] ; 按超类型数量排序(越少越具体),取第一个就是最具体的公共类型 (first (sort-by #(count (supers %)) common-supers))))
步骤2:实现dispatch函数
接下来编写多方法的dispatch函数,处理参数为空的情况,并将公共类型转换为合适的dispatch值:
(defmulti foo (fn [& args] (if (empty? args) :empty ; 处理无参数的情况 (let [types (map type args) ct (common-type types)] ; 如果公共类型是Object,说明没有更具体的公共类型,返回:default (if (= ct Object) :default ct)))))
步骤3:定义对应的method
按照你的需求定义各个method:
(defmethod foo String [& args] (println "All strings")) (defmethod foo Long [& args] (println "All longs")) (defmethod foo Number [& args] (println "All numbers")) (defmethod foo :empty [& args] (println "No arguments")) (defmethod foo :default [& args] (println "Default"))
测试效果
现在测试不同参数组合的情况:
(foo "hello" "world") ; 输出: All strings (foo 1 2 3) ; 输出: All longs (foo 1 2.0 3N) ; 输出: All numbers(Long、Double、BigInteger都是Number的子类) (foo "a" 1) ; 输出: Default(String和Long没有除Object外的公共类型) (foo) ; 输出: No arguments
原理说明
common-type函数通过获取每个参数类型的所有超类型,找到它们的交集,再筛选出最具体的那个类型(超类型数量最少的就是最具体的)。- dispatch函数根据公共类型返回对应的类型标识,如果公共类型是
Object则返回:default,确保走默认分支。 - Clojure的多方法会严格匹配dispatch值和method的dispatch标识,因此当所有参数都是
Long时,会优先匹配Long的method,而不会触发Number的method,完全符合你的预期。
内容的提问来源于stack exchange,提问作者OParry
相关产品推荐
相关产品推荐

