Question

I try to do something like this in core.logic

(defn count-different-elements-in-list [coll]
  (count (set coll)))

this works with integers just fine

(should= 1 (count-different-elements-in-list '(1 1 1)))
(should= 2 (count-different-elements-in-list '(1 1 2)))
(should= 3 (count-different-elements-in-list '(1 3 2)))

but now I'm trying to use core.logic to solve some stuff and there it get's messy

(run* [a b c]
  ;;the variables get values between 1 and 3
  (fd/in a b c (fd/interval 1 3))
  ;; in the list there should only be 2 different values
  (== 2 (count-different-elements-in-list '(a b c))))

but here comes the problem, a b c don't get passed as values to the function. They get passed as variables. With three variables count-different-elements-in-list returns always 3 and core.logic doesn't find a solution (empty list).

But I'm looking for this result.

([1 1 2] [1 2 1] [2 1 1] 
 [1 1 3] [1 3 1] [3 1 1]
 [2 2 1] [2 1 2] [1 2 2]
 [2 2 3] [2 3 2] [3 2 2]
 [3 3 1] [3 1 3] [1 3 3]
 [3 3 2] [3 2 3] [2 3 3])
Was it helpful?

Solution

You need to core.logic/project logic vars into non-relational goals, like the normal function count-different-elements-in-list. Unfortunately, you cannot project finite domain logic vars, like a, b, and c, that are not constrained to a single value. (See: this question)

In the example you have, you can swap out the fd/in and fd/interval for a generated range and membero. This would remove the unconstrained finite domain vars, keep the range constraint for integers, and allow for projection.

(def interval (vec (range 1 4)))
(run* [a b c]
  (membero a interval)
  (membero b interval)
  (membero c interval)
  (project [a b c]
    (== 2 (count-different-elements-in-list (list a b c)))))
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top