1
votes

I have the following code in Clojure (with core.logic):

(db-rel parent x y)
(db-rel go-to-school x y)

(def schools
  (db
    [parent 'Adam 'Ana]
    [parent 'Adam 'Andre]
    [parent 'Adam 'Alan]
    [parent 'Bernard 'Bia]
    [parent 'Bernard 'Beatrice]
    [parent 'Carl 'Carlos]
    [parent 'Carl 'Connie]

    [go-to-school 'School1 'Ana]
    [go-to-school 'School1 'Andre]
    [go-to-school 'School2 'Alan]
    [go-to-school 'School2 'Bia]
    [go-to-school 'School2 'Beatrice]
    [go-to-school 'School1 'Carlos]
    [go-to-school 'School2 'Connie]))

What I want is to find all parents where all the children go to the same school. So, running on the above list, my expected return would be ('Bernard), because his two daughters go the "school2", where every other parent have at least one child that will not go to the same school of others.

Is this possible with core.logic? If so, how can I do that?

1

1 Answers

1
votes

Never forget that when you're using Core.logic, you have full access to Clojure. Let's associate each parent with a school, and work form there:

stack-prj.logic-school> (set
 (pldb/with-db schools
   (run* [par sch]
     (fresh [kid]
       (go-to-school sch kid)
       (parent par kid)))))
#{[Bernard School2] [Adam School2] [Carl School2] [Carl School1] [Adam School1]}

I used the standard Clojure function set to get the unique results. From there, we can group-by the first element, filter out everything that has a count greater than 1, and the results will be the parents who send all their children to one school (as keys, so we'll need to take the first element from each item).

(defn parent-with-one-school []
  (->> (set
        (pldb/with-db schools
          (run* [par sch]
            (fresh [kid]
              (go-to-school sch kid)
              (parent par kid)))))
       (group-by first)
       (filter #(= 1 (count (second %))))
       (map first)))

Let's test it!

stack-prj.logic-school> (parent-with-one-school)
(Bernard)