I have implemented the following function in prolog with the following code:
abs2(X, Y) :- X < 0, Y is -X.
abs2(X, X) :- X >= 0, !.
How can I implement this function without the use of cut ("!")?
There's the "hidden" cut in the if-then-else construct of Prolog:
abs2(X,Y) :- X < 0 -> Y is -X ; Y = X.
It is something of a quirk, but Prolog does not backtrack on the subgoal that forms the "premise" of an if-then or if-then-else construct. Here, if X < 0 succeeds the first try, then the choice of "then" clause over "else" clause is committed (hence the description of this behavior as a "hidden" cut).
There is more of a role for a cut in the first clause of the predicate abs2/2 as written in the question. As Nicholas points out, the cut at the end of the second clause doesn't have any effect (there are no choice points left when you get there). But as Kaarel points out, there is a choice point left open if the first clause succeeds.
So what I would have written, allowing the use of a cut, is this:
abs2(X,X) :- X >= 0, !.
abs2(X,Y) :- Y is -X.
Nicholas's comments also suggest ways to "arithmetize" the absolute value (rather than use a logic definition) and avoid "cut" that way.
My prolog is a bit rusty, but why do you even need the cut? If you write the predicate properly, backtracking can't succeed, so the cut is unnecessary:
abs(X, Y) :- number(X) , X < 0 , Y is -X .
abs(X, X) :- number(X) , X >= 0 .
No need to use prolog-cut!
Simply write:
abs2(X,Y) :- Y is abs(X).
Related
I'm currently learning SWI-Prolog. I want to implement a function factorable(X) which is true if X can be written as X = n*b.
This is what I've gotten so far:
isTeiler(X,Y) :- Y mod X =:= 0.
hatTeiler(X,X) :- fail,!.
hatTeiler(X,Y) :- isTeiler(Y,X), !; Z is Y+1, hatTeiler(X,Z),!.
factorable(X) :- hatTeiler(X,2).
My problem is now that I don't understand how to end the recursion with a fail without backtracking. I thought the cut would do the job but after hatTeilerfails when both arguments are equal it jumps right to isTeiler which is of course true if both arguments are equal. I also tried using \+ but without success.
It looks like you add cuts to end a recursion but this is usually done by making rule heads more specific or adding guards to a clause.
E.g. a rule:
x_y_sum(X,succ(Y,1),succ(Z,1)) :-
x_y_sum(X,Y,Z).
will never be matched by x_y_sum(X,0,Y). A recursion just ends in this case.
Alternatively, a guard will prevent the application of a rule for invalid cases.
hatTeiler(X,X) :- fail,!.
I assume this rule should prevent matching of the rule below with equal arguments. It is much easier just to add the inequality of X and Y as a conditon:
hatTeiler(X,Y) :-
Y>X,
isTeiler(Y,X),
!;
Z is Y+1,
hatTeiler(X,Z),
!.
Then hatTeiler(5,5) fails automatically. (*)
You also have a disjunction operator ; that is much better written as two clauses (i drop the cuts or not all possibilities will be explored):
hatTeiler(X,Y) :- % (1)
Y > X,
isTeiler(Y,X).
hatTeiler(X,Y) :- % (2)
Y > X,
Z is Y+1,
hatTeiler(X,Z).
Now we can read the rules declaratively:
(1) if Y is larger than X and X divides Y without remainder, hatTeiler(X,Y) is true.
(2) if Y is larger than X and (roughly speaking) hatTeiler(X,Y+1) is true, then hatTeiler(X, Y) is also true.
Rule (1) sounds good, but (2) sounds fishy: for specific X and Y we get e.g.: hatTeiler(4,15) is true when hatTeiler(4,16) is true. If I understand correctly, this problem is about divisors so I would not expect this property to hold. Moreover, the backwards reasoning of prolog will then try to deduce hatTeiler(4,17), hatTeiler(4,18), etc. which leads to non-termination. I guess you want the cut to stop the recursion but it looks like you need a different property.
Coming from the original property, you want to check if X = N * B for some N and B. We know that 2 <= N <= X and X mod N = 0. For the first one there is even a built-in called between/2 that makes the whole thing a two-liner:
hT(X,B) :-
between(2, X, B),
0 is (X mod B).
?- hT(12,X).
X = 2 ;
X = 3 ;
X = 4 ;
X = 6 ;
X = 12.
Now you only need to write your own between and you're done - all without cuts.
(*) The more general hasTeiler(X,X) fails because is (and <) only works when the right hand side (both sides) is variable-free and contains only arithmetic terms (i.e. numbers, +, -, etc).
If you put cut before the fail, it will be freeze the backtracking.
The cut operation freeze the backtracking , if prolog cross it.
Actually when prolog have failed, it backtracks to last cut.
for example :
a:- b,
c,!,
d,
e,!,
f.
Here, if b or c have failed, backtrack do not freeze.
if d or f have failed, backtrack Immediately freeze, because before it is a cut
if e have failed , it can backtrack just on d
I hope it be useful
I'm new to prolog so the concept of unifying is still new, so bear with me.
I want something like:
pair(X,Y)
That works like this:
?- pair(4,Y).
Y = 4,
?- pair(3,Y).
Y = 0,
But I don't really know how to do this. I know you can use something like X mod 2 =:= 0 which is true or false, but I don't know how to make "if this than that" code in prolog's way.
There are several ways to express a conditional statement in Prolog, besides using CLPFD (Constraint Logic Programming over Finite Domains):
1) make several statements, one for each case you are considering, and let Prolog execution mechanism find the one that is appropriate:
pair(X,Y) :- even(X), !, Y = X. % 'if X is even then Y = X'
pair(_,0). % 'else Y = 0'
In the second case here the unification is implicit, but in the first case it is explicit.
The _ symbol is a syntactic sugar for an 'anonymous variable', that is a variable for whose bindings we do not care and that we do not use.
The cut operator ! is used in the first clause to tell Prolog that it should not try to find other solutions for the Y if the first clause succeeds (like, backtracking and trying the second clause):
% without cut | % with cut
?- pair(4,Y). | ?- pair3(4,Y).
|
Y = 4 ? ; | Y = 4 ? ;
|
Y = 0 ? ; % <- wrong behaviour! | no.
|
no |
2) use the (Condition -> ThenStatement ; ElseStatement) if-then-else construct:
pair(X,Y) :- (even(X) -> Y = X ; Y = 0).
No cuts are allowed in the Condition argument. The first solution of the Condition is used.
3) some systems have the if/3 predicate, that is evaluated as if(Condition,ThenStatement,ElseStatement):
pair(X,Y) :- if( even(X), Y = X, Y = 0).
No cuts are allowed in the Condition argument.
This Prolog program defines the third argument to be the maximum value of the first two numeric arguments:
max(X, Y, X) :- X >= Y, !.
max(X, Y, Y).
I think that this program works just fine. But I am told that it can give incorrect result. Can you tell when and why?
This is a textbook example.
?- max(5,1,1).
true.
Homework: Why is the program wrong? How do we make the program correct?
EDIT
max(X, Y, X) :- X >= Y, !.
max(X, Y, Y).
Our intention is to say:
If X is greater than Y, then Max is X. Otherwise, Max must be Y.
Instead, what is say is:
When the first and third arguments (X and Max) can be unified, and X is greater than Y, succeed. Otherwise, if the second and third arguments (Y and Max) can be unified, succeed.
The obvious problem arises then the first and third arguments cannot be unified, but the second and the third can.
Instead:
max(X, Y, X) :- X >= Y.
max(X, Y, Y) :- X < Y.
or
max(X, Y, Max) :- X >= Y, !, Max = X.
max(_, Max, Max).
It does work fine, provided the third argument is uninstantiated. The danger here would be if there were a way to backtrack into the second rule, or if the third argument is instantiated to the same value as the second. It's not particularly safe looking because max(X, Y, Y). is equal to max(_, Y, Y) which just sets the result to the second value without any thought. The cut at the end of the first rule effectively ensures that backtracking will not commence if X >= Y, so the second rule should only be entered when X < Y and Z is not already equal to Y.
Though it mostly works, it's not a good habit to get into. People new to Prolog tend to think procedurally and making use of the cut like this to ensure a particular result through procedural trickery ultimately holds you back and leads to convoluted Prolog that cannot be driven in different and interesting ways. There are several other ways of writing this predicate that work just as well but do not rely on the cut to ensure their behavior, for instance:
max(X, Y, X) :- X >= Y.
max(X, Y, Y) :- X < Y.
or
max(X, Y, Z) :- X >= Y -> Z = X ; Z = Y.
Neither of these is vulnerable to the problem of the third being instantiated. Interestingly, this is a great illustration of the difference between a red cut and a green cut. Your code has a red cut, where the behavior is dependent on the cut, but if I simply change my first solution to this:
max(X, Y, X) :- X >= Y, !.
max(X, Y, Y) :- X < Y.
That's a green cut, because the behavior is not dependent on the cut, but Prolog's performance may improve slightly since it won't backtrack into the second clause to try it. Here we're explicitly telling Prolog, don't both making the next check because we know it will fail. With a red cut, there's no other check which will fail.
It's unfortunate that stating the condition twice feels redundant but relying on a single rule feels clunky. In practice, my experience is that scenarios like these are not ultimately all that common; usually you have atoms or structures you can match in the head of the clause that create behavior like we have in my first substitute, but without needing a body. For example:
perform(scan(target, X, Y)) :- ...
perform(scan(calibration, X)) :- ...
This has the same effect: Prolog will backtrack until it unifies successfully, then it will back track again, but the exclusive nature of the matching will prevent another body from being executed. If we find out it's spending too much time backtracking we can add cuts to improve the performance, but in practice it's unlikely to be a problem.
I'm working on some prolog that I'm new to.
I'm looking for an "or" operator
registered(X, Y), Y=ct101, Y=ct102, Y=ct103.
Here's my query. What I want to write is code that will:
"return X, given that Y is equal to value Z OR value Q OR value P"
I'm asking it to return X if Y is equal to all 3 though. What's the or operator here? Is there one?
Just another viewpoint. Performing an "or" in Prolog can also be done with the "disjunct" operator or semi-colon:
registered(X, Y) :-
X = ct101; X = ct102; X = ct103.
For a fuller explanation:
Predicate control in Prolog
you can 'invoke' alternative bindings on Y this way:
...registered(X, Y), (Y=ct101; Y=ct102; Y=ct103).
Note the parenthesis are required to keep the correct execution control flow. The ;/2 it's the general or operator. For your restricted use you could as well choice the more idiomatic
...registered(X, Y), member(Y, [ct101,ct102,ct103]).
that on backtracking binds Y to each member of the list.
edit I understood with a delay your last requirement. If you want that Y match all 3 values the or is inappropriate, use instead
...registered(X, ct101), registered(X, ct102), registered(X, ct103).
or the more compact
...findall(Y, registered(X, Y), L), sort(L, [ct101,ct102,ct103]).
findall/3 build the list in the very same order that registered/2 succeeds. Then I use sort to ensure the matching.
...setof(Y, registered(X, Y), [ct101,ct102,ct103]).
setof/3 also sorts the result list
To grok green cuts in Prolog I am trying to add them to the standard definition of sum in successor arithmetics (see predicate plus in What's the SLD tree for this query?). The idea is to "clean up" the output as much as possible by eliminating all useless backtracks (i.e., no ... ; false) while keeping identical behavior under all possible combinations of argument instantiations - all instantiated, one/two/three completely uninstantiated, and all variations including partially instantiated args.
This is what I was able to do while trying to come as close as possible to this ideal (I acknowledge false's answer to how to insert green cuts into append/3 as a source):
natural_number(0).
natural_number(s(X)) :- natural_number(X).
plus(X, Y, X) :- (Y == 0 -> ! ; Y = 0), (X == 0 -> ! ; true), natural_number(X).
plus(X, s(Y), s(Z)) :- plus(X, Y, Z).
Under SWI this seems to work fine for all queries but those with shape ?- plus(+X, -Y, +Z)., as for SWI's notation of predicate description. For instance, ?- plus(s(s(0)), Y, s(s(s(0)))). yields Y = s(0) ; false.. My questions are:
How do we prove that the above cuts are (or are not) green?
Can we do better than the above program and eliminate also the last backtrack by adding some other green cuts?
If yes, how?
First a minor issue: the common definition of plus/3 has the first and second argument exchanged which allows to exploit first-argument indexing. See Program 3.3 of the Art of Prolog. That should also be changed in your previous post. I will call your exchanged definition plusp/3 and your optimized definition pluspo/3. Thus, given
plusp(X, 0, X) :- natural_number(X).
plusp(X, s(Y), s(Z)) :- plusp(X, Y, Z).
Detecting red cuts (question one)
How to prove or disprove red/green cuts? First of all, watch for "write"-unifications in the guard. That is, for any such unifications prior to the cut. In your optimized program:
pluspo(X, Y, X) :- (Y == 0 -> ! ; Y = 0), (X == 0 -> ! ; true), ...
I spot the following:
pluspo(X, Y, X) :- (...... -> ! ; ... ), ...
So, let us construct a counterexample: To make this cut cut in a red manner, the "write unification" must make its actual guard Y == 0 true. Which means that the goal to construct must contain the constant 0 somehow. There are only two possibilities to consider. The first or third argument. A zero in the last argument means that we have at most one solution, thus no possibility to cut away further solutions. So, the 0 has to be in the first argument! (The second argument must not be 0 right from the beginning, or the "write unification would not have a detrimental effect.). Here is one such counterexample:
?- pluspo(0, Y, Y).
which gives one correct solution Y = 0, but hides all the other ones! So here we have such an evil red cut!
And contrast it to the unoptimized program which gave infinitely many solutions:
Y = 0
; Y = s(0)
; Y = s(s(0))
; Y = s(s(s(0)))
; ... .
So, your program is incomplete, and any questions about further optimizing it are thus not relevant. But we can do better, let me restate the actual definition we want to optimize:
plus(0, X, X) :- natural_number(X).
plus(s(X), Y, s(Z)) :- plus(X, Y, Z).
In practically all Prolog systems, there is first-argument indexing, which makes the following query determinate:
?- plus(s(0),0,X).
X = s(0).
But many systems do not support (full) third argument indexing. Thus we get in SWI, YAP, SICStus:
?- plus(X, Y, 0).
X = Y, Y = 0
; false.
What you probably wanted to write is:
pluso(X, Y, Z) :-
% Part one: green cuts
( X == 0 -> ! % first-argument indexing
; Z == 0 -> ! % 3rd-argument indexing, e.g. Jekejeke, ECLiPSe
; true
),
% Part two: the original unifications
X = 0,
Y = Z,
natural_number(Z).
pluso(s(X), Y, s(Z)) :- pluso(X, Y, Z).
Note the differences to pluspo/3: There are now only tests prior to the cut! All unifications are thereafter.
?- pluso(X, Y, 0).
X = Y, Y = 0.
The optimizations so far relied only on investigating the heads of the two clauses. They did not take into account the recursive rule. As such, they can be incorporated into a Prolog compiler without any global analysis. In O'Keefe's terminology, these green cuts might be considered blue cuts. To cite The Craft of Prolog, 3.12:
Blue cuts are there to alert the Prolog system to a determinacy it should have noticed but wouldn't. Blue cuts do not change the visible behavior of the program: all they do is make it feasible.
Green cuts are there to prune away attempted proofs that would succeed or be irrelevant, or would be bound to fail, but you would not expect the Prolog system to be able to tell that.
However, the very point is that these cuts do need some guards to work properly.
Now, you considered another query:
?- pluso(X, s(s(0)), s(s(s(0)))).
X = s(0)
; false.
or to put a simpler case:
?- pluso(X, s(0), s(0)).
X = 0
; false.
Here, both heads apply, thus the system is not able to determine determinism. However, we know that there is no solution to a goal plus(X, s^n, s^m) with n > m. So by considering the model of plus/3 we can further avoid choicepoints. I'll be right back after this break:
Better use call_semidet/1!
It gets more and more complex and chances are that optimizations might easily introduce new errors in a program. Also optimized programs are a nightmare to maintain. For practical programming purposes use rather call_semidet/1. It is safe, and will produce a clean error should your assumptions turn out to be false.
Back to business: Here is a further optimization. If Y and Z are identical, the second clause cannot apply:
pluso2(X, Y, Z) :-
% Part one: green cuts
( X == 0 -> ! % first-argument indexing
; Z == 0 -> ! % 3rd-argument indexing, e.g. Jekejeke, ECLiPSe
; Y == Z, ground(Z) -> !
; true
),
% Part two: the original unifications
X = 0,
Y = Z,
natural_number(Z).
pluso2(s(X), Y, s(Z)) :- pluso2(X, Y, Z).
I (currently) believe that pluso2/3 is the optimal usage of green/blue cuts w.r.t. leftover choicepoints. You asked for a proof. Well, I think that is well beyond an SO answer...
The goal ground(Z) is necessary to ensure the non-termination properties. The goal plus(s(_), Z, Z) does not terminate, that property is preserved by ground(Z). Maybe you think it is a good idea to remove infinite failure branches too? In my experience, this is rather problematic. In particular, if those branches are removed automatically. While at first sight it seems to be a good idea, it makes program development much more brittle: An otherwise benign program change might now disable the optimization and thus "cause" non-termination. But anyway, here we go:
Beyond simple green cuts
pluso3(X, Y, Z) :-
% Part one: green cuts
( X == 0 -> ! % first-argument indexing
; Z == 0 -> ! % 3rd-argument indexing, e.g. Jekejeke, ECLiPSe
; Y == Z -> !
; var(Z), nonvar(Y), \+ unify_with_occurs_check(Z, Y) -> !, fail
; var(Z), nonvar(X), \+ unify_with_occurs_check(Z, X) -> !, fail
; true
),
% Part two: the original unifications
X = 0,
Y = Z,
natural_number(Z).
pluso3(s(X), Y, s(Z)) :- pluso3(X, Y, Z).
Can you find a case where pluso3/3 does not terminate while there are finitely many answers?