Retrieve Items in Prolog - list

Say I have this knowledge base:
step('pancakes', 1, 'mix butter and sugar in a bowl', [butter, sugar], [bowl]).
step('pancakes', 2, 'add eggs', [eggs], []).
step('pancakes', 3, 'mix flour and baking powder', [flour, baking powder], []).
How do I make a predicate that retrieves all the ingredients for all the steps of the recipe?
So if I would make a rule retrieveIngredients(X,Y). and ask retrieveIngredients('pancakes',Y)., how would I be able to make it retrieve Y = ['butter','eggs','flour', 'baking powder'].?

Simply using findall/3:
retrieveIngredients(R,Y):-
findall(A,step(R,_,_,A,_),Y).
?- retrieveIngredients('pancakes',Y).
Y = [[butter, sugar], [eggs], [flour, baking_powder]]
If you want to get the list flattened, add a call to flatten/2. If you want to remove duplicates, add a call to sort/2. Note that you need to rewrite baking powder as baking_powder (without space) in the third step/5 fact.

Related

Merging elements of tuples in a list in Prolog

I need to create a list from a knowledgebase that could look like this:
fact1(3,3).
fact1(2,3).
fact1(3,5).
fact1(2,2).
fact1(2,10).
fact1(3,1).
fact1(1,1).
fact1(1,6).
fact2(3,a,b)
fact2(2,c,d)
fact2(1,e,f)
That list needs to contain tuples with each containing the second and third Value of fact2, and the added numbers of fact2, whenever the first value of fact1 and fact2 match up.
Maybe it gets clearer when I show what I have so far.
Here is my predicate with the findall statement, which to me seems to get me the closest to where I need to get:
collect_items(List):-
findall((Out1,Out2,Nr),
(fact2(Val1,Out1,Out2),
fact1(Val1,Nr)),
List).
The List I receive from this, looks like this:
List = [(a,b,3),(a,b,5),(a,b,1),(c,d,3),(c,d,2),(c,d,10),(e,f,1),(e,f,6)]
But really I need the list to look like this:
List = [(a,b,9),(c,d,15),(e,f,7)]
Meaning that, whenever the first two elements of a tuple match up, the numbers, which are the third element of the tuple, should be added together.
I do not know however how to even approach something like this, as I have always read, that as soon as the list is set, it cannot be changed, since prolog is functional and declarative.
So I think I somehow need to match every element against the one before or after it (since the list will always be sortet by the Out1 and Out2 variables), and if they match, add the third value in the tuple together. The problem is, that I have no idea how.
To me it looks like this can not realy be done within the findall itself but would need to be done after the findall
I am a true beginner and would appreciate any help. In this case it would be best, if the solution was all in one predicate.
Here is another solution that uses more than one predicate:
collect_items(Result):-
findall([Out1,Out2,Nr],(fact2(Val1,Out1,Out2),fact1(Val1,Nr)),[[OutA, OutB, N]|B]),
sumElements([[OutA, OutB, N]|B], Result).
sumElements([],[]).
sumElements([[Out, Outt, N]|B], [[Out, Outt, SumLocal]|RestOfList]):-
findall([Out, Outt, X], member([Out, Outt, X], [[Out, Outt, N]|B]), SubList),
sumLocal(SubList, SumLocal),
subtract([[Out, Outt, N]|B], SubList, New),
sumElements(New, RestOfList).
sumLocal([],0).
sumLocal([[_,_,S]|B], T):-
sumLocal(B, R),
T is S + R.
output:
?- collect_items(Result).
Result = [[a, b, 9], [c, d, 15], [e, f, 7]].
with library aggregate:
collect_items(L) :-
setof((U,V,S),
K^aggregate((set(X/Y),sum(N)), (
fact2(K,X,Y),
fact1(K,N)
), ([U/V],S)), L).
we get
?- collect_items(L).
L = [(a, b, 9), (c, d, 15), (e, f, 7)].
You are not right here
since prolog is functional and declarative
Prolog is relational and declarative

Prolog - searching a list inside a predicate

