Replacing parts of expression in prolog - replace

I need to simplify identities in prolog (e.g. x+0 = x, x-x=0, etc.). For this I need to replace parts of the expression (say x+0 by x).
Can you please help me in doing the replacement?

A neat thing about Prolog is that you can deconstruct an arithmetic expression pretty easily. Your basic template is going to look like this:
simplify(X, X) :- number(X) ; atom(X) ; var(X).
simplify(X+Y, X1+Y1) :- simplify(X, X1), simplify(Y, Y1).
simplify(X-Y, X1-Y1) :- simplify(X, X1), simplify(Y, Y1).
simplify(X*Y, X1*Y1) :- simplify(X, X1), simplify(Y, Y1).
simplify(X/Y, X1/Y1) :- simplify(X, X1), simplify(Y, Y1).
This doesn't really do anything except recursively walk the expression. We're recursively calling simplify on both sides of the operator so that the simplification patterns are matched regardless of where they occur in the expression. But for now it appears to do nothing:
?- simplify(3+X*y, Q).
Q = 3+X*y
Think of it as your empty loop. With this in hand, you can start dealing with your identities by putting "special cases" above this traversal. The identities are one thing you can handle this way:
simplify(1*X, X1) :- simplify(X, X1).
simplify(X*1, X1) :- simplify(X, X1).
simplify(0+X, X1) :- simplify(X, X1).
simplify(X+0, X1) :- simplify(X, X1).
Trying it out:
?- simplify(x*1+0, Q).
Q = x
And you can see why we're using a recursive routine here:
?- simplify(x+(y+(z*43)+0)*1, Q).
Q = x+(y+z*43)
Even though the +0 is deeply nested within the structure, it is removed. We could do some more drastic simplifications too:
simplify(_*0, 0).
simplify(0*_, 0).
No need for recursion here, the whole subexpression is basically deleted:
?- simplify(x+(y+(z*43)+1)*0, Q).
Q = x+0 ;
You can do some more interesting things as well:
simplify(X*Y+Z*Y, Y1*(X1+Z1)) :-
simplify(X, X1), simplify(Y, Y1), simplify(Z, Z1).
Trying it out:
?- simplify(34*z+17*z, X).
X = z* (34+17)
?- simplify(34*z+z*17, X).
X = 34*z+z*17
This second example here reveals that there is a limitation to this type of processing. You don't want to have to give every permutation of your templates. If you want to make them more intelligent you're probably going to have to either adopt a more intelligent intermediate representation, or a more intelligent means of applying templates than simple unification. Unification is great, but it doesn't understand the commutative or associative properties.
To go further, you're probably going to want to dig a little deeper into Prolog. This type of problem is studied in most Prolog books; I'm partial to Programming Prolog and Art of Prolog but I hear Bratko's book is amazing. I'd recommend you get your hands on one of these and start digging through it. Don't hesitate to ask more questions though!

It is possible to replace subterms in an expression using =../2. Based on this predicate, I wrote a predicate that replaces all occurrences of a pattern in a term:
:- initialization(main).
:- set_prolog_flag('double_quotes','chars').
main :- Input=((X+0)=(Y+0)-(Z+0)),replace(A+0,A,Input,Output),writeln(Input),writeln(Output).
replace(Subterm0_, Subterm_, Term0, Term) :-
copy_term([Subterm0_,Subterm_],[Subterm0,Subterm]),
( Term0=Subterm0 -> (Term = Subterm)
; var(Term0) -> Term = Term0
; Term0 =.. [F|Args0],
maplist_(Args0, Args, replace(Subterm0,Subterm)),
Term =.. [F|Args]
).
maplist_([], [], _).
maplist_([Elem1|Tail1], [Elem2|Tail2], replace(A,B)) :-
copy_term([A,B],[A1,B1]),
Goal=replace(A1,B1),
writeln(Goal),
call(Goal, Elem1, Elem2),
maplist_(Tail1, Tail2, Goal).
In this case, the input is (X+0)=(Y+0)-(Z+0) and the output is X=Y-Z.

Related

Prolog: expressing that two lists have at least two elements in common

