我最近在Clojure上看过很多关于谓词调度的讨论,并想知道这件事是否有什么东西。换句话说,什么是谓词调度,它与通用函数,OOP多态和模式有何不同? 谢谢
答案 0 :(得分:22)
谓词调度包含泛型函数,OOP多态,模式匹配等。 Michael Ernst,Craig Kaplan和Craig Chambers对Predicate dispatching: A unified theory of dispatch进行了很好的概述。从它的摘要:
谓词调度通过允许任意谓词控制方法适用性并使用谓词之间的逻辑蕴涵作为覆盖关系来概括先前的方法调度机制。选择用于处理消息发送的方法不仅取决于参数的类,如在普通的面向对象的调度中,还取决于子组件的类,参数的状态以及对象之间的关系。
答案 1 :(得分:15)
编辑: Clojure多方法不是谓词调度。
在传统的面向对象编程中,多态意味着您可以拥有方法的多个实现,并且被调用的确切实现由您调用该方法的对象的类型确定。这是类型调度。
Clojure multimethods 对此进行扩展,以便任意函数可以决定调用哪个实现。在Clojure表单(defmulti name f)
中,函数f
是调度函数。
调度功能可以是class
,在这种情况下,您将返回类型调度。但是,该功能可以是其他任何功能:计算调度值,查找数据库中的内容,甚至调用Web服务。
True 谓词调度可能允许每个方法实现指定一个或多个调度函数(谓词)来决定何时应用该方法。这比多方法更通用,但实现起来更复杂。 Clojure不支持它。
通用函数是来自其他Lisp的术语。例如,Common Lisp提供了generic functions,它可以在类型上发送,还有一组受限制的其他函数。
答案 2 :(得分:8)
谓词调度是一种根据函数参数的数量,“形状”和值,为函数调用提供不同响应的方法。 Clojure函数已经调度到不同的代码体,具体取决于传递给函数的参数数量:
(defn my-func
([a] (* a a))
([a b] (* a b)))
Clojure multimethods增加了调度到不同方法的能力 - 可能在不同的命名空间中定义 - 基于调度参数的返回值(检查参数可以包括它们的数量,类和值)并识别哪些所有的方法。如Stuart Sierra的答案脚注中所述,多方法的创建者可以定义调度函数,通常不能修改它。此外,程序员必须为一个函数手工设计一个超复杂的调度函数,该函数对一个值为0的整数执行一个函数,对另一个正整数执行另一个函数。或者一个用于一个或多个项目的列表,另一个用于空列表。
谓词调度(可能)提供了一种语法,可以生成这个复杂的调度函数本身。例如,可以通过这种方式定义阶乘函数
(defmatch fact [0] 1)
(defmatch fact [n] (* n (fact (dec n))))
前一代码响应对
的调用(fact 0)
后一个代码用于具有任何其他值的单个参数的调用。这将(在幕后)定义一个带有调度函数的多方法,该函数将零与其他值区分开来。
但后来我可以通过编码
指定我想要地图的因子(可能)(defmatch fact [x {}] (fact (:value x)))
并且代码可以(理论上)拦截将地图传递给事实的调用,将其他调用委托给原始调度函数......所有这些都在幕后。
答案 3 :(得分:2)
要将谓词调度与多方法进行对比,有点像你定义了一个多方法而没有指定调度fn:
(defmulti my-method)
并且,当你想扩展它时,你没有指定一个调度值(因为没有生成它的解析fn),而是一个谓词:
(defmethod my-method (fn [a b] (and (vector? a) (vector? b)))
[a b]
(do something))
简单而有力。
问题是谓词可能重叠,而且您不希望在每次调用时检查所有可能的谓词。这就是为什么实现限制谓词的表达性(对于模式案例的相似性),以便能够聪明地理解它们(检测歧义,创建快速决策树等)。