I have predicates of students and sports they do, and I want to find out which students do a particular sport. I have this sofar, but i can only get results if I enter exact sports in a list , and my find predicate works only to find a sport in a list. I don't know how to put it together to use to find students that do 1 sport:
student('Quinton Tarentino', male, 12).
student('Tom Hanks', male, 9).
student('Ed Harris', male, 11).
does_sport('Quinton Tarentino', [soccer, hockey, cricket]).
does_sport('Tom Hanks', []).
does_sport('Ed Harris', [hockey, swimming]).
sports([soccer, hockey, swimming, cricket, netball]).
find(X) :- sports(L), member(X, L).
I tried things like:
?- does_sport(X, find(soccer, L)).
This just returns false. I know I need to link my sports list to the does_sports predicate but not sure how.
Any advice appreciated :)
To find out which students do a particular sport, you could define a predicate like so:
student_sport(St,Sp) :-
does_sport(St,L), % L is a list of sports student St does
member(Sp,L). % Sp is a member of list L
Then you can query for e.g. soccer, as you seem to intend in your question, like so:
?- student_sport(St,soccer).
St = 'Quintin Tarentino' ? ;
no
Hockey on the other hand yields two results:
?- student_sport(St,hockey).
St = 'Quintin Tarentino' ? ;
St = 'Ed Harris' ? ;
no
If you want to have a list of students doing hockey instead, you can use findall/3 like so:
?- findall(St,student_sport(St,hockey),L).
L = ['Quintin Tarentino','Ed Harris']
Or alternatively setof/3 to get a sorted list (without duplicates, in case you happened to have facts that contain any):
?- setof(St,student_sport(St,hockey),L).
L = ['Ed Harris','Quintin Tarentino']
Note that in some Prologs you might have to explicitly include a library to use member/2, e.g. in Yap: :- use_module(library(lists))., while others autoload it, e.g. SWI.
EDIT:
Concerning the issues you raised in your comment, let's maybe start with your observation that student_sport/2 produces the answers one at a time. That is intentional, as suggested by the predicate name that contains the word student in singular: It describes a relation between a student and a particular sport that very student practices. That's why I added the example queries with findall/3 and setof/3, to show ways how you can collect solutions in a list. You can easily define a predicate students_sport/2 that describes a relation between a particular sport and a list of all students who practice it:
students_sport(L,Sp) :-
setof(St,student_sport(St,Sp),L).
Concerning the sports-austere, you can choose an atom to denote that case, say none and then add an according rule to student_sport/2 like so:
student_sport(St,none) :- % <- rule for the sports-austere
does_sport(St,[]). % <- succeeds if the student does no sport
student_sport(St,Sp) :-
does_sport(St,L),
member(Sp,L).
This yields the following results:
?- student_sport(St,none).
St = 'Tom Hanks' ? ;
no
?- students_sport(St,none).
St = ['Tom Hanks']
?- students_sport(St,hockey).
St = ['Ed Harris','Quintin Tarentino']
?- students_sport(St,Sp).
Sp = cricket,
St = ['Quintin Tarentino'] ? ;
Sp = hockey,
St = ['Ed Harris','Quintin Tarentino'] ? ;
Sp = none,
St = ['Tom Hanks'] ? ;
Sp = soccer,
St = ['Quintin Tarentino'] ? ;
Sp = swimming,
St = ['Ed Harris']
And finally, concerning your assumption of your code being exactly as I wrote it: There is a similarity in structure, namely your predicate find/1 having a first goal (sports/1) involving a list and subsequently using member/2 to check for membership in that list. The second rule (or single rule before the edit) of student_sport/2 is also having a first goal (but a different one: does_sport/2) involving a list and subsequently using member/2 to check for membership in that list. Here the similarities end. The version I provided is not using sports/1 at all but rather the list of sports associated with a particular student in does_sport/2. Note that find/1 does not describe any connection to students whatsoever. Furthermore your query ?- does_sport(X, find(soccer, L)). indicates that you seem to expect some sort of return value. You can regard predicates as functions returning true or false but that is usually not very helpful when programming Prolog. The argument find(soccer,L) is not being called as you seem to expect, but literally passed as an argument. And since your facts do not include something along the lines of
does_sport(*SomeStudentHere*, find(soccer,L)).
your query fails.

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]]).

Merge alternate elements from two lists in Prolog

