我试图使用核心生成一个数字序列((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时出错?
我应该如何按正确的顺序生成序列?
答案 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
,因为conj
(conjo
的功能对应物)将项目预先添加到列表中(这里的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])
这是因为向量支持在右端有效附加元素。