I am trying to find out if two lists overlap. The predicate I want to write
takes two lists and returns true if the lists have at least two elements in common.
Sample queries with expected answer:
?- overlap([13,14,15], [17,18,13,19]).
false.
?- overlap([13,14,15], [14,17,13,18,16]).
true.
However, so far, I only got one element to work.
member(M, [M|_]).
member(M, [_|T]) :-
member(M, T).
overlap(X, Y) :-
member(M, X),
member(M, Y).
?- overlap([a,b,c,d], [1,2,c,d]).
How can I make sure it checks two elements, not just one?
Another approach, very close to your code, would be to make sure that the two members are not the same:
overlap(X, Y) :-
dif(A, B),
member(A, X), member(A, Y),
member(B, X), member(B, Y).
Since there is a comment asking for a more efficient way to do it, here is an altogether different approach, as in this answer to a very similar question.
overlap(N, X, Y) :-
sort(Xs, SX),
sort(Ys, SY),
append(SX, SY, All), length(All, Len_all),
sort(All, Sorted), length(Sorted, Len_sorted),
Len_sorted =< Len_all - 2.
In simple words, since sort also removes all duplicates, you can count the number of duplicates in a list by comparing the length before and after sorting. Once you write the predicate in this fashion, you will also notice that you can generalize it a bit, so that it has two arguments: a list of lists, and a non-negative integer which is the number of elements shared among all lists:
overlap_n(LL, N) :-
maplist(sort, LL, SLL), % sort all lists
append(SLL, All), length(All, Len_all),
sort(All, Sorted), length(Sorted, Len_sorted),
N is Len_all - Len_sorted.
You can now express your original question as:
?- overlap_n([X, Y], N), N >= 2.
If your Prolog has intersection/3, the shorter form could be:
overlap(X,Y) :- intersection(X,Y,[_,_|_]).
It will be inefficient for large, overlapping lists. Your approach is easily corrected and extended:
overlap(X,Y) :-
select(A,X,Rx), select(A,Y,Ry),
member(B,Rx), member(B,Ry).
I would add a cut at end to avoid multiple solutions...

How to group list elements in prolog

