Consider the following Prolog program:
transform([], []).
transform([X | Xs],[Y | Ys]):-
isSpecial(X),
isSpecial(Y),
transformSpecial(X,Y),
transform(Xs,Ys).
transform([X | Xs],[ X | Ys]):-
transform(Xs,Ys).
isSpecial(foo).
isSpecial(bar).
isSpecial(foobar).
transformSpecial(X,Y):-
isSpecial(X),
isSpecial(Y),
not(X = Y).
There are cases where I will call tranform([foo, 1, 2], X)
and cases where I want to call transform(X, [foo, 1, 2])
.
In both cases I want X to unify with [bar, 1, 2]
and with [foobar, 1, 2]
, but not with [foo, 1, 2]
. That is, I want that once the predicate correctly recognizes that the second clause is applicable it should stick to only backtrack using the second clause.
Where should I insert the cut to achieve this behavior?
Here is another approach that reuses your existing definition of isSpecial/1
. It's biggest drawback is that it makes a lot of assumptions that are not readily visible. So even a small extension might invalidate it. It uses iwhen/2
("instantiated when").
el_transformed(X, Y) :-
iwhen(( nonvar(X) ; nonvar(Y) ), iel_transformed(X, Y) ).
iel_transformed(X, Y) :-
( nonvar(X)
-> ( isSpecial(X) -> isSpecial(Y), X \= Y ; X = Y )
; ( isSpecial(Y) -> isSpecial(X), X \= Y ; X = Y )
).
Very tricky - I even made a two mistakes on my first try ... and no cut, mince!
For the most general query, we now get an instantiation error. Not very satisfying, but better than an incorrect result.
In general, you would have to look for implementations of constructive negation to handle this in the general case...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With