This is the knowledge base I am working with:
localLib('AHorowitz','Stormbreaker',2).
localLib('AHorowitz','Scorpia',4).
localLib('AHorowitz','Ark Angel',6).
The key for the knowledge base is as follows:
localLib(W,B,C) where
W=Writer
B=Book
C=Acknowledgements
I would like to write a rule that adds up all the acknowledgements of the writer.
This is the code I have written so far:
getAcknowledgement(W,X):- findall(C,localLib(W,_,C),X).
This rule helps me list all the acknowledgements in separate list e.g.
?- getAcknowledgement('AHorowitz',X).
X = [2, 4, 6]
I am now getting stuck on how to add these items. I know of the sum_list built in and though I know it is not correct the thing I want to achieve is this:
getAcknowledgement(W,X):- findall(C,localLib(W,_,C),X).
sum_list(X,[getAcknowledgement]).
/* I would like to sum the output that I receive from the first rule above.
The KB has been simplified in this example to 3 clauses however in reality
there are 1000.*/
How would I go about doing this, any help would be great?
It sounds like you want to find the count of acknowledgements by writer.
bagof/3 is your friend here. It
bagof(+Template, :Goal, -Bag)
Unify Bag with the alternatives of Template. If Goal has free variables besides the one
sharing with Template, bagof/3 will backtrack over the alternatives of these free
variables, unifying Bag with the corresponding alternatives of Template. The construct
+Var^Goal tells bagof/3 not to bind Var in Goal. bagof/3 fails if Goal has no
solutions.
findall/3 is
equivalent to bagof/3 with all free variables bound with the existential operator (^),
except that bagof/3 fails when Goal has no solutions.
So...this should get you the summed count of knowledgements for a given writer, or, if Writer is unbound, on backtracking, it will find the solutions for all writers, one at a time.
acknowledgements_by_writer( Writer , Acknowledgements ) :-
bagof( N , local_lib(Writer,_,N) , Ns ) ,
sum_list(Ns,Acknowledgments).
If you want the overall count of acknowledgements, something like this ought to do you
total_acknowledgements(T) :-
findall(N,local_lib(,,N),Ns),
sum_list(Ns,T).
Related
Trying to implement following predicate, which takes on input
list of lists - one list = one tested graph T (as edges)
graph G itself (as edges)
and tests whether T includes all of the vertices of G. If it does(true) should return it. Basically it's list filtering.
What I have so far is:
covb([],G).
covb([H|R],G) :-
isItCov(G,H), covb(R,G).
isItCov([],H).
isItCov([V-W|R],H) :-
vertex(V,H),
vertex(W,H),
isItCov(R,H).
vertex(V,[V-_|G]).
vertex(V,[_-V|G]).
vertex(V,[_|G]):- vertex(V,G).
For covb([[a-c,c-b,c-d]],[a-b,a-c,a-d,c-d,b-c]) works fine(true). For covb([[a-c]],[a-b,a-c,a-d,c-d,b-c]) works fine too (false). I got an issue while I call it with multiple lists like covb([[a-c,c-b,c-d],[a-c]],[a-b,a-c,a-d,c-d,b-c]). which should work only for the first one.
I actually got two questions -
Why does it work for one list only?
I want to return items of lists of lists which passed the condition and returned true (that's the filtering part). How should I do that?
First of all, your program has a number of singleton variable warnings. Do not ignore singleton variable warnings. They can hide real bugs. Also, since more experienced Prolog users here know that programs with singleton variable warnings are not even worth running, they will (a) just see the warnings and decide that they are no longer interested in trying to help you, or (b) fix the warnings on their side, but then by definition they will be working on a program that is no longer the program you posted!
Now for your questions.
Why does it work for one list only?
It's really not clear what you are asking here, or just above with "covb([[a-c,c-b,c-d],[a-c]],[a-b,a-c,a-d,c-d,b-c]). which should work only for the first one."
This query does fail:
?- covb([[a-c,c-b,c-d],[a-c]],[a-b,a-c,a-d,c-d,b-c]).
false.
This comes down to testing each of the two lists:
?- isItCov([a-b,a-c,a-d,c-d,b-c], [a-c,c-b,c-d]).
true .
?- isItCov([a-b,a-c,a-d,c-d,b-c], [a-c]).
false.
The first list does cover the graph, while the second doesn't. Overall, your definition of covb/2 is written to succeed if all lists cover the graph. This is not the case, so your covb/2 query fails.
Was this what you wanted to know?
I want to return items of lists of lists which passed the condition and returned true (that's the filtering part). How should I do that?
You could see if your Prolog's documentation has something for the word "filter". On SWI-Prolog you can do this:
?- apropos(filter).
true.
This will point you to the include/3 predicate, which seems to do what you want:
?- include(isItCov([a-b,a-c,a-d,c-d,b-c]), [[a-c,c-b,c-d],[a-c]], Covers).
Covers = [[a-c, c-b, c-d]].
If you want to write a filter predicate for your concrete application, it might look something like this:
graph_covers(_Graph, [], []).
graph_covers(Graph, [Nodes|NodesRest], Covers) :-
( isItCov(Graph, Nodes)
-> Covers = [Nodes|CoversRest]
; Covers = CoversRest ),
graph_covers(Graph, NodesRest, CoversRest).
This is similar to your predicate, it just adds an extra argument to collect those node lists for which isItCov/2 succeeded. If it did not succeed, it continues with a list not containing that current node list.
?- graph_covers([a-b,a-c,a-d,c-d,b-c], [[a-c,c-b,c-d],[a-c]], Covers).
Covers = [[a-c, c-b, c-d]] ;
false.
I'm new to Prolog. These are my facts.
part_cost(Part, Supplier)
part_cost(top_tube ,cinelli).
part_cost(top_tube ,columbus).
part_cost(down_tube ,columbus).
part_cost(head_tube ,cinelli).
part_cost(head_tube ,columbus).
part_cost(seat_mast ,cinelli).
part_cost(seat_mast ,columbus).
I want to find a supplier who supplies all kinds of parts, which is actually columbus.
I don't know how to say "all" in Prolog language.
Any help is appreciated.
Update
Thank you, #Ankur and #Sergey Dymchenko. Your list approach inspired me! I can write rules:
supplyAllParts(Supplier, []):-true.
supplyAllParts(Supplier, [Part|PartRest]) :-
part_cost(Part, Supplier, _, _),
supplyAllParts(Supplier, PartRest).
and call it by
?- supplyAllParts(S,[top_tube, down_tube, head_tube, seat_mast]).
S = columbus.
Now can Prolog dynamically find the part list ([top_tube, down_tube, head_tube, seat_mast]) from the facts rather than I manually provide it?
supplies([],_).
supplies([H|T],S) :- part_cost(H,S), supplies(T,S).
| ?- setof(P,S^part_cost(P,S),R), supplies(R,Supplier).
R = [down_tube,head_tube,seat_mast,top_tube]
Supplier = columbus
First, you probably need to define a notion of "all parts" manually, because maybe there is some kind of "bottom_super_gravitsapa", that no one has:
% store in sorted order, to compare with setof results later
sort([top_tube, down_tube, head_tube, seat_mast], AllParts)
To get a list of all parts for every supplier we can use setof/3:
?- setof(Part, part_cost(Part, Supplier), Parts).
setof(Part, part_cost(Part, Supplier), Parts).
Supplier = cinelli
Parts = [head_tube,seat_mast,top_tube] ?;
Supplier = columbus
Parts = [down_tube,head_tube,seat_mast,top_tube]
yes
And now just add a condition that list of parts for a specific supplier and AllParts are the same:
Parts == AllParts
If you don't want to define AllParts manually and assume that there is a supplier for every possible part, you can use another setof to get AllParts from the facts before the main setof.
all is a big word, in any language, and what it means varies so much... Down to the earth , in Prolog we have findall/3 family, that outputs a list with all patterns we instruct to extract from succeeded queries. The output list then is further processed ... But for your case, library(aggregate) is more handy:
supplies_all(Supplier) :-
aggregate(max(N,S), count_part_cost(S,N), max(_, Supplier)).
count_part_cost(S,N) :-
aggregate(count, P^part_cost(P,S), N).
I used a service predicate count_part_cost/2, just to keep the main one clear...
If you try to 'inline' them, beware to variables usage. Study variables quantification until you are comfortable with it.
edit As noted by Sergey, my code is not correct. Here is my bet for a more appropriate definition:
supplies_all(Supplier) :-
setof(P, S^part_cost(P,S), Ps),
setof(P, part_cost(P,Supplier), Ps).
I think it highlights well what I intended above recommending about studying variable quantification...
edit another possible definition, less costly but with redundant multiple solution (well, we have setof for that...)
supplies_all(Supplier) :-
% peek any Supplier from catalog
part_cost(_,Supplier),
% check it has all parts available
forall(part_cost(P,_), part_cost(P,Supplier)).
I am very new to Prolog and trying to learn.
For my program, I would like to have the user provide pairs of strings which are "types of".
For example, user provides at command line the strings "john" and "man". These atoms would be made to be equal, i.e. john(man).
At next prompt, then user provides "man" and "tall", again program asserts these are valid, man(tall).
Then the user could query the program and ask "Is john tall?". Or in Prolog: john(tall) becomes true by transitive property.
I have been able to parse the strings from the user's input and assign them to variables Subject and Object.
I tried a clause (where Subject and Object are different strings):
attribute(Subject, Object) :-
assert(term_to_atom(_ , Subject),
term_to_atom(_ , Object)).
I want to assert the facts that Subject and Object are valid pair. If the user asserts it, then they belong to together. How do I force this equality of the pairs?
What's the best way to go about this?
Questions of this sort have been asked a lot recently (I guess your professors all share notes or something) so a browse through recent history might have been productive for you. This one comes to mind, for instance.
Your code is pretty wide of the mark. This is what you're trying to do:
attribute(Subject, Object) :-
Fact =.. [Object, Subject],
assertz(Fact).
Using it works like this:
?- attribute(man, tall).
true.
?- tall(X).
X = man.
So, here's what you should notice about this code:
We're using =../2, the "univ" operator, to build structures from lists. This is the only way to create a fact from some atoms.
I've swapped subject and object, because doing it the other way is almost certainly not what you want.
The predicate you want is assertz/1 or asserta/1, not assert/2. The a and z on the end just tells Prolog whether you want the fact at the beginning or end of the database.
Based on looking at your code, I think you have a lot of baggage you need to shed to become productive with Prolog.
Prolog predicates do not return values. So assert(term_to_atom(... wasn't even on the right track, because you seemed to think that term_to_atom would "return" a value and it would get substituted into the assert call like in a functional or imperative language. Prolog just plain works completely differently from that.
I'm not sure why you have an empty variable in your term_to_atom predicates. I think you did that to satisfy the predicate's arity, but this predicate is pretty useless unless you have one ground term and one variable.
There is an assert/2, but it doesn't do what you want. It should be clear why assert normally only takes one argument.
Prolog facts should look like property(subject...). It is not easy to construct facts and then query them, which is what you'd have to do using man(tall). What you want to say is that there is a property, being tall, and man satisfies it.
I would strongly recommend you back up and go through some basic Prolog tutorials at this point. If you try to press forward you're only going to get more lost.
Edit: In response to your comment, I'm not sure how general you want to go. In the basic case where you're dealing with a 4-item list with [is,a] in the middle, this is sufficient:
build_fact([Subject,is,a,Object], is_a(Subject, Object)).
If you want to isolate the first and last and create the fact, you have to use univ again:
build_fact([Subject|Rest], Fact) :-
append(PredicateAtoms, [Object], Rest),
atomic_list_concat(PredicateAtoms, '_', Predicate),
Fact =.. [Predicate, Subject, Object].
Not sure if you want to live with the articles ("a", "the") that will wind up on the end though:
?- build_fact([john,could,be,a,man], Fact).
Fact = could_be_a(john, man)
Don't do variable fact heads. Prolog works best when the set of term names is fixed. Instead, make a generic place for storing properties using predefined, static term name, e.g.:
is_a(john, man).
property(man, tall).
property(john, thin).
(think SQL tables in a normal form). Then you can use simple assertz/1 to update the database:
add_property(X, Y) :- assertz(property(X, Y)).
I have a predicate "lookupOptions" which returns one by one some lists (Menus).
I'm trying to get it to satisfy the case of multiple inputs. I can return a single set of options as follows, by reading the head of the "list_places" list.
find_options(Restaurant,Town,Menu) :- lookupOptions(Restaurant,H,Menu), list_places(Town,[H|T])
But, I'm not able to get it to iterate.
I have tried a lot of things, these were my best efforts so far.
a) standard enough iteration, but it wont resolve ...
doStuff(X,[],_).
doStuff(Restaurant,[H|T],_):- lookupOptions(Resturant,H,_), doStuff(Restaurant,T,_).
find_options(Restaurant,Town,Menu) :- doStuff(Restaurant,[H|T],Menu), list_places(Town,[H|T]).
b) expanding the goal predicate ...
find_options(_,Town,[H|T],_)
find_options(Restaurant,Town,Menu) :- find_options(Restaurant,Town,[],Menu).
find_options(Restaurant,Town,X,Menu) :- list_places(Town,X).
find_options(Restaurant,Town,[H|T],Menu) :- lookupOptions(Restaurant,[H],Menu), find_options(Restaurant,Town,T,Menu).
Would either of these work ? if the pattern was written correctly. Or if there was an appropriate cut put in place?
Any help most appreciated ...
It's no clear on what you want iterate. Prolog uses backtracking to examine all alternatives, then you should start backtracking if you are after some alternative, or use the all solutions family.
Now I think you want simply declare there could be more find_options(Restaurant,Town,Menu). Then try replacing the head match [H|T] with this:
find_options(Restaurant,Town,Menu) :-
lookupOptions(Restaurant,H,Menu),
list_places(Town, Places),
member(H, Places).
BTW T is a singleton in your original rule. This could be a hint for the need of generalize it.
I know on how to display a list by using loop.
For example,
choice(a):-write('This is the top 15 countries list:'),nl,
loop(X).
loop(X):-country(X),write(X),nl,fail.
Unfortunately, I don't know on how to display list by using list. Anyone can guide me?
it's not very clear what it is that you're trying to achieve.
I'm not sure from your description whether you have quite got to grips with the declarative style of Prolog. When you wrote your rule for loop you were providing a set of conditions under which Prolog would match the rule. This is different from a set of procedural instructions.
If you want to collect all the countries into a list you can use the setof rule like follows
top_countries(Cs):-
setof(C, country(C), Cs).
This will return a list [] of the countries matched by the rule.
If you wanted to output each element of this list on a new line you could do something like the following recursive function.
write_list([]).
write_list([H|T]):-
write(H),nl,
write_list(T).
The first rule matches the base case; this is when there are no elements left in the list. At this point we should match and stop. The second rule matches (unifies) the head of the list and writes it to screen with a newline after it. The final line unifies the tail (remainder) of the list against the write_list function again.
You could then string them together with something like the following
choice(a):-
write('This is the top 15 countries list:'),nl,
top_countries(X),
write_list(X).
Things to note
Try not to have singleton variables such as the X in your choice rule. Variables are there to unify (match) against something.
Look into good declarative programming style. When you use functions like write it can be misleading and tempting to treat Prolog in a procedural manner but this will just cause you problems.
Hope this helps
write/1 doesn't only write strings, it writes any Prolog term. So, though Oli has given a prettier write_list, the following would do the job:
choice(Countries):-write('This is the top 15 countries list:'),nl,write(Countries).