Of course, you can do it with logical-purity! Here's how...
Let's call the actual relation pairs_key_unmapped/3
. That's a somewhat more descriptive name. unmap/3
is just a wrapper for pairs_key_unmapped/3
:
unmap(Key,Ps0,Ps) :-
pairs_key_unmapped(Ps0,Key,Ps).
The implementation of pairs_key_unmapped/3
is built on the predicates if_/3
and =/3
(a.k.a. equal_truth/3
), as defined by @false in an answer to "Prolog union for A U B U C":
pairs_key_unmapped([],_,[]).
pairs_key_unmapped([P|Ps],K,Us) :-
P = (K0,_),
if_(K0=K, Ps=Us, (Us=[P|Us0],pairs_key_unmapped(Ps,K,Us0))).
Let's run some queries!
?- unmap(key1,[(key1,value1),(key2,value2),(key3,value3)],Ps).
Ps = [(key2,value2),(key3,value3)]. % succeeds deterministically
?- unmap(key4,[(key1,value1),(key2,value2),(key3,value3)],Ps).
Ps = [(key1,value1),(key2,value2),(key3,value3)]. % succeeds deterministically
Let's try something different... What if Key
occurs twice in Ps0
?
?- unmap(key1,[(key1,x),(key1,y)],Ps). % only the 1st occurrence is removed
Ps = [(key1,y)]. % succeeds deterministically
What if Ps0
is unknown, but Ps
is known?
?- unmap(key4,Ps0,[(key1,value1),(key2,value2),(key3,value3)]).
Ps0 = [(key4,_A), (key1,value1),(key2,value2),(key3,value3)] ;
Ps0 = [(key1,value1),(key4,_A), (key2,value2),(key3,value3)] ;
Ps0 = [(key1,value1),(key2,value2),(key4,_A), (key3,value3)] ;
Ps0 = [(key1,value1),(key2,value2),(key3,value3) ] ;
Ps0 = [(key1,value1),(key2,value2),(key3,value3),(key4,_A) ] ;
false.
How about something a little more general?
?- unmap(Key,Ps0,[_,_]).
Ps0 = [(Key,_A),_B, _C ] ;
Ps0 = [(_A,_B), (Key,_C), _D ], dif(_A,Key) ;
Ps0 = [(_A,_B), (_C,_D) ], dif(_A,Key), dif(_C,Key) ;
Ps0 = [(_A,_B), (_C,_D), (Key,_E)], dif(_A,Key), dif(_C,Key) ;
false.
And what answers does the most general query give us?
?- unmap(Key,Ps0,Ps).
Ps0 = [], Ps = [] ;
Ps0 = [(Key,_A)|Ps] ;
Ps0 = [(_A,_B)], Ps = [(_A,_B)], dif(_A,Key) ;
Ps0 = [(_A,_B),(Key,_C)|_Z], Ps = [(_A,_B)|_Z], dif(_A,Key) ;
Ps0 = [(_A,_B),(_C,_D)], Ps = [(_A,_B),(_C,_D)], dif(_A,Key), dif(_C,Key) ...