I implemented the following power program in Prolog:
puissance(_,0,1).
puissance(X,N,P) :- N>0,A is N-1, puissance(X,A,Z), P is Z*X.
The cod
Currently, there are 3 different versions of puissance/3
, and I would like to show a significant semantic difference between some of them.
As a test case, I consider the query:
?- puissance(X, Y, Z), false.
What does this query mean? Declaratively, it is clearly equivalent to false. This query is very interesting nevertheless, because it terminates iff puissance/3
terminates universally.
Now, let us try the query on the different variants of the program:
Original definition (from the question):
?- puissance(X, Y, Z), false.
ERROR: puissance/3: Arguments are not sufficiently instantiated
Accepted answer:
?- puissance(X, Y, Z), false.
false.
Other answer:
?- puissance(X, Y, Z), false.
ERROR: puissance/3: Arguments are not sufficiently instantiated
Obviously, the solution shown in the accepted answer yields a different result, and is worth considering further.
Here is the program again:
puissance(_,0,1) :- !. puissance(X,N,P) :- N>0,A is N-1, puissance(X,A,Z), P is Z*X.
Let us ask something simple first: Which solutions are there at all? This is called the most general query, because its arguments are all fresh variables:
?- puissance(X, Y, Z). Y = 0, Z = 1.
The program answers: There is only a single solution: Y=0, Z=1
.
That's incorrect (to see this, try the query ?- puissance(0, 1, _)
which succeeds, contrary to the same program claiming that Y
can only be 0
), and a significant difference from the program shown in the question. For comparison, the original program yields:
?- puissance(X, Y, Z). Y = 0, Z = 1 ; ERROR: puissance/3: Arguments are not sufficiently instantiated
That's OK: On backtracking, the program throws an instantiation error to indicate that no further reasoning is possible at this point. Critically though, it does not simply fail!
So, let us stick to the original program, and consider the query:
?- puissance(1, 1, Z). Z = 1 ; false.
We would like to get rid of false
, which occurs because the program is not deterministic.
One way to solve this is to use zcompare/3
from library(clpfd)
. This lets you reify the comparison, and makes the result available for indexing while retaining the predicate's generality.
Here is one possible solution:
puissance(X, N, P) :- zcompare(C, 0, N), puissance_(C, X, N, P). puissance_(=, _, 0, 1). puissance_(<, X, N, P) :- A #= N-1, puissance(X, A, Z), P #= Z*X.
With this version, we get:
?- puissance(1, 1, Z). Z = 1.
This is now deterministic, as intended.
Now, let us consider the test case from above with this version:
?- puissance(X, Y, Z), false. nontermination
Aha! So this query neither throws an instantiation error nor terminates, and is therefore different from all the versions that have hitherto been posted.
Let us consider the most general query with this program:
?- puissance(X, Y, Z). Y = 0, Z = 1 ; X = Z, Y = 1, Z in inf..sup ; Y = 2, X^2#=Z, Z in 0..sup ; Y = 3, _G3136*X#=Z, X^2#=_G3136, _G3136 in 0..sup ; etc.
Aha! So we get a symbolic representation of all integers that satisfy this relation.
That's pretty cool, and I therefore recommend you use CLP(FD) constraints when reasoning over integers in Prolog. This will make your programs more general and also lets you improve their efficiency more easily.