2013-03-07 5 views
6

Ich habe Probleme, meinen Kopf darum herumzulegen, wie man clojure und core.logic mischt.Wie benutzt man core.logic in einem größeren Clojure-Programm?

Zum Beispiel sagen, ich habe folgendes Problem:

Ich habe eine Liste von Schlüssel-Wert-Paar mit angebautem Noten:

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

Und ich habe auch eine Karte:

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

Was ich tun möchte, ist, eine Liste der Ergebnisse auf der Grundlage der Liste der Bewertungen, die in Bezug auf die Karte bewertet wurden, zurückzugeben.

Mit Core-Logik kann ich so etwas tun:

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

Und ich bekomme das gewünschte Ergebnis:

(2 7) 

Mein Problem ist, ich sehe nicht, wie dies verwandeln sich in etwas, das Ich kann dynamisch in einem größeren Programm laufen. Das bedeutet, dass ich verschiedene Karten und verschiedene Einschränkungen haben werde, um sie zu verschiedenen Zeiten anzuwenden. Ich denke, ich kann das Conde-Argument erstellen, indem ich eine Funktion schreibe, die die Map aufnimmt und die Constraints ausgibt, aber wie kann ich das im Zusammenhang mit einer Menge temporärer Fakten bewerten? ?

Ich könnte sicherlich eine Funktion schreiben, um zurückzukehren, was ich ohne core.logic will, aber das scheint weniger elegant. Vielleicht bell ich den falschen Baum (ich bin neu in Clojure und core.logic) und das ist überhaupt kein Einschränkungsproblem.

Also meine Frage lautet:

Wie Sie in Kernlogik mischen, wenn Sie Ihre Tatsachen und Abhängigkeiten von einer Quellen sind Ziehen Sie erst zur Laufzeit wissen?

Und wie sehen Sie dies in einer Umgebung, in der Sie eine Reihe von Einschränkungen in einer neuen Umgebung von Fakten bewerten möchten?

Antwort

4

Die wichtigste Sache zu erinnern ist diese: Beziehungen sind nur Funktion, die ein Ziel zurückbringen. Ein Ziel ist eine Funktion, die succeed oder fail, so im Grunde Beziehungen sind nur höhere Ordnung Funktion.

Jetzt können Sie Ihr Beispiel so machen, dass die Beziehung und die verschiedenen Tatsachen in einer einzigen Funktion sind, und es gibt keine „global“ Beziehung/Fakten, die miteinander interferieren können:

(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 ist nur eine Funktion, die ein Ziel zurückgibt (unter Verwendung des Makros conde)

Dies löst das Problem der lokalen/globalen Beziehungen, aber die Fakten und die Abfrage sind weiterhin in die Funktion festgeschrieben, die wie in Parametern übergeben werden soll. Eine Möglichkeit, das zu tun, ist, die core.logic-APIs zu verstehen, mit denen man dynamische Logic-Variablen definieren und vereinheitlichen kann usw. Ich habe diese API nicht benutzt, daher kann ich sie nicht beantworten.Ein anderer Weg wäre Magie Makro- und eval zu verwenden:

(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

Vielen Dank. Das hat mich auf den richtigen Weg gebracht. Ich muss das Makro durcharbeiten, aber das sollte mir sehr helfen und ich bin mir ziemlich sicher, dass core.logic die richtige Lösung für das Problem ist, das ich lösen möchte. – jgerman

+0

Siehe auch Wiki zum Erweitern von core.logic mit API https://github.com/clojure/core.logic/wiki/Extending-core.logic-%28Datomic-example%29 –

3

Ich hatte eine ähnliche Frage, und hier ist, was ich kam mit, übersetzt, um Ihr Problem.

Definieren Sie Ihre Sammlung von Partituren.

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

Als nächstes definieren Sie eine Funktion, die die Scores in relationale Form umwandelt.

Schließlich, bestimmen, welche Punkte im Vektor gegeben Schlüssel und Werte.

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

Das Ergebnis ist (2 7).

Die Abfrage ist anders formuliert, aber es ist äquivalent.

+0

Was ist mit dynamischen Abfragen? – fmjrey