似乎clojure.core.logic在步行集方面存在问题。 最小的失败示例:
(run* [q] (== q #{}))
产生
java.lang.StackOverflowError的 在clojure.core.logic.Substitutions.walk(logic.clj:344) 在clojure.core.logic $ walk_STAR_ $ fn_ 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_STAR_.invoke(logic.clj:214) 在clojure.core.logic $ walk_STAR_ $ fn_ 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_STAR_.invoke(logic.clj:214) 在clojure.core.logic $ walk_STAR_ $ fn_ 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_STAR_.invoke(logic.clj:214) 在clojure.core.logic $ walk_STAR_ $ fn_ 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)
为什么会生成Stackoverflow? 与空矢量/列表/地图/其他类型统一可以按预期工作。
答案 0 :(得分:5)
你有一个来自core.logic的原始作者的答案,不支持这些集合,但我认为你可以将你的问题提到更具领先地位,并且可能会对为什么<获得更有趣的回应/ em> sets不支持(还)或支持它们可能需要的东西。至于原因,我怀疑它们确实不需要,因为distincto
和permuteo
提供了可用于测试集属性的目标。至于在统一中支持它们可能需要做些什么,请按照下面的说明进行粗略,丑陋,不完整和低效的初看。
发生堆栈溢出是因为集合是集合,集合在行走时被递归。但是由于不支持集合,因此集合没有walk实现,对象的默认值是返回自身。最终的结果是,从步行集的角度来看,它包含了自己,并且堆栈正在试图递归到底部。
在查看source的同时加入我的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.combinatorics
代替它
(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)
再次看起来很有希望。
答案 1 :(得分:2)
。