How to find someone who only published one book?

Viewed 223

Hello I am using ECliPSe Prolog to do some homework and was have a problem with one of my questions. I want to find people that have only published one book using the below Prolog program that I have created.

hasBook(markham_library,dave,"Artifical Intelligence: A Modern Approach",1).
hasBook(indigo,levesque,"the two",2).
hasBook(union_library,dave,"the three",3).
hasBook(somewhere_library,bob,"Thinking as Computation",4).
hasBook(amazon,robert,"the five",5).
hasBook(ajax_library,daniel ,"the six",6).
hasBook(markham_library,evan,"Computational Intelligence",7).
hasBook(stouffvile_library,john ,"the eight",8).
hasBook(ajax_library,sam,"the nine",9).
hasBook(kitchner_library,david,"the ten",10).
hasBook(amazon,chad,"the eleven",105).
hasBook(amazon,chad, "the twelve", 107).
hasBook(amazon,chad, "the thirteen",10).
hasBook(amazon,chad, "the fourteen", 20).
hasBook(amazon,jkrowling,"harrypotter",10).
hasBook(markham_library,jkrowling,"harrypotter",5).

lives(brad,markham).
lives(joyce,stouffville).
lives(opal,union).
lives(delia,ajax).
lives(verna,ville).
lives(sean,ajax).
lives(william,kitchner).
lives(casey,ajax).
lives(courtney,markham).
lives(garrett,stouffville).
lives(chad,newyork).

shipping(markham_library, union, 1).
shipping(stouffville_library, toronto, 2).
shipping(markham_library, stouffville, 3).
shipping(stouffville_library, stouffville, 4).
shipping(markham_library, markham, 5).
shipping(stouffville_library, ajax, 6).
shipping(markham_library, kitchner, 7).
shipping(stouffville_library, kitchner, 11).
shipping(union_library, markham, 9).
shipping(union_library, stouffville, 2).
shipping(amazon, stouffville, 5).
shipping(amazon, markham, 17).
shipping(amazon, toronto, 20).
shipping(markham_library, toronto, 5).

I do not understand why the query hasBook(V,W,X,Y), not hasBook(L,W,N,M). does not return a result. Instead it returns false. Can someone please explain.

2 Answers

Not sure if this is the solution you are supposed to come up with but you can use the setof/3 predicate to group all solutions by author and pick only those where the result list has one element:

?- Xs = [_], setof(Book, Lib^N^hasBook(Lib,Name,Book,N), Xs).
Xs = ["Thinking as Computation"],
Name = bob ;
Xs = ["the six"],
Name = daniel ;
Xs = ["the ten"],
Name = david ;
Xs = ["Computational Intelligence"],
Name = evan ;
Xs = ["the eight"],
Name = john ;
Xs = ["the two"],
Name = levesque ;
Xs = ["the five"],
Name = robert ;
Xs = ["the nine"],
Name = sam.

The pattern Lib^N^ binds the two variables in hasBook(Lib,Name,Book,N) such that if the same book appears in different libraries, it will not give a separate result. Using setof/3 instead of bagof/3 makes sure that only one result per Name/Book pair is returned.

Edit: sorry, I forgot to explain why your solution does not work. Let me rewrite the old style not to \+ and hide all the singleton variables in your query:

?- hasBook(_,W,B1,_), \+ hasBook(_,W,B2,_).
false.

You're looking for an assignment that finds a book B1 by W but then you assert that W has not written any books which can not be true because we have already found B1. As @Ruzihm pointed out, you are missing the information that the second query is not about B1. Their solution uses \= but this only works after the second query has sufficiently instantiated B2 to compare it to B1. An alternative is to use dif/2:

?- dif(B1,B2), hasBook(_,W,B1,_), \+ hasBook(_,W,B2,_).

The difference is that dif/2 introduces a constraint that B1 and B2 are different. As long as one of them is still a variable, this can not be decided yet. Your database has only ground facts where the problem does not arise. In general, using dif/2 is less problematic though.

You need to indicate that the books, N and X need to be different in order to be thrown out.

hasBook(_,X,A,_), not(( hasBook(_,X,B,_), B\=A) ) will throw away any results with the same author and a different book, and not care about the 1st or the 4th arguments.

Confirm at https://swish.swi-prolog.org/p/fkukVFSw.swinb

Related