Устранение повторяющихся результатов при запросе генеалогического дерева с помощью core.logic
Я моделирую семейное дерево с core.logic. Я хотел бы run*
запросить и вернуть все результаты без дублирования. Замена всего defn
на def tabled
дает мне результаты, которые я ожидаю (на данный момент, по крайней мере), и я знаю, что condu
и onceo
могут уменьшить количество результатов, но я не уверен, что любой из этих являются лучшим способом устранения дубликатов.
Меня особенно беспокоит мой нынешний подход, потому что это похоже на дублирующую работу, чтобы объявить как отношения, так и функции. Я знаю, что некоторые из моих отношений "взаимно рекурсивные" (mothero
и womano
ссылаются друг на друга), но я сделал это, потому что в будущем я мог бы добавить новый (defrel mother*)
, который должен позволить ему сделать вывод о том, что мать является родителем и женщиной.
(defrel man* person)
(defrel woman* person)
(defrel parent* child father)
(fact man* :Father)
(fact woman* :Mother)
(fact man* :Son)
(fact woman* :Daughter)
(fact parent* :Son :Father)
(fact parent* :Son :Mother)
(fact parent* :Daughter :Father)
(fact parent* :Daughter :Mother)
(defn mano [person]
(conde
[(man* person)]
[(fresh [c]
(fathero c person))]))
(defn womano [person]
(conde
[(woman* person)]
[(fresh [c]
(mothero c person))]))
(defn parento [child person]
(conde
[(parent* child person)]
[(mothero child person)]
[(fathero child person)]))
(defn fathero [child father]
(all
(mano father)
(parento child father)))
(defn mothero [child mother]
(all
(womano mother)
(parento child mother)))
(defn siblingso [c1 c2 mother father]
(all
(mothero c1 mother)
(mothero c2 mother)
(fathero c1 father)
(fathero c2 father)
(!= c1 c2)))
(run 10 [q]
(fresh [child parent]
(parento child parent)
(== q [child parent])))
(run 10 [q]
(fresh [c1 c2 p1 p2]
(siblingso c1 c2 p1 p2)
(== q [c1 c2 p1 p2])))
Ответы
Ответ 1
Не уверен, что именно вы пытаетесь достичь, но цели (вещи, заканчивающиеся на "o" ) кажутся (как вы сказали) излишними, и они есть. Кроме того, вы не можете запустить parento
с run*
, потому что ограничений на ваши запросы нет. Он попытается вернуть бесконечный список дочерних родительских пар. Вот несколько примеров запросов, использующих ваши отношения:
;; find all child-parent pairs
(run* [q] (fresh [c p] (parent* c p) (== q [c p])))
;=> ([:Daughter :Mother] [:Son :Mother] [:Daughter :Father] [:Son :Father])
;; find all child-father pairs
(run* [q] (fresh [c p] (parent* c p) (man* p) (== q [c p])))
;=> ([:Daughter :Father] [:Son :Father])
;; find all daughter-father pairs
(run* [q] (fresh [c p] (parent* c p) (man* p) (woman* c) (== q [c p])))
;=> ([:Daughter :Father])
;; some new facts
(fact parent* :grand-child :Son)
(fact parent* :great-grand-child :grand-child)
;; find all people who are grandparent
(run* [q] (fresh [c p gp] (parent* c p) (parent* p gp) (== q [gp])))
;=> ([:Mother] [:Father] [:Son])
И вы можете так долго продолжать. Логическое программирование делает очень мощный язык запросов даже при использовании только с простыми отношениями.
Обновить. Здесь приведен пример brothero
, где вторым аргументом должен быть брат:
(defn brothero [a b]
(fresh [f]
(!= a b)
(parent* a f)
(parent* b f)
(man* f)
(man* b))))
(run* [q] (fresh [a b] (brothero a b) (== q [a b])))
;=> ([:Daughter :Son])
Как вы видите, я не хочу определять цель parento
, поскольку она избыточна. Следует отметить, что (!= a b)
требуется, чтобы не получить пары, содержащие одного и того же человека дважды, и что существует ограничение на родителя, чтобы предотвратить удвоение ответов. Очевидно, что этот пример не сработает, если у вас нет зарегистрированного отца или для человека, у которого есть дети из нескольких женщин.
Ответ 2
Если вы хотите использовать рекурсивное отношение, вы можете использовать это расширение
https://github.com/niitsuma/Racket-miniKanren/tree/recursive
Возможно переписывание
ходить пешком * унифицировать происходит-проверить
как это расширение,
также позволяет рекурсивное соотношение в clojure