Reputation: 2565
I wonder if anybody else has ever needed, and thus coded, a predicate like membero
but for hash-maps. I can, of course, use (seq)
on a hash-map, but if it's already an LVar, it won't work.
If we call it keyvalo
, it'd work like:
(run* [q]
(fresh [m kv]
(== m {:a 2 :b 3})
(keyvalo kv m)
(== q kv)))
=> ([:a 2] [:b 3])
It can be defined as
(defn keyvalo [kv map]
(fresh [s]
(seqo s map)
(membero kv s)))
But I having a hard time trying to code a seqo
, which would succeed for (seqo [[:foo 3]] {:foo 3})
.
Thanks!
Upvotes: 1
Views: 267
Reputation: 26446
Easiest would be to project
(run* [q]
(fresh [m]
(== m {:foo 1 :bar 2}) ; m is an LVar
(project [m] ; but when bound values are projected
(membero q (seq m))))) ; seq can be used
;=> ([:foo 1] [:bar 2])
(defn keyvalo [kv m] (project [m] (membero kv (seq m))))
This is non-relational, which means that while
(run 1 [q] (membero [:foo 1] q))
;=> (([:foo 1] . _0))
will give you a list for which [:foo 1]
is a member (don't try this with run*
),
(run 1 [q] (keyvalo [:foo 1] q))
;=> IllegalArgumentException Don't know how to create ISeq from: clojure.core.logic.LVar
is going to throw an exception instead. But, this probably will still suffice for your purposes.
For example, we can invert a map now
(run* [q] (== (partial-map {:foo q}) {:foo 1 :bar 2}))
;=> (1)
(run* [q] (== (partial-map {q 1}) {:foo 1 :bar 2}))
;=> ()
But,
(run* [q] (keyvalo [:foo q] {:foo 1 :bar 2}))
;=> (1)
(run* [q] (keyvalo [q 1] {:foo 1 :bar 2}))
;=> (:foo)
Note that a problem with seqo
here is that the keys in the map have no order, whereas a sequence does. Therefore, for (seqo [[:a 1] [:b 2] [:c 3]] {:a 1 :b 2 :c 3})
to succeed, you would have test for any permutation of (seq {:a 1 :b 2 :c 3})
For the implementation I am using (seq {:a 1 :b 2 :c 3}) ;=> ([:a 1] [:c 3] [:b 2])
, for example. You could work around this with permutations, but that's likely not what you want for larger maps.
Upvotes: 1