6

I'm having trouble wrapping my head around how to mix clojure and core.logic.

For example say I have the following problem:

I have a list of key value pairs with attached scores:

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

And I also have a map:

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

What I'd like to do, is return a list of scores based on the list of scores evaluated in terms of the map.

Using core logic I can do something like this:

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

And I get the intended result:

(2 7)

My problem is I don't see how to turn this into something that I can run in a larger program dynamically. Meaning that I will have different maps and different constraints to apply at different times. I think I can create the argument to conde by writing a function that takes the map and outputs the constraints, but how do I have that run* evaluate in the context of a set of temporary facts?

I could certainly write a function to return what I want without core.logic, but that seems less elegant. Maybe I'm barking up the wrong tree (I'm new to both Clojure and core.logic) and this isn't a constraint problem at all.

So my question are:

How do you mix in core logic when you're pulling your facts and constraints from a sources you won't know until runtime?

And related, how do you do so in an evironment where you want to evaluate a set of constraints within a new environment of facts?

4

2 に答える 2

4

覚えておくべき最も重要なことはこれです:関係はただ目標を返す関数です。目標は、succeedまたはできる関数であるfailため、基本的に関係は高階関数にすぎません。

これで、関係とさまざまなファクトが1つの関数に含まれ、相互に干渉する可能性のある「グローバル」な関係/ファクトがないように例を作成できます。

(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マクロを使用)

これにより、ローカル/グローバル関係の問題は解決されますが、それでもファクトとクエリは、パラメーターのように渡される関数にハードコードされます。そのための1つの可能な方法は、動的ロジック変数を定義してそれらを統合することを可能にするcore.logic APIを理解することです。私はそのAPIを使用したことがないため、それを使用して答えることはできません。別の方法は、マクロと評価の魔法を使用することです。

(defmacro find-things-generator [data query]
  (let [key (gensym) value (gensym) score (gensym) q (gensym)]
    `(letfn [(~'scoref [~key ~value ~score]
               (conde
                ~@(map #(-> [`(== ~key ~(% 0))
                             `(== ~value ~(% 1))
                             `(== ~score ~(% 2))]) data)
                ))]
       (run* [~q]
             (conde
              ~@(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)
于 2013-03-07T05:27:23.030 に答える
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) です。

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

于 2013-03-28T02:52:47.423 に答える