core.logic 解释“新鲜”如何改变结果

core.logic explain how `fresh` changes results

刚开始使用 core.logic,版本“0.8.11”:

  (q/run* [q]
    (q/fresh [a]
      (q/membero a [2 3]))
    (q/membero q [1]))

我不明白结果:(1 1)

我的理解是我用fresh创建了另一个变量a,可以取2或3的值。 q 可以取 1 的值。因此我期待看到类似的东西:(1),或 (1 2 1 3),或者 ([1 2] [1 3]) 甚至 ({:q 1 :a 2} {:q 1 :a 3}),但不是实际结果。

另一个例子:

  (q/run* [q]
    (q/fresh [a]
      (q/membero a [1 2 3])
      (q/membero q [3 4 5])
      (q/== a q)))
  ;; make sense to me, returns (3)

  (q/run* [q]
    (q/fresh [a]
      (q/membero a [1 2 3]))
    (q/membero q [3 4 5]))
  ;; does not make sense to me, returns (3 4 3 5 4 3 5 4 5)
  ;; I was expecting `(3 4 5)`

有人能解释一下这里发生了什么吗?

core.logic 可以看作是一种搜索算法,搜索可能的方法来为所有相关变量赋值,而不会导致冲突。它有很多非常聪明的剪枝技术,所以它不会搜索已知不好的子树,但基本上它是一个搜索。

它找到了两种分配满足您查询的变量的方法:

  • a=2,q=1
  • a=3,q=1

所以它返回了两个结果。但是你只询问 q(这就是 run* 的参数,你想知道其值的变量集),并且 q 在这两个赋值中是相同的,所以你看到相同的结果( 1)两次。一般来说,您不应该假设 run* 的结果是不同的,除非您已付出一些努力使它们如此。

与上一个示例类似,您可以将三个值分配给 q,每个值都适用于您可以分配给 a 的三个值中的任何一个,因此您会得到 3*3=9 个结果,每个q值重复三次。这些结果的返回顺序(从您的角度来看)是任意的;实际上,它们的排序方式有助于 core.logic 防止在其他更复杂的程序中陷入僵局。如果你愿意,你可以看到所有的 a,q 对,写成 (q/run* [a q] ...).