I am trying to group a List of elements.In this case I have some grammar notations and I want to grouped elements in same group.if there are some fruits in the list then all fruits are merge in one list.Like that there are no birds then then the Birds list come as empty.I try to use append/3 with matching but its not work.
Grammar rules:
animals-->[cat]|[dog]|[cow].
birds-->[parrot],[peacock]|[penquin].
fruits-->[apple]|[orange]|[banana].
The goal will come
?- grouped([[apple],[dog],[cat],[banana],[cow],[car]],Animals,Birds,Fruits,Rest).
Animals=[dog,cat,cow],
Birds=[],
Fruits=[apple,banana],
Rest[car].
I share some perplexity with other commenters about your representation. Also, I think this clause
birds-->[parrot],[peacock]|[penquin].
should instead be
birds-->[parrot]|[peacock]|[penquin].
given the semantic, but syntactically is interesting to handle the generality of DCG - that is, without assuming that each DCG clause matches only one literal, as in your example. Then, my solution
:- meta_predicate capture(//,+,-,-).
capture(NT, L0, Capture, R) :-
phrase(NT, L0, L1),
append(C0, L1, L0), % recover tokens matched by NT
capture(NT, L1, C1, R),
!, append(C0, C1, Capture).
capture(NT, [Skip|L0], Capture, [Skip|R]) :-
!, capture(NT, L0, Capture, R).
capture(_, L, [], L).
grouped(L0,Animals,Birds,Fruits,Rest) :-
capture(animals,L0,Animals,L1),
capture(birds,L1,Birds,L2),
capture(fruits,L2,Fruits,Rest).
yields
?- grouped([apple,dog,cat,banana,cow,car],Animals,Birds,Fruits,Rest).
Animals = [dog, cat, cow],
Birds = [],
Fruits = [apple, banana],
Rest = [car].
For an aesthetic viewpoint, grouped/4 doesn't like me too much ... but it does its job
You could solve this by first creating some kind of database:
animals(cat).
animals(dog).
animals(cow).
birds(parrot).
birds(peacock).
birds(penquin).
fruits(apple).
fruits(orange).
fruits(banana).
Next you create some kind of filter:
grouped([],[],[],[],[]).
grouped([[H]|T],[H|A],B,F,R) :-
animals(H),
!,
grouped(T,A,B,F,R).
grouped([[H]|T],A,[H|B],F,R) :-
birds(H),
!,
grouped(T,A,B,F,R).
grouped([[H]|T],A,B,[H|F],R) :-
fruits(H),
!,
grouped(T,A,B,F,R).
grouped([[H]|T],A,B,F,[H|R]) :-
!,
grouped(T,A,B,F,R).
Note that this predicate only works in one way. You can change it to work multidirectional however by removing the cuts and altering the last clause:
grouped([],[],[],[],[]).
grouped([[H]|T],[H|A],B,F,R) :-
animals(H),
grouped(T,A,B,F,R).
grouped([[H]|T],A,[H|B],F,R) :-
birds(H),
grouped(T,A,B,F,R).
grouped([[H]|T],A,B,[H|F],R) :-
fruits(H),
grouped(T,A,B,F,R).
grouped([[H]|T],A,B,F,[H|R]) :-
\+ animals(H),
\+ birds(H),
\+ fruits(H),
grouped(T,A,B,F,R).
Now however there is a possibility that collisions will start to occur (a parrot is for instance both a bird and animal).
But in my opinion there are few things rather strange with the question:
why use a list of lists? [[apple],[dog],[cat],[banana],[cow],[car]] instead of [apple,dog,cat,banana,cow,car]?
The design of using parameters as filter input/output (categories) is rather weird. If you would define a new category, it would result in rewriting the entire predicate. There are definitely better ways to do this.

Removing heads from lists in Prolog

I'm trying to write a predicate to remove the head from every list in list of lists and add the tails to a new list. The resulting list should be returned as the second parameter.
Here's the attempt:
construct_new(S,New) :-
New = [],
new_situation(S,New).
new_situation([],_).
new_situation([H|T], New) :-
chop(H, H1),
new_situation(T, [H1|New]).
chop([_|T], T).
You would call it like this:
construct_new([[x,x],[b,c],[d,e,f]],S).
This, however, only produces output true..
Step-by-step execution
Your query is construct_new(Input,Output), for some instanciated Input list.
The first statement in construct_new/2 unifies Output (a.k.a. New) with the empty list. Where is the returned list supposed to be available for the caller? Both arguments are now unified.
You call new_situation(Input,[])
You match the second clause new_situation([H|T],[]), which performs its task recursively (step 4, ...), until ...
You reach new_situation([],_), which successfully discards the intermediate list you built.
Solutions
Write a simple recursive predicate:
new_situation([],[]).
new_situation([[_|L]|T],[L|R]) :-
new_situation(T,R).
Use maplist:
construct_new(S,R) :-
maplist(chop,S,R).
Remark
As pointed out by other answers and comments, your predicates are badly named. construct_new is not a relation, but an action, and could be used to represent almost anything. I tend to like chop because it clearly conveys the act of beheading, but this is not an appropriate name for a relation. repeat's list_head_tail(L,H,T) is declarative and associates variables to their roles. When using maplist, the other predicate (new_situation) doesn't even need to exist...
...even though guillotine/3 is tempting.
This could be done with a DCG:
owth(Lists, Tails) :-
phrase(tails(Tails), Lists).
tails([]) --> [].
tails([T|Tails]) --> [[_|T]], tails(Tails).
Yielding these queries:
| ?- owth([[x,x],[b,c],[d,e,f]], T).
T = [[x],[c],[e,f]] ? ;
no
| ?- owth(L, [[x],[c],[e,f]]).
L = [[_,x],[_,c],[_,e,f]]
yes
(owth = Off with their heads! or, if used the other direction, On with their heads!)
If you also want to capture the heads, you can enhance it as follows:
owth(Lists, Heads, Tails) :-
phrase(tails(Heads, Tails), Lists).
tails([], []) --> [].
tails([H|Hs], [T|Tails]) --> [[H|T]], tails(Hs, Tails).
We use meta-predicate maplist/[3-4] with one of these following auxiliary predicates:
list_tail([_|Xs],Xs).
list_head_tail([X|Xs],X,Xs).
Let's run some queries!
?- maplist(list_head_tail,[[x,x],[b,c],[d,e,f]],Heads,Tails).
Heads = [x,b,d],
Tails = [[x],[c],[e,f]].
If you are only interested in the tails, use maplist/4 together with list_head_tail/3 ...
?- maplist(list_head_tail,[[x,x],[b,c],[d,e,f]],_,Tails).
Tails = [[x],[c],[e,f]].
... or, even simpler, maplist/3 in tandem with list_tail/2:
?- maplist(list_tail,[[x,x],[b,c],[d,e,f]],Tails).
Tails = [[x],[c],[e,f]].
You can also use the somewhat ugly one-liner with findall/3:
?- L = [[x,x],[b,c],[d,e,f]],
findall(T, ( member(M, L), append([_], T, M) ), R).
R = [[x], [c], [e, f]].
(OK, technically a two-liner. Either way, you don't even need to define a helper predicate.)
But definitely prefer the maplist solution that uses chop as shown above.
If you do the maplist expansion by hand, and name your chop/2 a bit better, you would get:
lists_tails([], []).
lists_tails([X|Xs], [T|Ts]) :-
list_tail(X, T),
lists_tails(Xs, Ts).
And since you can do unification in the head of the predicate, you can transform this to:
lists_tails([], []).
lists_tails([[_|T]|Xs], [T|Ts]) :-
lists_tails(Xs, Ts).
But this is identical to what you have in the other answer.
Exercise: why can't we say:
?- maplist(append([_]), R, [[x,x],[b,c],[d,e,f]]).

Prolog Ancestor with List

I searched around and couldn't find the answer. I'm having trouble making a genealogy list.
So, I have some is_a relations, for example:
is_a(cow, animal).
is_a(calf, cow).
is_a(dog, animal).
.... etc.
I want to have a procedure that does the following:
toAnimal(cow, X).
that outputs
X= [calf, cow, animal].
Basically, if I give it an input(cow), then it will go from cow until animal and add every step to the list.
So far, I have this:
toAnimal(A, B) :- is_a(A,B).
toAnimal(A, B) :- is_a(A, X), toAnimal(X, B).
The output of this is would be
X= cow;
X = animal;
false
How would I get this to become a list?
EDIT:
descend(X,Y) :- is_a(X,Y).
descend(X,Y) :- is_a(X,Z), descend(Z,Y).
toAnimal(X,Y):-
findall(X, descend('animal', X), Y).
I have updated it to this after looking at the suggestion. However, how do I get the list to print? I'm still new to prolog. The findall page said that it would return the list, but it is not doing so for me.
toAnimal(calf, Y)
outputs:
false.
EDIT:
It now returns an empty list. I'm not sure what the issue is here. I have not changed the code at all, so the output should not change but it has.
EDIT:
Thanks MrBratch for the response.
I made the suggested changes, but I now have another issue.
For example,
if I have the relations:
is_a(calf, cow).
is_a(calf, animal).
is_a(cow, cool).
is_a(cool, awesome).
But I ONLY want the path from calf to awesome.
The code will give me the ALL possible paths from calf,x.
For example,
descend(X,Y) :- is_a(X,Y).
descend(X,Y) :- is_a(X,Z), descend(Z,Y).
toAwesome(A,Y) :-
findall(X, descend(calf, X), Y).
will give me a list Y that has
[cow,animal,cool,awesome].
but what I want is
[calf,cow,cool,awesome].
How do I filter the other paths?
and also add the starting point? I suppose I can append calf to the beginning as the head, but how do I ignore the other paths?
EDIT:
Thanks for the help
I figured it out, but I lose the end path and start path. For example,
L contains cow,cool.
But calf and awesome are not there. I tried appending but I don't really understand the syntax. I'm not allowed to do append(X,L,anewlist)?
descend(X,Y) :- is_a(X,Y).
descend(X,Y) :- is_a(X,Z), descend(Z,Y).
toAnimal(A,B) :-
setof(X, (descend(A,X), descend(X,'awesome')), B).
--> append(A, L,anewlist).
?? Is this line not allowed here? How else would I do it? or is there a simpler way to just add it from the beginning
Here it is. (NOTE: you don't need descend predicate to figure out the path of a particular branch of the tree)
is_a(calf, cow).
is_a(calf, animal).
is_a(cow, cool).
is_a(cool, awesome).
path(X,Y,[Z|T]) :- \+ is_a(X,Y), is_a(X,Z), path(Z,Y,T).
path(X,Y,[Y]) :- is_a(X,Y).
find_path(X,Y,[X|L]) :- path(X,Y,L).
Usage:
| ?- find_path(calf,awesome,L).
L = [calf,cow,cool,awesome] ? ;
This sample more or less does what you want:
is_a(cow, animal).
is_a(calf, cow).
is_a(dog, animal).
is_a(snoopy, dog).
is_a(lassie, collie).
is_a(collie, dog).
toAnimal3( X, [X,animal] , animal ):- is_a( X, animal).
toAnimal3( X, [X|R], R ):- is_a( X, Y), toAnimal3(Y, R, _).
:- initialization(main).
main :- toAnimal3( lassie, A, B), write(A), write(B).
When run, this is the output:
[lassie,collie,dog,animal][collie,dog,animal]
Tested it online using this Prolog online interpreter
POST EDIT: Ah, that was it! I should've written "[X,animal]" instead of "[X|animal]" for the first clause! Thanks galore to #mbratch , now the program does exactly what was intended.
toAnimal(X,Y) :- setof(X, descend('animal', X), Y). should do it. Or findall/3.
Info and some examples of bagof, setof, findall.
But remember that you are asking for descend(animal, X) so it won't match the fact is_a(dog, animal) for example, which descend(X, animal) will. You need to make descend to search both sides, or simply be sure that your is_a facts say animal just on left side.
If you want to filter you could do
toAnimal(X,Y) :- setof(X, (descend('animal', X), not(X = animal)), Y).
but you are getting animal as a result because what I mentioned before.

How can I check if an element in the list is an empty list: []?

How can I check if an element in the list is an empty list: [] ?
I've got the following:
display_degrees([A,B,C,D]):- write(B).
display_degrees([A,B,C,D]):- B==[], nl,write('has no degree'), nl, !.
When I enter in something like:
display_degrees([1,[],3,4]).
I just get: [] instead of 'has no degree'. Is my syntax wrong? Can I not add a clause to this predicate like this?
You're getting this behavior because proof search stops when a goal has succeeded. When you type
display_degrees([1,[],3,4]).
the first rule unifies, and it writes B. Since it was a success, it stops. You can ask Prolog to keep searching, and then it will find the second clause. In swipl, I get
?- [foo].
?- display_degrees([1,[],3,4]).
[]
true r % I type 'r' there
has no degree
true.
If you're just learning Prolog, I suggest you avoid the cut operator ! for some time. Also, doing IO is not the most intuitive thing. I would try some exercises with defining things like natural numbers and recursive functions. E.g., plus:
plus(z, X, X).
plus(s(X), Y, s(Z)) :- plus(X, Y, Z).
The problem with what you have is that the more general rule will fire first. You could switch the order:
display_degrees([A,[],C,D]) :- nl, write('has no degree'), nl, !.
display_degrees([A,B,C,D]) :- write(B).
I could just as well have written for the first predicate:
display_degrees([A,B,C,D]) :- B == [], nl, write('has no degree'), nl, !.
But the "shortcut" I show initially is more idiomatic for a Prolog predicate like this.
I kept the cut since you know you deterministically want one choice. The first rule will match if and only if the second list element is [].
| ?- display_degrees([1,[],3,4]).
has no degree
yes
| ?- display_degrees([1,2,3,4]).
2
yes
| ?-