Related
I am currently going through "Programming in Prolog" by Clocksin & Mellish. One of the exercises asks to print list elements each on a line while indenting nested elements, so for example we need to print [a,b,[c,d],e,f] as:
a
b
c
d
e
f
So, here is my solution (assume we have a predicate 'indent' that prints a specified no. of spaces for indentation). I have defined two predicates 'print' & 'printelement', each takes a first argument to be printed and a second for the indentation (no. of spaces):
print([],_).
print([H|T],Indent):- H\=[_|_], % if not a list
printelement(H,Indent),
print(T,Indent).
print([H|T],Indent):- H=[_|_], NewIndent is Indent+2, % if a list, increase the indent
print(H,NewIndent), % NewIndent
print(T,Indent). % Indent
printelement(X,I):- indent(I), write(X), nl. % print individual elements
... and it does the job. On the other hand, the book presents a solution that does the job too but with a bit of going back and forth between two predicates as follows:
printA([H|T], I) :- !, J is I + 2, printA(H, J), printB(T, J), nl.
printA(X, I) :- indent(I), write(X), nl.
printB([],_).
printB([H|T], I) :- printA(H, I), printB(T, I).
There are a number of other exercises that are solved in a similar manner; and even though I can trace those solutions and validate their correctness, I am a bit confused by this approach. So, would you please help point out the differences between the above solutions? I find mine a bit more logical and straight-forward, and I don't quite get the second one!
If I had to pick between the two solutions, I actually prefer the first solution to the one in the textbook. At least I see no advantages to the second approach, and both solutions are a fairly imperative approach to Prolog. If you had a big enough list, you could do a performance comparison, if that was an important factor. Both have a somewhat awkward calling convention where you need to provide a second argument even though you don't care what it is, ultimately. The second solution has the two arbitrarily named predicates printA and printB that don't seem to have a distinguishable enough semantic meaning between them. You can call printA(MyList, 0). or printB(MyList, 0). and get (sort of) the same results (one having one extra level of indent).
Both printA/2 and print/2 treat [] as an atom rather than an empty list. Thus:
| ?- print([a,b,[],c], 0).
a
b
[]
c
And similarly for printA([a,b,[],c], 0).
If I were writing this, I would take a different approach altogether. First, I might write a predicate with 3 arguments: element_depth(List, X, D) that succeeds if X is in the multi-level list List at depth D and it fails otherwise.
element_depth(List, X, Depth) :-
element_depth(List, X, 0, Depth). % Starts with depth 0
element_depth([X|_], X, Depth, Depth) :-
\+ is_list(X).
element_depth([L|_], X, D, Depth) :-
is_list(L),
D1 #= D + 1,
element_depth(L, X, D1, Depth).
element_depth([_|Xs], X, D, Depth) :-
element_depth(Xs, X, D, Depth).
Now you have a Prolog predicate that behaves more like a predicate and less like a C function. You use it to make queries and it provides solutions. You can do queries such as:
| ?- element_depth([a,b,[d, []], c], X, D).
D = 0
X = a ? a
D = 0
X = b
D = 1
X = d
D = 0
X = c
no
| ?- element_depth([a,b,[d,[]], c], X, 1).
X = d ? ;
no
| ?- element_depth([a,b,[d,[]], c], c, D).
D = 0 ? ;
no
If you want to do a formatted printing of results, you can write a specific formatting predicate that calls it:
print_elements(L) :-
element_depth(L, X, D),
N #= D * 2,
indent(N),
write(X), nl,
fail.
Which you can then call like this:
| ?- print_elements([a,b,[d,[]], c]).
a
b
d
c
no
| ?-
This looks like a little more code, but it is more general and more Prology.
I'm working on trying to solve an LSAT logic problem with Prolog. I'm trying to get Prolog to tell me whether possible values for actors in a line are valid.
I wrote the following:
:- use_module(library(clpfd)).
actor("Jeff",A) :-
A #>= 0,
A #<5.
actor("Rich",C) :-
C #>= 0,
C #<5,
actor("Jeff",A),
A #< C.
When I query:
?- actor("Rich",0).
false.
Which is right since Jeff is < than Rich and Jeff is => 0 so Rich cannot be 0.
But, when I query:
?- actor("Jeff",1), actor("Rich",1).
true
I also get true, which is impossible because Rich > Jeff.
I feel the problem is because of something going on with my variable. I don't understand how
actor("Jeff",A),
A #< C.
is evaluating. It seems to me that actor("Jeff",A) should be set to actor("Jeff",1) and then 1 #< 1 should fail.
Variables within a clause or local to that clause.
In your case, to understand the issue, consider first the following query:
?- actor("Rich", C),
C = 1.
C = 1.
This does not tell us a lot, so we apply the following purely algebraic reasoning: For the goal actor("Rich", C), we substitute the body of the single matching clause:
?- C #>= 0,
C #< 5,
actor("Jeff",A),
A #< C,
C = 1.
The answer is:
C = 1,
A = 0.
This shows that when C is 1, then A is 0. However, on the toplevel, you did not see this because this variable only occurs within the clause for "Rich". Making this explicit shows that there is a solution that satisfies the constraint within that clause, but it is not linked to the variable we want.
There are several ways out. One of them is to make A available as an argument so that you can explicitly refer to it from the toplevel. In general, to link the relevant entities together, you will have to introduce arguments for each of your clauses, so that you can refer to the variables you need to reason about, instead of introducing new ones within each clause.
For example, in your case, this could look as follows:
actor("Jeff", A, _) :-
A #>= 0,
A #< 5.
actor("Rich", A, C) :-
C #>= 0,
C #< 5,
actor("Jeff", A, C),
A #< C.
I have used A to refer to Jeff, and C to refer to Rich.
While we are at at, let us tidy up the code, and use the following essentially equivalent version:
actor(jeff, A, _) :- A in 0..4.
actor(rich, A, C) :- C in 0..4, actor(jeff, A, C), A #< C.
Make sure you understand the following answer:
?- actor(jeff, 1, C), actor(rich, C, 1).
C = 0.
Your original example now yields false, exactly as expected:
?- actor(rich, 1, 1).
false.
Thus, you should be able to solve your task in principle.
However, there is a much simpler way to solve all this, which avoids the reification overload.
Instead of painstakingly keeping track of connections between names and corresponding variables, let us use the variables directly with the intended names. For example, what do you say about this:
?- Jeff in 0..4,
Rich in 0..4,
Jeff #< Rich.
This uses Prolog variables to denote the people, and makes the work a lot simpler. In this representation, your query becomes:
?- Jeff in 0..4,
Rich in 0..4,
Jeff #< Rich,
Jeff = 1,
Rich = 1.
And this obviously results in false.
I want to generate a list with all the numbers abc, where a = c and b >= a + c.
I know how to generate the numbers, but I do not know how to add them to the list because I can not do something like L = [Number|L].
I don't know how to define the add predicate... or should I do it in another way?
I have tried defining it as add(Nr, L, [Nr|L])., but I have no idea of what to do after that.
c(0). c(1). c(2). c(3). c(4). c(5). c(6). c(7). c(8). c(9).
bts(L, Lr) :- c(A), A =\= 0, c(C), A =:= C, c(B), C =\=9, B >= A + C,
Nr is A * 100 + B * 10 + C,
add(......).
solve(L) :- bts([], L).
The output should be:
L=[121,131,141,151,161,171,181,191,242,252,262,272,282,292,363,373,383,393,484,494]
You are on the right track. Here are two small tips:
First and most importantly, focus on what a single solution looks like. You can always use meta-predicates like findall/3 and setof/3 to collect all solutions into a list of solutions.
Second, there are more intelligent ways to describe solutions in your case. Currently, you are using generate and test, which is an infeasible strategy for larger problems. Instead, use your Prolog system's constraint solvers to declaratively describe all requirements. This lets the engine apply pruning to avoid generating all combinations.
In total, my recommendation is to do it similar to the following:
abc(Ls) :-
Ls = [A,B,C],
A #= C,
A #\= 0,
C #\= 9,
B #>= A + C,
Ls ins 0..9.
We can now try the most general query to see what solutions look like in general:
?- abc(Ls).
Ls = [_940, _946, _940],
_940 in 1..4,
2*_940#=<_946,
_946 in 2..9.
This is of course not very useful, but it at least tells us that our relation terminates and is deterministic.
This means that so-called labeling, which is the search for concrete solutions will also terminate. That is a very nice property, because it means that the search will always terminate, even if it may take a long time.
In this case, the search is of course trivial, and we can use for example label/1 to enumerate solutions:
?- abc(Ls), label(Ls).
Ls = [1, 2, 1] ;
Ls = [1, 3, 1] ;
Ls = [1, 4, 1] ;
Ls = [1, 5, 1] ;
etc.
Now, to obtain the results you want, we will use findall/3 to collect all solutions into a list:
?- abc(Ls),
findall(N, (label(Ls),
atomic_list_concat(Ls,A),atom_number(A,N)), Ns).
Ls = [_774, _780, _774],
Ns = [121, 131, 141, 151, 161, 171, 181, 191, 242|...],
_774 in 1..4,
2*_774#=<_780,
_780 in 2..9.
I have also taken the liberty to apply a small transformation to each solution, so that you get the desired result immediately.
The formalism I am applying here is called CLP(FD), and the exact details for invoking this mechanism differ slightly between available Prolog systems. Check your system's manual for more information, and see also clpfd.
a simple application of findall/3 and between/3
abc(Ns) :- findall(N, (between(1,9,A),between(1,9,B),C=A,B>=A+C,N is A * 100 + B * 10 + C), Ns).
This is the solution I found:
bts(1000, []) :- !.
bts(Nr, L) :-
N is Nr + 1,
bts(N, X),
once(((number_chars(Nr, [H1, H2, H3]), number_chars(A, [H1]), number_chars(C, [H3]),
A =:= C, number_chars(B, [H2]), B >= A + C,
L = [Nr|X]);
L = X)).
solve(L) :- bts(100, L).
Exercise 1.3 of the book Structure and Interpretation of Computer Programs asks the following:
Define a procedure that takes three numbers as arguments and returns the sum of the squares of the two larger numbers.
I'm learning Prolog. Here's the function I tried to implement:
square(X, Y) :- Y is X * X.
squareTwoLargest(X, Y, Z, R) :-
R is square(L1) + square(L2), L1 = max(X, Y), L2 = max(min(X, Y), Z).
However, when I run it, it gives the following error: ERROR: is/2: Arguments are not sufficiently instantiated. I think I'm not only not getting Prolog's syntax, but I'm also not getting the logic programming paradigm yet. So, how could I implement this function in good logic programming style?
To get the two largest numbers out of three (V1, V2, and V3) you can proceed as follows: Sort the list [V1,V2,V3] and take the last two list items [_,X,Y], square and sum them.
:- use_module(library(lists)).
:- use_module(library(clpfd)).
squareTwoLargest(V1,V2,V3, R) :-
Zs = [_,X,Y],
chain(Zs, #=<),
permutation([V1,V2,V3],Zs),
R #= X*X + Y*Y.
Sample query:
?- squareTwoLargest(20,30,10, R).
R = 1300
Better implementation
Above code is based on "permutation sort", which makes it inefficient in more than one way.
The goal squareTwoLargest(X,Y,Z, R) succeeds multiple times and gives redundant answers, if two or more of X, Y, and Z are equal. This is shown by the following two queries:
?- squareTwoLargest(0,10,10, R).
R = 200 ;
R = 200 ;
false.
?- squareTwoLargest(10,10,10, R).
R = 200 ;
R = 200 ;
R = 200 ;
R = 200 ;
R = 200 ;
R = 200 ;
false.
We can eliminate the redundant answers by using a sorting network of size 3. For details, look at this answer to the question
ordering lists with constraint logic programming.
list_sorted__SN3([A0,A1,A2], [D0,D1,C2]) :-
B1 #= min(A1,A2), B2 #= max(A1,A2),
C0 #= min(A0,B2), C2 #= max(A0,B2),
D0 #= min(C0,B1), D1 #= max(C0,B1).
squareTwoLargest__SN(V1,V2,V3, R) :-
list_sorted__SN3([V1,V2,V3],[_,X,Y]),
R #= X*X + Y*Y.
Consider the following queries:
?- squareTwoLargest__SN(20,30,10, R).
R = 1300. % works like it did before
?- squareTwoLargest__SN(20,20,10, R).
R = 800. % succeeds deterministically
?- squareTwoLargest__SN(20,20,20, R).
R = 800. % succeeds deterministically
Note that all redundant answers of the corner cases shown above have been eliminated.
Unfortunately, max function you are using, is built-in arithmetic function and does not behave as a predicate, this may trick you into thinking that you will write your predicates in the same way.
In Prolog, what you will be writing is predicates. Predicate does not return any value, it just holds or does not hold (you can think of it as if it returned true or false). Your predicate square is a good example, what it square(X,Y) really means is 'Y is square of X'. If you ask Prolog console square(4, 16)., it will tell you true. If you ask square(4, 44), it will tell you false. So how do you find out square root of some number? You ask Prolog a question with free (unknown) variable square(4,R)., then Prolog will tell you that R=16. That is the important part of logical programming, you do not explain Prolog, how to calculate square, you only tell Prolog what square is in terms of logic and then you ask Prolog question and it will find answer by itself.
Soo what if you try instead of
R is square(L1) + square(L2)
something like
square(L2, L2SQUARED), square(L1, L1SQUARED), ...
which will give you square of L1 in L1SQUARED
However, L1 must not be free variable, Prolog must be able to deduce some value for it based on some other predicates (...), so that it can answer to square(L1, L1SQUARED). Imagine question square(SOMETHING1, SOMETHING2), where both arguments are unknown, what will the answer be? There is infinite number of correct answers, for example [2, 4] or [3, 9] etc.
Note: yes, it can be onliner with arithmetics, but if you want to learn logical programming, try more 'logical programming' like approach. In some flavours of Prolog, you do not get arithmetics and they are still useful...
my bet, using the 'if-then-else' construct.
squareTwoLargest(X, Y, Z, R) :-
( X > Y -> A = X, B = Y ; A = Y, B = X ),
R is A + max(B, Z).
Two temp variables are needed.
To calculate the hamming distance between two lists of the same length, I use foldl(hamm, A, B, 0, R). with this definition of hamm/4:
hamm(A, A, V, V) :- !.
hamm(A, B, V0, V1) :- A \= B, V1 is V0 + 1.
The cut in the first rule prevents the unnecessary backtracking. The second rule, however, could have been written differently:
hamm2(A, A, V, V) :- !.
hamm2(_, _, V0, V1) :- V1 is V0 + 1.
and hamm2/4 will still be correct together with foldl/5 or for queries where both A and B are ground.
So is there a really good reason to prefer the one over the other? Or is there a reason to keep the rules in that order or switch them around?
I know that the query
hamm(a, B, 0, 1).
is false, while
hamm2(a, B, 0, 1).
is true, but I can't quite decide which one makes more sense . . .
The OP implemented two accumulator-style predicates for calculating the Hamming distance (hamm/4 and hamm2/4), but wasn't sure which one made more sense.
Let's read the query that puzzled the OP: "Is there an X such that distance(a,X) is 1?". Here are the "answers" Prolog gives:
?- hamm(a,X,0,1).
false. % wrong: should succeed conditionally
?- hamm2(a,X,0,1). % wrong: should succeed, but not unconditionally
true.
From a logical perspective, both implementations misbehave in above test. Let's do a few tests for steadfastness:
?- hamm(a,X,0,1),X=a. % right
false.
?- hamm(a,X,0,1),X=b. % wrong: should succeed as distance(a,b) is 1
false.
?- hamm2(a,X,0,1),X=a. % wrong: should fail as distance(a,a) is 0
X = a.
?- hamm2(a,X,0,1),X=b. % right
X = b.
Note that in previous queries hamm/4 rightly fails when hamm2/4 wrongly succeeded, and vice-versa.
So both are half-right/half-wrong, and neither one
is steadfast.
What can be done?
Based on if_/3 and (=)/3 presented by #false in this answer, I implemented the following pure code for predicate hamm3/4:
:- use_module(library(clpfd)).
hamm3(A,B,V0,V) :-
if_(A = B, V0 = V, V #= V0+1).
Now let's repeat above queries using hamm3/4:
?- hamm3(a,X,0,1).
dif(X,a).
?- hamm3(a,X,0,1),X=a.
false.
?- hamm3(a,X,0,1),X=b.
X = b.
It works! Finally, let's ask the most general query to see the entire solution set of hamm3/4:
?- hamm3(A,B,N0,N).
A = B, N0 = N ;
dif(A,B), N0+1 #= N.
You already spotted the differences between those definitions: efficiency apart, you should decide about your requirements. Are you going to accept variables in your data structures? Such programming style introduces some of advanced Prolog features (incomplete data structures).
Anyway, I think the first form is more accurate (not really sure about, I would say steadfast on 4° argument)
?- hamm(a, B, 0, 1).
false.
?- hamm(a, B, 0, 0).
B = a.
while hamm2 is
?- hamm2(a, B, 0, 1).
true.
?- hamm2(a, B, 0, 0).
B = a.