12

いくつかの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)))))

ディスパッチされた関数は、関数のアリティの引数ごとに1つずつ、これらの述語を提供します。

(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の同等の慣用的な構成は何でしょうか?

編集:上記のコードは、AlexeyRadulとGeraldSussmanによる「TheArtofthePropagator 」からのものです。

4

1 に答える 1

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)
于 2011-10-04T04:20:03.697 に答える