Clojure 使用集合时的core.logic堆栈溢出

Clojure 使用集合时的core.logic堆栈溢出,clojure,clojure-core.logic,Clojure,Clojure Core.logic,似乎clojure.core.logic在行走集合方面存在问题。 最小的失败示例: (运行*[q](==q{})) 产生 java.lang.StackOverflower错误 在clojure.core.logic.Substitutions.walk(logic.clj:344) 在clojure.core.logic$walk\u STAR\uu$fn\u 2633.invoke(logic.clj:216) 位于clojure.core.logic$eval2838$fn_2839.in

似乎clojure.core.logic在行走集合方面存在问题。 最小的失败示例:

(运行*[q](==q{}))

产生

java.lang.StackOverflower错误 在clojure.core.logic.Substitutions.walk(logic.clj:344) 在clojure.core.logic$walk\u STAR\uu$fn\u 2633.invoke(logic.clj:216) 位于clojure.core.logic$eval2838$fn_2839.invoke(logic.clj:956) 在clojure.core.logic.protocols$eval1389$fn_1390$G_1380_1397.invoke(protocols.clj:55) 在clojure.core.logic$walk\u STAR\u.invoke(logic.clj:214) 在clojure.core.logic$walk\u STAR\uu$fn\u 2633.invoke(logic.clj:218) 位于clojure.core.logic$eval2838$fn_2839.invoke(logic.clj:956) 在clojure.core.logic.protocols$eval1389$fn_1390$G_1380_1397.invoke(protocols.clj:55) 在clojure.core.logic$walk\u STAR\u.invoke(logic.clj:214) 在clojure.core.logic$walk\u STAR\uu$fn\u 2633.invoke(logic.clj:218) 位于clojure.core.logic$eval2838$fn_2839.invoke(logic.clj:956) 在clojure.core.logic.protocols$eval1389$fn_1390$G_1380_1397.invoke(protocols.clj:55) 在clojure.core.logic$walk\u STAR\u.invoke(logic.clj:214) 在clojure.core.logic$walk\u STAR\uu$fn\u 2633.invoke(logic.clj:218) 位于clojure.core.logic$eval2838$fn_2839.invoke(logic.clj:956) 在clojure.core.logic.protocols$eval1389$fn_1390$G_1380_1397.invoke(protocols.clj:55)

为什么会产生堆栈溢出?
与空向量/列表/映射/其他类型的统一工作正常。

集合在core.logic中不受支持。

您从core.logic的原始作者那里得到的答案是集合不受支持,但是我认为你可以把你的问题表达得更具引导性,并且可能会得到一个更有趣的回答,关于为什么集合(现在)不被支持,或者需要什么来支持它们。至于原因,我怀疑它们确实不需要,因为
distincto
permuteo
提供了可用于测试集合属性的目标。至于需要什么来支持他们的统一,请跟随下面的内容,看一看粗糙、丑陋、不完整和低效的第一眼

堆栈溢出发生的原因是集合是集合,集合在遍历时被递归到集合中。但由于不支持集合,因此集合没有walk实现,对象的默认值是返回自身。最终的结果是,从walk的角度来看,集合包含它们自己,堆栈被吹到试图递归到底部

请在REPL上加入我,同时查看,让我们一起做些事情

(use 'clojure.core.logic)
(use 'clojure.core.logic.protocols)
让我们告诉core.logic使用序列的现有实现遍历集合

(extend-protocol IWalkTerm 
  clojure.lang.IPersistentSet 
  (walk-term [v f] (with-meta (set (walk-term (seq v) f)) (meta v))))

(run* [q] (== q []))
;=> ([])
(run* [q] (== q #{}))
;=> (#{})
很好,到目前为止

(run* [q] (== q [1 2 3]))
;=> ([1 2 3])
(run* [q] (== q #{1 2 3}))
;=> (#{1 2 3})
一致,但不是非常有用

(run* [q] (== [1 q 3] [1 2 3]))
;=> (2)
(run* [q] (== #{1 q 3} #{1 2 3}))
;=> ()
(run* [q] (== #{1 3 q} #{1 2 3}))
;=> ()
现在我们有一个问题。最后两个都应该返回
(2)
,因为集合没有顺序,但都不返回结果。我们还需要告诉core.logic如何统一集合。让我们懒惰一点,试着用现有的
permuteo
来表达缺乏秩序

(extend-protocol IUnifyTerms 
  clojure.lang.IPersistentSet 
  (unify-terms [u v s] (bind s (permuteo (seq u) (seq v)))))

(run* [q] (== #{1 q 3} #{1 2 3}))
;=> (2)
(run* [q] (== #{3 1 q} #{1 2 3})) 
;=> (2)
好极了

(run* [q] (fresh [a1 a2 a3] (== #{a1 a2 a3} #{1 2 3}) (== q [a1 a2 a3])))
;=> ([1 2 3] [2 1 3] [1 3 2] [3 1 2] [2 3 1] [3 2 1])
很酷

(run* [q] (== #{1 2 [3 q]} #{1 2 [3 4]}))
;=> (4)
很好…但是

(run* [q] (== #{1 2 #{3 q}} #{1 2 #{3 4}}))
;=> IllegalArgumentException No implementation of method: :walk of protocol:  #'clojure.core.logic.protocols/ISubstitutions found for class: clojure.core.logic$permuteo$fn...
所以我们在使用
permuteo
时有点太草率了,让我们试着用
clojure.math.combinationics
来代替它

(use 'clojure.math.combinatorics)

(extend-protocol IUnifyTerms 
  clojure.lang.IPersistentSet 
  (unify-terms [u v s]
    (when (set? v)
      (let [u (seq u)
            v (seq v)]
        (reduce #(mplus % (-inc %2)) 
                (for [p (permutations u)] (unify s p v))))))) 
现在

(run* [q] (== #{1 2 #{3 q}} #{1 2 #{3 4}}))
;=> 4

(run* [q] (== #{ #{ #{q} :bar} :baz}  #{:baz #{:bar #{:foo} } }))
;=> (:foo)

看起来又很有希望了。

我不承认我对core.logic.Edited只有最基本、最基本、最粗糙和最笨拙的理解,删除了“读者练习”;然而,对于有意愿的人来说,还有很多改进。例如,整个集合上的排列通常会产生太多的工作(对于计算机来说——对于我来说是最少的工作量!)。更好的做法是首先提取两个集合的共同元素。