2013-03-07 14 views
6

私はclojureとcore.logicをどのようにミックスするかについて頭を悩ましています。例えばClojureの大きなプログラムでは、core.logicを実用的な方法でどのように使用しますか?

は、私は次のような問題を持っていると言う:

私は付属のスコアがキーと値のペアのリストを持っている:

(:foo "10" 2) 
(:bar "20" 3) 
(:baz "30" 7) 

をそして私はまた、マップを持っている:

{:foo "10", 
:bar "42", 
:baz "30"} 

私がしたいことは、地図の点で評価されたスコアのリストに基づいて得点のリストを返すことです。私はこのような何かを行うことができ、コア・ロジックを使用して

(defrel score key value score) 
(fact score :foo "10" 2) 
(fact score :bar "20" 3) 
(fact score :baz "30" 7) 

(run* [q] 
    (conde 
    ((score :foo "10" q)) 
    ((score :baz "30" q)))) 

そして、私は意図した結果を得る:

(2 7) 

を私の問題は、私はその何かにこれを有効にする方法を見ていないです私は大きなプログラムを動的に実行することができます。異なる時期に適用するために、私は異なる地図と異なる制約を持つことを意味します。私はマップをとり、制約を出力する関数を書くことで、condeに引数をつけることができると思いますが、実行するにはどうすればいいですか? 一時的な事実の集合の中で評価しますか?

core.logicを使用せずに私が望むものを返す関数を作成することはできますが、それほどエレガントではありません。たぶん私は間違ったツリー(私はClojureとcore.logicの両方に新しい)を吠えているかもしれません。これは制約の問題ではありません。

だから私の質問は以下のとおりです。あなたが実行時まで知ることができませんソースからのあなたの事実と制約を引いているとき

はどのようにして、コア・ロジックに混ぜていますか?

関連する新しい環境のファクト内で一連の制約を評価する環境ではどうしますか?

答えて

4

覚えておくべき最も重要なことは、次のとおりです。関係は単なる目標を返す関数です。 ゴールはsucceedまたはfailのような関数なので、基本的には上位関数です。

(defn find-things [] 
    (letfn [(scoref [key value score] 
      (conde 
      [(== key :foo) (== value "10") (== score 2)] 
      [(== key :bar) (== value "20") (== score 3)] 
      [(== key :baz) (== value "30") (== score 7)]))] 
    (run* [q] 
      (conde 
      ((scoref :foo "10" q)) 
      ((scoref :baz "30" q)))))) 

scoreだけである:あなたが関係して様々な事実が一つの関数であり、互いに干渉することができない「グローバル」関係/事実が存在しないようにあなたの例を作ることができる今

condeマクロを使用して返す関数

ローカル/グローバルリレーションの問題は解決されますが、ファクトとクエリはパラメータとして渡される関数にハードコードされます。 これを行うには、core.logic APIを理解しておく必要があります。このAPIを使用すると、動的ロジック変数を定義して統一することができます。そのAPIを使用していないため、これを使用して回答することはできません。もう一つの方法は、マクロとevalの魔法を使用することです:

(defmacro find-things-generator [data query] 
    (let [key (gensym) value (gensym) score (gensym) q (gensym)] 
    `(letfn [(~'scoref [~key ~value ~score] 
       (conde 
       [email protected](map #(-> [`(== ~key ~(% 0)) 
          `(== ~value ~(% 1)) 
          `(== ~score ~(% 2))]) data) 
       ))] 
     (run* [~q] 
      (conde 
       [email protected](map #(-> [`(~'scoref ~(% 0) ~(% 1) ~q)]) query) 
      ))))) 


(defn find-things [data query] 
    (eval `(find-things-generator ~data ~query))) 

(def data [[:foo "1" 2] 
      [:bar "2" 3] 
      [:baz "3" 7]]) 

(def query {:foo "1", 
      :bar "2", 
      :baz "3"}) 

(find-things data query) 
+0

大変ありがとうございます。これは正しい道で私を止めてしまった。私はマクロを使って作業する必要がありますが、これは多くの助けになるはずです。core.logicは私が解決したい問題の正しい解決策です。 – jgerman

+0

APIを使用してcore.logicを拡張する例もwikiを参照してくださいhttps://github.com/clojure/core.logic/wiki/Extending-core.logic-%28Datomic-example%29 –

3

は、私は同様の質問を持っていたし、ここで私はあなたの問題への翻訳、思い付いたものです。

あなたのスコアのコレクションを定義します。

(def scores 
    [[:foo "10" 2] 
    [:bar "20" 3] 
    [:baz "30" 7]]) 

次に、スコアをリレーショナル形式に変換する関数を定義します。

(defn scoreso [key value score scores] 
    (conde 
    [(fresh [a] 
     (firsto scores a) 
     (== [key value score] a))] 
    [(fresh [d] 
     (resto scores d) 
     (scoreso key value score d))])) 

最後に、指定されたキーと値に一致するベクトルのスコアを特定します。

(run* [score] 
    (fresh [key value] 
    (scoreso key value score scores) 
    (conde 
     [(== key :foo) (== value "10")] 
     [(== key :baz) (== value "30")]))) 

この結果は(2 7)です。

クエリの表現方法は異なりますが、同等です。

+0

動的クエリはどうですか? – fmjrey

関連する問題