方案-> Clojure:方法中带有谓词的多方法?

发布于 2024-12-07 17:25:46 字数 1805 浏览 0 评论 0原文

我正在将一些Scheme 代码转换为Clojure。原始版本使用与多方法非常相似的调度模式,但对匹配谓词采用了相反的方法。例如,有一个通用函数“分配操作”。目前,精确的实现细节并不是太重要,但请注意,它可以采用参数谓词列表。

(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)))))

分派函数提供这些谓词,函数数量中的每个参数一个。

(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”时,会询问每个处理程序是否适用于操作数。

据我了解多方法,调度函数是跨一组实现定义的,并根据dispatch-fn的返回值调度到特定方法。在上面的方案中,新的赋值操作函数可以任意定义谓词。

Clojure 中等效的惯用构造是什么?

编辑:上面的代码来自“传播者的艺术”,作者:Alexey Radul 和杰拉德·萨斯曼.

I'm converting some Scheme code to Clojure. The original uses a dispatching pattern that's very similar to multimethods, but with an inverted approach to the matching predicates. For example, there a generic function "assign-operations". The precise implementation details aren't too important at the moment, but notice that it can take a list of argument-predicates.

(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)))))

The dispatched functions supply these predicates, one per argument in the arity of the function.

(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?)

Later, when the generic function "merge" is called, each handler is asked if it works on the operands.

As I understand multimethods, the dispatch function is defined across the set of implementations, with dispatch to a specific method based on the return value of the dispatch-fn. In the Scheme above, new assign-operation functions can define predicates arbitrarily.

What would be an equivalent, idiomatic construct in Clojure?

EDIT: The code above comes from "The Art of the Propagator", by Alexey Radul and Gerald Sussman.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

一指流沙 2024-12-14 17:25:46

您可以使用 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)

You can do this with Clojure's multimethods fairly easily - the trick is to create a dispatch function that distinguishes between the different sets of predicates.

The easiest way to do this is probably just to maintain a vector of "composite predicates" that apply all of the individual predicates to the full argument list, and use the index of this vector as the dispatch value:

(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)))))  

Then you can create operations to handle whatever predicates you like as follows:

(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)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文