6

我很难理解如何混合 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 的参数,但是我如何让 run* 在一组临时事实的上下文中进行评估?

我当然可以编写一个函数来返回我想要的没有 core.logic 的东西,但这似乎不太优雅。也许我在叫错树(我对 Clojure 和 core.logic 都是新手),这根本不是一个约束问题。

所以我的问题是:

当您从直到运行时才知道的来源中提取事实和约束时,您如何混合核心逻辑?

与此相关的是,您如何在一个新的事实环境中评估一组约束的环境中这样做?

4

2 回答 2

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,所以我无法使用它来回答。另一种方法是使用宏和评估魔法:

(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 回答