Why does Prolog match (X, Xs) with a tuple containing more elements? An example:
test2((X, Xs)) :- write(X), nl, test2(Xs).
test2((X)) :- write(X), nl.
test :-
read(W),
test2(W).
?- test.
|: a, b(c), d(e(f)), g.
a
b(c)
d(e(f))
g
yes
Actually this is what I want to achieve but it seems suspicious. Is there any other way to treat a conjunction of terms as a list in Prolog?
Tuple term construction with the ,/2 operator is generally right-associative in PROLOG (typically referred to as a sequence), so your input of a, b(c), d(e(f)), g might well actually be the term (a, (b(c), (d(e(f)), g))). This is evidenced by the fact that your predicate test2/1 printed what is shown in your question, where on the first invocation of the first clause of test2/1, X matched a and Xs matched (b(c), (d(e(f)), g)), then on the second invocation X matched b(c) and Xs matched (d(e(f)), g), and so on.
If you really wanted to deal with a list of terms interpreted as a conjunction, you could have used the following:
test2([X|Xs]) :- write(X), nl, test2(Xs).
test2([]).
...on input [a, b(c), d(e(f)), g]. The list structure here is generally interpreted a little differently from tuples constructed with ,/2 (as, at least in SWI-PROLOG, such structures are syntactic sugar for dealing with terms constructed with ./2 in much the same way as you'd construct sequences or tuple terms with ,/2). This way, you get the benefits of the support of list terms, if you can allow list terms to be interpreted as conjunctions in your code. Another alternative is to declare and use your own (perhaps infix operator) for conjunction, such as &/2, which you could declare as:
:- op(500, yfx, &). % conjunction constructor
You could then construct your conjunct as a & b(c) & d(e(f)) & g and deal with it appropriately from there, knowing exactly what you mean by &/2 - conjunction.
See the manual page for op/3 in SWI-PROLOG for more details - if you're not using SWI, I presume there should be a similar predicate in whatever PROLOG implementation your'e using -- if it's worth it's salt :-)
EDIT: To convert a tuple term constructed using ,/2 to a list, you could use something like the following:
conjunct_to_list((A,B), L) :-
!,
conjunct_to_list(A, L0),
conjunct_to_list(B, L1),
append(L0, L1, L).
conjunct_to_list(A, [A]).
Hmm... a, b(c), d(e(f)), g means a and (b(c) and (d(e(f)) and g)), as well list [1,2,3] is just a [1 | [2 | [3 | []]]]. I.e. if you turn that conjuction to a list you'll get the same test2([X|Xs]):-..., but difference is that conjunction carries information about how that two goals is combined (there may be disjunction (X; Xs) as well). And you can construct other hierarchy of conjunctions by (a, b(c)), (d(e(f)), g)
You work with simple recursive types. In other languages lists is also recursive types but they often is pretending to be arrays (big-big tuples with nice indexing).
Probably you should use:
test2((X, Y)):- test2(X), nl, test2(Y).
test2((X; Y)). % TODO: handle disjunction
test2(X) :- write(X), nl.
Related
All of these predicates are defined in pretty much the same way. The base case is defined for the empty list. For non-empty lists we unify in the head of the clause when a certain predicate holds, but do not unify if that predicate does not hold. These predicates look too similar for me to think it is a coincidence. Is there a name for this, or a defined abstraction?
intersect([],_,[]).
intersect(_,[],[]).
intersect([X|Xs],Ys,[X|Acc]) :-
member(X,Ys),
intersect(Xs,Ys,Acc).
intersect([X|Xs],Ys,Acc) :-
\+ member(X,Ys),
intersect(Xs,Ys,Acc).
without_duplicates([],[]).
without_duplicates([X|Xs],[X|Acc]) :-
\+ member(X,Acc),
without_duplicates(Xs,Acc).
without_duplicates([X|Xs],Acc) :-
member(X,Acc),
without_duplicates(Xs,Acc).
difference([],_,[]).
difference([X|Xs],Ys,[X|Acc]) :-
\+ member(X,Ys),
difference(Xs,Ys,Acc).
difference([X|Xs],Ys,Acc) :-
member(X,Ys),
difference(Xs,Ys,Acc).
delete(_,[],[]).
delete(E,[X|Xs],[X|Ans]) :-
E \= X,
delete(E,Xs,Ans).
delete(E,[X|Xs],Ans) :-
E = X,
delete(E,Xs,Ans).
There is an abstraction for "keep elements in list for which condition holds".
The names are inclide, exclude. There is a library for those in SWI-Prolog that you can use or copy. Your predicates intersect/3, difference/3, and delete/3 would look like this:
:- use_module(library(apply)).
intersect(L1, L2, L) :-
include(member_in(L1), L2, L).
difference(L1, L2, L) :-
exclude(member_in(L2), L1, L).
member_in(List, Member) :-
memberchk(Member, List).
delete(E, L1, L) :-
exclude(=(E), L1, L).
But please take a look at the implementation of include/3 and exclude/3, here:
https://www.swi-prolog.org/pldoc/doc/_SWI_/library/apply.pl?show=src#include/3
Also in SWI-Prolog, in another library, there are versions of those predicates called intersection/3, subtract/3, delete/3:
https://www.swi-prolog.org/pldoc/doc/_SWI_/library/lists.pl?show=src#intersection/3
https://www.swi-prolog.org/pldoc/doc/_SWI_/library/lists.pl?show=src#subtract/3
https://www.swi-prolog.org/pldoc/doc_for?object=delete/3
Those are similar in spirit to your solutions.
Your next predicate, without_duplicates, cannot be re-written like that with include/3 or exclude/3. Your implementation doesn't work, either. Try even something easy, like:
?- without_duplicates([a,b], L).
What happens?
But yeah, it is not the same as the others. To implement it correctly, depending on whether you need the original order or not.
If you don't need to keep the initial order, you can simply sort; this removes duplicates. Like this:
?- sort(List_with_duplicates, No_duplicates).
If you want to keep the original order, you need to pass the accumulated list to the recursive call.
without_duplicates([], []).
without_duplicates([H|T], [H|Result]) :-
without_duplicates_1(T, [H], Result).
without_duplicates_1([], _, []).
without_duplicates_1([H|T], Seen0, Result) :-
( memberchk(H, Seen0)
-> Seen = Seen0 , Result = Result0
; Seen = [H|Seen0], Result = [H|Result0]
),
without_duplicates_1(T, Seen, Result0).
You could get rid of one argument if you use a DCG:
without_duplicates([], []).
without_duplicates([H|T], [H|No_duplicates]) :-
phrase(no_dups(T, [H]), No_duplicates).
no_dups([], _) --> [].
no_dups([H|T], Seen) -->
{ memberchk(H, Seen) },
!,
no_dups(T, Seen).
no_dups([H|T], Seen) -->
[H],
no_dups(T, [H|Seen]).
Well, these are the "while loops" of Prolog on the one hand, and the inductive definitions of mathematical logic on the other hand (See also: Logic Programming, Functional Programming, and Inductive Definitions, Lawrence C. Paulson, Andrew W. Smith, 2001), so it's not surprising to find them multiple times in a program - syntactically similar, with slight deviations.
In this case, you just have a binary decision - whether something is the case or not - and you "branch" (or rather, decide to not fail the body and press on with the selected clause) on that. The "guard" (the test which supplements the head unification), in this case member(X,Ys) or \+ member(X,Ys) is a binary decision (it also is exhaustive, i.e. covers the whole space of possible X)
intersect([X|Xs],Ys,[X|Acc]) :- % if the head could unify with the goal
member(X,Ys), % then additionally check that ("guard")
(...action...). % and then do something
intersect([X|Xs],Ys,Acc) :- % if the head could unify with the goal
\+ member(X,Ys), % then additionally check that ("guard")
(...action...). % and then do something
Other applications may need the equivalent of a multiple-decision switch statement here, and so N>2 clauses may have to be written instead of 2.
foo(X) :-
member(X,Set1),
(...action...).
foo(X) :-
member(X,Set2),
(...action...).
foo(X) :-
member(X,Set3),
(...action...).
% inefficient pseudocode for the case where Set1, Set2, Set3
% do not cover the whole range of X. Such a predicate may or
% may not be necessary; the default behaviour would be "failure"
% of foo/1 if this clause does not exist:
foo(X) :-
\+ (member(X,Set1);member(X,Set2);member(X,Set3)),
(...action...).
Note:
Use memberchk/2 (which fails or succeeds-once) instead of member/2 (which fails or succeeds-and-then-tries-to-succeed-again-for-the-rest-of-the-set) to make the program deterministic in its decision whether member(X,L).
Similarly, "cut" after the clause guard to tell Prolog that if a guard of one clause succeeds, there is no point in trying the other clauses because they will all turn out false: member(X,Ys),!,...
Finally, use term comparison == and \== instead of unification = or unification failure \= for delete/3.
Anyone knows how to tell Prolog that n(n(p)) and n(p) represent the same thing?
Or how to reduce n(n(p)) to n(p)?
Anyone knows how to tell Prolog that n(n(p)) and n(p) represent the same thing?
Prolog's unification system has a clear meaning of when two things are the same. n(n(p)) and n(p) are not the same thing. You can construct a predicate that defines a more generic equivalence relation than (=)/2.
Normally in Prolog two constants are different if they have a different name. Two compound terms are the same only if the functor is the same (the name of the functor and the number of parameters), and the parameters are equal as well.
We thus can define a predicate like:
equiv_(n(n(p)), n(p)).
equivalence(L, R) :-
equiv_(L, R).
equivalence(L, R) :-
equiv_(R, L).
equivalence(L, L).
If you the match with equivalence(n(n(p)), n(p)), it will return true.
#false Can't I define a predicate n(n(p)) that returns n(p). What I want, in fact, is that all occurrences of the within a list to be replaced with the latter.
You can make a predicate that unifies a second parameter with n(p) if the first one is n(n(p)):
replace_nnp(X, n(p)) :-
X == n(n(p)).
replace_nnp(X, X) :-
X \== n(n(p)).
Then we can use maplist/3 [swi-doc] to map a list of items to another list of items:
replace_nnp_list(LA, LB) :-
maplist(replace_nnp, LA, LB).
For example:
?- replace_nnp_list([n(n(p)), p, n(p), n(n(p))], X).
X = [n(p), p, n(p), n(p)] ;
false.
I am looking for ways to make a Prolog program "look" more like first order logic. Things I would like to have are for example:
-> for implication
antecedent to the left of ->
^ for conjunction v for disjunction
Or is there other software that already implements this?
Thanks in advance!
/JC
Update 20190313
I followed the suggestions in the answers below and tried this:
:- op(1200, xfx, ==>).
:- op(1000, xfy, /\).
:- op(1100, xfy, \/).
term_expansion(A ==> B, B:- A).
term_expansion(A /\ B, A, B).
term_expansion(A \/ B, A; B).
man(X) /\ unmarried(X) ==> bachelor(X).
man(john).
man(peter).
unmarried(john).
main:-bachelor(X), writeln(X), nl, fail.
But i get the following error:
ERROR: bachelor/1: Undefined procedure: (/\)/2
Exception: (5) man(_1740)/\unmarried(_1740) ?
Only using the op/3 and term_expansion/3 for ==> however works as expected. Not sure why this is so...
Use term_expansion/2 that is macro of Prolog on SWI-Prolog:
% calc.pl
:- op(1200,xfx,--).
term_expansion(A--B,B:-A).
integer(I)
--%----------------------- (E-Int)
I => I.
E1=>I1, E2=>I2, I is I1+I2
--%----------------------- (E-Add)
E1+E2 => I.
:- 1+2+3=>6.
:- 1+2+3=>I,writeln(I).
:- halt.
and run
$ swipl calc.pl
6
Here are a few Unicode characters that can help you:
¬
→ ⇒
← ⇐
∨ ∧
∀ ∃
I leave defining suitable precedences as a challenge, using op/3.
Once you have these definitions, you can write first-order sentences with them. You can then convert these sentences to Prolog, or interpret them with Prolog.
This answer refers to your updated question ("Update 20190313").
Be careful when defining operators:
Don't redefine standard operators, changing their specifier/precedence.
This can introduce errors in existing code which are very hard to find.
Weigh benefits and costs upfront.
Aim at readability, shorter code, and fewer parentheses.
Keep in mind that using too many custom Prolog operators can also obfuscate code and confuse the reader.
Think twice before using standard operators in different domains.
Let's take the predefined (\/)/2 as an example.
It is an evaluable functor in arithmetic expressions—used with (is)/2, (=:=)/2, (<)/2, etc.
clpfd uses it for representing set unions like 1..3 \/ 5..7—fine!
However, using it for denoting list concatenation is questionable.
Let's get to your actual question!
Consider these queries decomposing some terms using (=..)/2 ("univ"):
?- term_expansion(A /\ B, A, B) =.. Xs.
Xs = [term_expansion, A/\B, A, B].
?- term_expansion(A \/ B, A; B) =.. Xs.
Xs = [term_expansion, A\/B, (A;B)].
So it's term_expansion/2 for (\/)/2, but term_expansion/3 for (/\)/2!
The bottom line: (',')/2 terms as arguments need parentheses.
?- term_expansion(A /\ B, (A,B)) =.. Xs.
Xs = [term_expansion, A/\B, (A,B)].
This is the code that i am trying to understand.
co(X) :- co(X,[],L).
co([],A,A):- write(A).
co([X|Xs], A, L) :- p(X-Z,A,R), !, Z1 is Z+1, co(Xs, [X-Z1|R], L).
co([X|Xs], A, L) :- co(Xs, [X-1|A], L).
p(X-Y,[X-Y|R],R):- !.
p(X,[H|Y], [H|Z]) :- p(X,Y,Z).
What is the use of '!' and predicate p(,,) in the above code. OR Can anybody just add comments in every step of the above code so that i can able to understand . Thanks.
There are many things to address in your program. Cuts are not even the major concern. Please, bring me the broom.
Clean up the interface
What is the precise interface you are after? The purpose of co(Xs) currently, is to produce a side effect. Otherwise it can succeed or fail for a given list. But not more than that. Yet, this side effect is not at all needed - and is for most situations not a helpful approach, since such a program is practically unreusable and defies any logical reasoning. You need to leave a hole to let some result lurk out of the relation. Add another argument and remove the goal write/1 in co/3.
co(Xs, D) :-
co(Xs, [], D).
Now you can test the program with the top-level shell alone. You do not need any harness or sandbox to check for the "output". It is there, readily in a separate argument.
Clean up the program structure
Next is co/3 itself. Here, the best is to clarify the intention by separating a bit the concerns, and making these extra arguments a bit more intention-revealing. D stands for dictionary. Another good name would be KVs meaning list (the plural s) of key-value pairs. Note how the different states are numbered: They start with D0, D1, ... and at the end there is D. In this manner, if you start to write a rule, you can put D0,D already in the head without knowing how many states you will need in that rule.
co([], D,D).
co([X|Xs], D0,D) :-
nn(X, D0,D1),
co(Xs, D1,D).
nn(K, D0,D) :-
p(K-V0,D0,D1), !,
V is V0+1,
D = [X-V|D1].
nn(K, D0,D) :-
D = [K-1|D0].
p(X-Y,[X-Y|R],R):- !.
p(X,[H|Y], [H|Z]) :- p(X,Y,Z).
co/3 now more clearly reveals its intention. It somehow relates the elements of a list to some state that is "updated" for each element. There is a word for this: This is a left-fold. And there is even a predicate for it: foldl/4. So we could equally define co/3 as:
co(Xs, D0,D) :-
foldl(nn, Xs, D0,D).
or better get rid of co/3 altogether:
co(Xs, D) :-
foldl(nn, Xs, [], D).
foldl(_C_3, [], S,S).
foldl(C_3, [X|Xs], S0,S) :-
call(C_3, X, S0,S1),
foldl(C_3, Xs, S1,S).
Note, that so far, I have not even touched any cuts of yours, these are now their last moments...
Remover superfluous cuts
The cut in p/3 does not serve any purpose. There is a cut immediately after the goal p/3 anyway. Then, X-Y is not needed in p/3, you can safely replace it by another variable. In short, p/3 is now the predicate select/3 from the Prolog prologue.
select(E, [E|Xs], Xs).
select(E, [X|Xs], [X|Ys]) :-
select(E, Xs, Ys).
nn(K, D0,D) :-
select(K-V0, D0,D1), !,
V is V0+1,
D = [K-V|D1].
nn(K, D0,D) :-
D = [K-1|D0].
This one remaining cut cannot be removed so easily: it protects the alternate clause from being used should K-V not occur in D. However, there are still better ways to express this.
Replace cuts with (\+)/1
nn(K, D0,D) :-
select(K-V0, D0,D1),
V is V0+1,
D = [K-V|D1].
nn(K, D0,D) :-
\+select(K-_, D0,_),
D = [K-1|D0].
Now, each rule states what it wants for itself. This means, that we can now freely change the order of those rules. Call it superstition, but I prefer:
nn(K, D0,D) :-
\+select(K-_, D0,_),
D = [K-1|D0].
nn(K, D0,D) :-
select(K-V0, D0,D1),
V is V0+1,
D = [K-V|D1].
Purify with dif/2
To make this into a true relation, we need to get rid of this negation. Instead of saying, that there is no solution, we can instead demand that all keys (key is the first argument in Key-Value) are different to K.
nokey(_K, []).
nokey(K, [Kx-|KVs]) :-
dif(K, Kx),
nokey(K, KVs).
nn(K, D,[K-1|D]) :-
nokey(K, D).
nn(K, D0,[K-V|D]) :-
select(K-V0, D0,D),
V is V0+1.
With the help of lambdas, nokey(K, D) becomes maplist(K+\(Kx-_)^dif(Kx,K), D)
To summarize, we have now:
co(Xs, D) :-
foldl(nn, Xs, [], D).
nn(K, D,[K-1|D]) :-
maplist(K+\(Kx-_)^dif(Kx,K), D).
nn(K, D0,[K-V|D]) :-
select(K-V0, D0,D),
V is V0+1.
So what is this relation about: The first argument is a list, and the second argument a Key-Value list, with each element and the number of occurrences in the list.
Beginners tend to use !/0 because they are not aware of its negative consequences.
This is because most Prolog textbooks that are popular among beginners are quite bad and often contain wrong and misleading information about !/0.
There is an excellent answer by #false on when to use !/0. In summary: don't.
Instead, focus on a declarative description about what holds, and try to make the description elegant and general using pure and monotonic methods like constraints, clean representations, ...
I wanted to write evaluating predicate in Prolog for arithmetics and I found this:
eval(A+B,CV):-eval(A,AV),eval(B,BV),CV is AV+BV.
eval(A-B,CV):-eval(A,AV),eval(B,BV),CV is AV-BV.
eval(A*B,CV):-eval(A,AV),eval(B,BV),CV is AV*BV.
eval(Num,Num):-number(Num).
Which is great but not very DRY.
I've also found this:
:- op(100,fy,neg), op(200,yfx,and), op(300,yfx,or).
positive(Formula) :-
atom(Formula).
positive(Formula) :-
Formula =.. [_,Left,Right],
positive(Left),
positive(Right).
?- positive((p or q) and (q or r)).
Yes
?- positive(p and (neg q or r)).
No
Operator is here matched with _ and arguments are matched with Left and Right.
So I came up with this:
eval(Formula, Value) :-
Formula =.. [Op, L, R], Value is Op(L,R).
It would be DRY as hell if only it worked but it gives Syntax error: Operator expected instead.
Is there a way in Prolog to apply operator to arguments in such a case?
Your almost DRY solution does not work for several reasons:
Formula =.. [Op, L, R] refers to binary operators only. You certainly want to refer to numbers too.
The arguments L and R are not considered at all.
Op(L,R) is not valid Prolog syntax.
on the plus side, your attempt produces a clean instantiation error for a variable, whereas positive/1 would fail and eval/2 loops which is at least better than failing.
Since your operators are practically identical to those used by (is)/2 you might want to check first and only then reuse (is)/2.
eval2(E, R) :-
isexpr(E),
R is E.
isexpr(BinOp) :-
BinOp =.. [F,L,R],
admissibleop(F),
isexpr(L),
isexpr(R).
isexpr(N) :-
number(N).
admissibleop(*).
admissibleop(+).
% admissibleop(/).
admissibleop(-).
Note that number/1 fails for a variable - which leads to many erroneous programs. A safe alternative would be
t_number(N) :-
functor(N,_,0),
number(N).