我正在将一些Scheme代码转换为Clojure。原始使用的调度模式与多方法非常相似,但是对匹配谓词采用反向方法。例如,有一个通用函数“assign-operations”。精确的实现细节目前并不太重要,但请注意它可以采用参数谓词列表。
(define (assign-operation operator handler . argument-predicates)
(let ((record
(let ((record (get-operator-record operator))
(arity (length argument-predicates)))
(if record
(begin
(if (not (fix:= arity (operator-record-arity record)))
(error "Incorrect operator arity:" operator))
record)
(let ((record (make-operator-record arity)))
(hash-table/put! *generic-operator-table* operator record)
record)))))
(set-operator-record-tree! record
(bind-in-tree argument-predicates
handler
(operator-record-tree record)))))
调度函数提供这些谓词,函数arity中每个参数一个。
(assign-operation 'merge
(lambda (content increment) content)
any? nothing?)
(assign-operation 'merge
(lambda (content increment) increment)
nothing? any?)
(assign-operation 'merge
(lambda (content increment)
(let ((new-range (intersect-intervals content increment)))
(cond ((interval-equal? new-range content) content)
((interval-equal? new-range increment) increment)
((empty-interval? new-range) the-contradiction)
(else new-range))))
interval? interval?)
稍后,当调用泛型函数“merge”时,会询问每个处理程序是否对操作数有效。
据我了解multimethods,dispatch函数是在整个实现集中定义的,并根据dispatch-fn的返回值调度到特定方法。在上面的Scheme中,新的赋值操作函数可以任意定义谓词。
在Clojure中,什么是等效的惯用结构?
编辑:上面的代码来自"The Art of the Propagator",Alexey Radul和Gerald Sussman。
答案 0 :(得分:3)
你可以很容易地使用Clojure的多方法做到这一点 - 诀窍是创建一个区分不同谓词集的调度函数。
最简单的方法可能只是维护一个“复合谓词”向量,将所有单个谓词应用于完整参数列表,并使用此向量的索引作为调度值:
(def pred-list (ref []))
(defn dispatch-function [& args]
(loop [i 0]
(cond
(>= i (count @pred-list)) (throw (Error. "No matching function!"))
(apply (@pred-list i) args) i
:else (recur (inc i)))))
(defmulti handler dispatch-function)
(defn assign-operation [function & preds]
(dosync
(let [i (count @pred-list)]
(alter pred-list conj
(fn [& args] (every? identity (map #(%1 %2) preds args))))
(defmethod handler i [& args] (apply function args)))))
然后你可以创建操作来处理你喜欢的任何谓词,如下所示:
(assign-operation (fn [x] (/ x 2)) even?)
(assign-operation (fn [x] (+ x 1)) odd?)
(take 15 (iterate handler 77))
=> (77 78 39 40 20 10 5 6 3 4 2 1 2 1 2)