开发者

prolog problem; displaying same rank universities

Sorry for the blunt title, but couldn't really generalize the question. Hope someone with Prolog experience can help me out here. So I've got a database which basically lists universities and their rank, i.e: oxford(1), warwick(2), etc. The que开发者_如何学Pythonstion requires me to write a rule that returns all the names of the universities that have the same rank. Thanks in advance.


I believe this is going to require a bit of meta-programming, but only a little bit. You are probably going to have to provide some feedback about my assumptions in this answer in order to get a robust solution. But I think jumping in will get you there faster (with both of us learning something along the way) than asking a sequence of clarifying comments.

Our immediate goal will be to find these "university" facts through what SWI-Prolog calls "Examining the program" (links below, but you could search for it as a section of the Manual). If we can do this, we can query those facts to get a particular rank, thus obtaining all universities of the same rank.

From what you've said, there are a number of "facts" of the form "UNIVERSITY(RANK)." Typically if you consult a file containing these from SWI-Prolog, they will be dynamic predicates and (unless you've done something explicit to avoid it) added to the [user] module. Such a database is often called a "factbase". Facts here mean clauses with only a head (no body); dynamic predicates can in general have clauses with or without bodies.

SWI-Prolog has three different database mechanisms. The one we are discussing is the clause database that is manipulated through not only consulting but also by the assert/retract meta-predicates. We will refer to these as the "dynamic" predicates.

Here's a modification of a snippet of code that Jan Wielemaker provides for generating (through backtracking) all the built-in predicates, now repurposed to generate the dynamic predicates:

    generate_dynamic(Name/Arity) :-  
        predicate_property(user:Head, dynamic),  
        functor(Head, Name, Arity).  % get arity: number of args  

In your case you are only interested in certain dynamic predicates, so this may return too much in the way of results. One way to narrow things down is by setting Arity = 1, since your university facts only consist of predicates with a single argument.

Another way to narrow things down is by the absence of a body. If this check is needed, we can incorporate a call to clause/2 (documented on the same page linked above). If we have a "fact" (clause without a body), then the resulting call to clause/2 returns the second argument (Body) set to the atom true.

As a final note, Jan's website uses SWI-Prolog to deliver its pages, but the resulting links don't always cut-and-paste well. If the link I gave above doesn't work for you, then you can either navigate to Sec. 4.14 of the Manual yourself, or try this link to a mirrored copy of the documentation that appears not-quite current (cf. difference in section numbering and absence of Jan's code snippet).

And feel free to ask questions if I've said something that needs clarification or assumed something that doesn't apply to your setup.

Added: Let's finish the answer by showing how to query a list of universities, whether given as such or derived from the "factbase" as outlined above. Then we have a few comments about design and learning at the end.

Suppose LU = [oxford,warwick,...] is in hand, a list of all possible universities. Apart from efficiency, we may not even care if a few things that are not universities or are not ranked are on the list, depending on the nature of the query you want to do.

listUniversityRank(LU,V,R) :-  % LU: list of universities  
    member(V,LU),  
    call(V(R)).  

The above snippet defines a predicate listUniversityRank/2 that we would provide a list of universities, and which would in turn call a dynamically generated goal on each member of the list to find its rank. Such a predicate can be used in several ways to accomplish your objective of finding "all the names of the universities that have the same rank."

For instance, we might want to ask for a specific rank R=1 what universities share that rank. Calling listUniversityRank(LU,V,R) with R bound to 1 would accomplish that, at least in the sense that it would backtrack through all such university names. If you wanted to gather these names into a list, then you could use findall/3.

For that matter you might want to begin listing "all the names of the universities that have the same rank" by making a list of all possible ranks, using setof/3 to collect the solutions for R in listUniversityRank(LU,_,R). setof is similar to findall but sorts the results and eliminates duplicates.

Now let's look back and think about how hard we are working to accomplish the stated aim, and what might be a design that makes life easier for that purpose. We want a list of university names with a certain property (all have the same rank). It would have been easier if we had the list of university names to start with. As Little Bobby Tables points out in one of the Comments on the Question, we have a tough time telling what is and isn't a university if there are facts like foo(3) in our program.

But something more is going on here. Using the university names to create the "facts", a different predicate for each different university, obscures the relationship university vs. rank that we would like to query. If we only had it to do over again, surely we'd rather represent this relationship with a single two-argument predicate, say universityRank/2 that directly connects each university name and the corresponding rank. Fewer predicates, better design (because more easily queried, without fancy meta-programming).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