I need to write a Prolog predicate mergealt(X,Y,Z) that succeeds if the list Z is a merger of alternate elements from the lists X and Y.
The input and output will like below:
?- mergealt([1,2,3,4],[6,7,8],Z).
Z = [1, 7, 3] .
?- mergealt([1,2,3,4],[6,7,8,9],Z).
Z = [1, 7, 3, 9] .
?- mergealt([1,2,3,4],[6,7,8,9,10],Z).
Z = [1, 7, 3, 9] .
I don't really understand recursion. How can I get started on this problem?
Prolog can be considered the 'flagman' of declarative languages.
So try to describe your problem, top down:
mergealt(X, Y, Z) :-
'take first element from X and put it in Z',
'discard first element from Y',
'mergealt rest-of-X and rest-of-Y, but exchange them'.
First step can't be accomplished if there are no elements in X.
This fact highlights the recursion termination case. Originally, Prolog didn't used if then else, instead alternatives are stated as different rules:
mergealt([], _Y, []).
Here you can see that pattern matching on first argument it's the key to distinguish alternatives, and contextually, Z get bound to an empty list. Y is unused, so it's marked as anonymus place holder, just to avoid a warning.
Then this simpler case suggests that we should use pattern matching to accomplish those verbose descriptions. See if you can complete the procedure with these guidelines:
mergealt([X|Xs], Y, [X|Zs]) :-
% take first element from X and put it in Z : done in the head
% discard first element from Y : see below
% mergealt rest-of-X and rest-of-Y, but exchange them'. : make your recursive call
discard_first_element([_|Rest], Rest).
% why is this necessary? do you see where it fails if we don't specify this case?
discard_first_element([], []).
Notice that the result always starts with the first element of the first list.
This means that, if the first list is empty, you know the answer right away.
Also notice that, if it isn't empty, we already know the first item of the result, so we need to use mergealt to compute the rest. But "the rest" will have the second item of the second list as the first item of the result, and as we said above, that means that a call to mergealt to compute it would have to have that be the first item of the first list (yeah, this is the tricky part).

Prolog list adding

--the question has been edited--
Using this data, I need to create a list:
team(milan,1).
team(napoli,2).
team(lazio,3).
team(roma,4).
team(inter,4).
team(juventus,5).
So, given a query like:
check([milan,lazio,roma,inter]).
make a new list with their respective team number.
X=[1,3,4,4]
What I'm trying to do is creating a list, adding elements one at a time.
check([H|T]) :-
team(H,R),
append([R],_, X),
check(T).
Could someone help me complete this?
You need to find all the team numbers for which the name of the team is a member of the list of team names that you are interested in:
?- findall(Number, (
team(Name, Number),
member(Name, [milan, lazio, roma, inter])), Numbers).
Numbers = [1, 3, 4, 4].
To return the numbers in a given order, just apply member/2 before team/2, in this case member/2 generates names (in the given order), and team/2 maps them to numbers:
?- findall(Number, (
member(Name, [lazio, milan, inter]),
team(Name, Number)), Numbers).
Numbers = [3, 1, 4].
A lot of time since I used Prolog but an answer -more or less- would look like:
check([]) :- true.
check([X]) :- team(X,_).
check([X,Y]) :- team(X,N), team(Y,M), N < M.
check([X,Y|T]) :- check(X,Y), check([Y|T]).
See this question for a very similar problem.
From what you say you might be better off making a list and then sorting it. That way you'd know the list is in order. Of course it's tricky in that you are sorting on the team ranks, not the alphabetic order of their names.
But the question you asked is how to check the list is in sorted order, so let's do it.
check([ ]). % just in case an empty list is supplied
check([_]). % singleton lists are also in sort order
check([H1,H2|T]) :-
team(H1,R1),
team(H2,R2),
R1 <= R2,
check([H2|T]).
Note that the recursion reduces lists with at least two items by one, so the usual termination case will be getting down to a list of length one. That's the only tricky part of this check.
Added in response to comment/question edit:
Sure, it's good to learn a variety of simple "design patterns" when you are getting going with Prolog. In this case we want to "apply" a function to each item of a list and build a new list that contains the images.
mapTeamRank([ ],[ ]). % image of empty list is empty
mapTeamRank([H|T],[R|S]) :-
team(H,R),
mapTeamRank(T,S).
So now you have a predicate that will turn a list of teams LT into the corresponding list of ranks LR, and you can "check" this for sorted order by calling msort(LR,LR):
check(LT) :-
mapTeamRank(LT,LR),
msort(LR,LR).