If you want to quickly learn Prolog and CLP (FD), use the top-level Prolog shell to play around until you get comfortable with it. In fact, everything you need to know about CLP (FD) and Prolog can be explained there; or almost. No need to write (what is their name?) Files, everything fits into a line. Yes, I know, our parents warned us: “My child, promise me, never do one-line. But you will learn much faster.
So what are you expecting ?- ?
In traditional Prolog (without limitation), what you get from the request is called the so-called replacement of the response . In many situations, this replacement of the answer already describes the solution. This is so if a free member of the variable is found for each variable. Let's look at a specific example and describe a list with 5 elements, where each element has a value from 1 to 5. In this case, solutions are found for different values for L
?- N = 5, length(L,N),maplist(between(1,N),L). N = 5, L = [1, 1, 1, 1, 1] ; N = 5, L = [1, 1, 1, 1, 2] ; N = 5, L = [1, 1, 1, 1, 3] ...
Prolog will show you only one solution (secretly hoping that you will be happy with it, its a little lazy, not strict). You get all of them by typing SPACE or ; . Try to find out a little how much they are ...
There are only 5^5 solutions. This is not very practical if you just want to select several solutions from this number. Thus, a large set of solutions seems to be very inefficient. And then, think of endless sets! How can Prolog or any finite being enumerate an infinite set? We can only begin to do this, of course, as we are.
To overcome this, Prolog is not always forced to show specific values, that is, solutions, but can add them a little, showing the answers:
?- N = 5, length(L,N). N = 5, L = [_A, _B, _C, _D, _E].
This answer (-substitution) contains all the answers 5^5 above, and many others, for example L = [stack,over,flow,dot,com] . In fact, it describes an endless set of solutions! Didn't I say that we finite creatures cannot do this? While we insist on specific solutions, we cannot, but if we are satisfied with the answers, we can do the impossible.
This idea can be expanded to describe more specific sets. All with one answer. For integer sets, we have library(clpfd) . Use it like this:
?- use_module(library(clpfd)). ?- asserta(clpfd:full_answer). % only necessary for SICStus
Now we can reformulate our original query (in SWI you can do Cursor up ↑ to get it):
?- N = 5, length(L,N),L ins 1..N. N = 5, L = [_A, _B, _C, _D, _E], _A in 1..5, _B in 1..5, _C in 1..5, _D in 1..5,_E in 1..5.
Now all 3125 solutions are compactly described with one answer. (3125 ?, which is 5^5 ). We can continue to formulate additional requirements, so that they are all different:
?- N = 5, length(L,N),L ins 1..N, all_different(L). N = 5, L = [_A, _B, _C, _D, _E], _A in 1..5,_B in 1..5,_C in 1..5,_D in 1..5,_E in 1..5, all_different([_A, _B, _C, _D, _E]).
What (practically) all restrictions have in common is that they do not list the solutions, instead they try to maintain consistency . Let's try this by declaring that the first element should be 1:
?- N = 5, length(L,N),L ins 1..N, all_different(L), L = [1|_]. N = 5, L = [1, _A, _B, _C, _D], _A in 2..5,_B in 2..5,_C in 2..5,_D in 2..5, all_different([1, _A, _B, _C, _D]).
Have you seen the effect? They quickly changed their domains! Now they are all in 2..5 .
And they should all be in 1..4 :
?- N = 5, length(L,N),L ins 1..N, all_different(L), L = [1|_], L ins 1..4. N = 5, L = [1, _A, _B, _C, _D], _A in 2..4,_B in 2..4,_C in 2..4,_D in 2..4, all_different([1, _A, _B, _C, _D]).
Again, they are updated. But ... think about it: there are 4 variables left, they should all be different, but for them there are only 3 different values.
So we caught that Prolog is a bit lazy. In fact, there is a better constraint called all_distinct/1 that will fail, but no matter how many smart constraints the system has, there will always be such inconsistencies. Ask Professor Gödel . The only ways to save would be errors or endless cycles.
Therefore, we need another method to be sure that the answer describes real solutions. Enter labeling! Using label/1 or labeling/2 we can eliminate all these strange restrictions and inconsistencies in the bust:
?- N = 5, length(L,N),L ins 1..N, all_different(L), L = [1|_], L ins 1..4, labeling([], L). false. ?- N = 5, length(L,N),L ins 1..N, all_different(L), L = [1|_], labeling([], L). N = 5, L = [1, 2, 3, 4, 5] ; N = 5, L = [1, 2, 3, 5, 4] ; N = 5, L = [1, 2, 4, 3, 5] ...
How can we be sure that these are real solutions? Easy: they do not contain any additional goals other than substituting answer 1 . For if we forget something:
?- N = 5, length(L,N),L ins 1..N, all_different(L), L = [1,B,C|_], labeling([],[B,C]). N = 5, L = [1, 2, 3, _A, _B], B = 2, C = 3, _A in 4..5, _B in 4..5, all_different([1, 2, 3, _A, _B]),
They will show.
SWI labeling/2 has a very useful guarantee:
Labeling is always completed, always ends and does not provide redundant solutions.
1 Since SWI toplevel does not display all restrictions, you need to wrap call_residue_vars(Goal, Vs) around it. But for simple top-level queries above this is good enough.