Search code examples
prologprolog-cutclp

In writing a purely relational prolog program, is it ok to use a carefully placed cut?


I am trying my hand in writing a relational prolog program that manages a key, value store. The initial code is taken from some lecture slides i found on the internet (http://people.eng.unimelb.edu.au/pstuckey/book/course.html -- see: using data structure slides).

newdic([]).
addkey(D0,K,I,D) :- D = [p(K,I)|D0].
delkey([],_,[]).
delkey([p(K,_)|D],K,D).
delkey([p(K0,I)|D0],K,[p(K0,I)|D]) :-
    dif(K, K0), delkey(D0,K,D).

This code allows adding more than one value with the same key -- which, is fine with me. However, it also adds the same key, value pair twice, e.g.

?- newdic(D), addkey(D, a, 1, D2), addkey(D2, a, 1, D3), lookup(D3, a, X).
D = [],
D2 = [p(a, 1)],
D3 = [p(a, 1), p(a, 1)],
X = 1 

D3 includes p(a,1) twice.

To ensure that this doesn't happen i added the following code; and to ensure that backtracking doesn't find the alternative addkey clause, I added a cut in the end of the first clause.

Is this fair game for a purely relational program -- or are the better ways to ensure that no duplicate key,value pairs are added --without the use of a cut.

newdic([]).
addkey(D0,K,I,D0) :- lookup(D0, K, I), !.   % if the key already do nothing
addkey(D0,K,I,D) :- D = [p(K,I)|D0].
delkey([],_,[]).
delkey([p(K,_)|D],K,D).
delkey([p(K0,I)|D0],K,[p(K0,I)|D]) :-
    dif(K, K0), delkey(D0,K,D).

this leads to the following:

?- newdic(D), addkey(D, a, 1, D2), addkey(D2, a, 1, D3), lookup(D3, a, X).
D = [],
D2 = D3, D3 = [p(a, 1)],
X = 1.

No, more solutions are available -- the program returns immediately.

any suggestions are much appreciated,

Daniel

Note: as an aside: that if i add for the same key different values, the cut does allow to backtrack to identify the second value for the same key:

?- newdic(D), addkey(D, a, 1, D2), addkey(D2, a, 1, D3), addkey(D3, a, 2, D4), lookup(D4, a, X).
D = [],
D2 = D3, D3 = [p(a, 1)],
D4 = [p(a, 2), p(a, 1)],
X = 2 ;
D = [],
D2 = D3, D3 = [p(a, 1)],
D4 = [p(a, 2), p(a, 1)],
X = 1.

Solution

  • SWI Prolog has library predicates for dealing with key-value pairs and associations. I haven't looked at them closely to see what might match your situation, but something to consider.

    If you want to roll your own solution, you could write addkey/4 out recursively and maintain the relational behavior:

    addkey([], K, V, [p(K,V)]).             % Add to empty dict
    addkey([p(K,V)|T], K, _, [p(K,V)|T]).   % Don't add
    addkey([p(K,V)|T], Kadd, Vadd, [p(K,V)|TK]) :-
        dif(Kadd, K),
        addkey(T, Kadd, Vadd, TK).
    

    This implementation adds if the key is unique. It ignores the addition and yields the same dictionary back if you try the same key even with different a value (which is usually how a dictionary of key-value pairs behaves). You can enhance it for the uniqueness of the key-value pair pretty easily. Of course, the Prolog you're using would need to include dif/2, or you'd need to roll your own dif/2. :)