core.logic:如何生成重复的数字序列?

时间:2016-04-05 18:07:36

标签: clojure

我试图使用核心生成一个数字序列((1),(1,2),(1,2,1),(1,2,1,2)...)。逻辑:

(defn generator
  ([q] (generator q '()))
  ([q r]
   (lc/fresh [rw rws]
     (lc/conso 1 r rw)
     (lc/conjo rw 2 rws)
     (lc/conde
      [(lc/== q rw)]
      [(lc/== q rws)]
      [(generator q rws)]))))

(lc/run 6 [q]
    (generator q)))

但是我得到了这个例外

clojure.core.logic.LCons无法强制转换为clojure.lang.IPersistentCollection

相反,如果我用conso替换conjo:

(defn generator
  ([q] (generator q '()))
  ([q r]
   (lc/fresh [rw rws]
     (lc/conso 1 r rw)
     (lc/conso 2 rw rws)
     (lc/conde
      [(lc/== q rw)]
      [(lc/== q rws)]
      [(generator q rws)]))))

我得到了正确的结果,但顺序不正确:

((1),(2,1),(1,2,1),(2,1,2,1)...)

为什么我在使用conco而不是使用conso时出错?

我应该如何按正确的顺序生成序列?

1 个答案:

答案 0 :(得分:1)

解决方案

首先,这是一种利用特定问题结构的方法:

(defn generator
  ([q] (generator q '(1) '(1 2)))
  ([q x y]
   (l/fresh [x' y']
     (l/appendo '(1 2) x x')
     (l/appendo '(1 2) y y')
     (l/conde
       [(l/== q x)]
       [(l/== q y)]
       [(generator q x' y')]))))

在REPL:

(l/run 6 [q] (generator q))
;= ((1) (1 2) (1 2 1) (1 2 1 2) (1 2 1 2 1) (1 2 1 2 1 2))

请注意,这非常有效,因为它只会将项目添加到列表中。

为什么原始方法不起作用?

在表面上,conso使用传统的Lisp cons单元格,使用core.logic内部类型表示。这些不是常规的Clojure持久集合,conjo不接受它们作为参数。

在更深层次上,即使conjo接受lcons单元格作为参数,这里它实际上等同于conso,因为conjconjo的功能对应物)将项目预先添加到列表中(这里的lconses表示列表)。这是因为列表只支持有效预占;在列表末尾添加一个项目涉及重建整个列表,因此是一个O(n²)操作。

最后,即使conjo可以在列表末尾追加项目,但仍然无效,因为你会得到一系列

((1) (1 2) (1 1 2) (1 1 2 2) (1 1 1 2 2) (1 1 1 2 2 2) …)

事实上,您可以使用appendo对core.logic执行此操作 - 尝试使用(appendo rw '(2) rws)代替您conjo来电。{/ p>

如果您愿意,可以采用原始方法,将conso替换为conjo,将()文字替换为[]字面值,然后运行generator获取一系列向量:

([1] [1 2] [1 2 1] [1 2 1 2] [1 2 1 2 1] [1 2 1 2 1 2])

这是因为向量支持在右端有效附加元素。