• Aucun résultat trouvé

Recursive Predicate Definitions

Dans le document Prolog and Natural-Language Analysis D (Page 29-33)

This digital edition of Prolog and Natural-Language Analysis is distributed at no charge for noncommercial use by Microtome Publishing.

19

The following trace of the “writings of Russell” example may elucidate the Prolog trace facility. Note especially the changing instantiation of the variables during the trace. The Prolog tracing facility is invoked with the literal “trace”.

?- trace.

Debug mode switched on.

yes

?- author_of(bertrand, What).

(1) 0 Call : author_of(bertrand,What) (2) 1 Call : book(What)

(2) 1 Exit : book(begriffsschrift)

(3) 1 Call : wrote(bertrand,begriffsschrift) (3) 1 Fail : wrote(bertrand,begriffsschrift) (2) 1 Redo : book(begriffsschrift)

(2) 1 Exit : book(principia)

(4) 1 Call : wrote(bertrand,principia) (4) 1 Exit : wrote(bertrand,principia) (1) 0 Exit : author_of(bertrand,principia) What = principia

yes

Note that the exit lines leading to the final proof contain the same information as a proof tree for the goal.

Not only does the trace make explicit the ordering in which the proof tree was tra-versed by Prolog, it also shows all the blind alleys that Prolog tried before finding an actual proof. These two phenomena are related. For example, if the second branch of the proof tree (corresponding to the second literal in the clause definingauthor_of) had been tried first, the only satisfying assignment for it would have beenBook = principia. Under this assignment, the first clause becomes book(principia), which is immediately proved from the database. Thus no blind alleys are tried. This behavior would be engendered by the following alternative definition ofauthor_of:

author_of(Person, Book) :-wrote(Person, Book), book(Book).

This example shows that although the ordering of literals within a clause does not affect the logical meaning of the clause as a definition of a relation, it can have far-reaching effects in terms of the control flow of the program. That is, although Prolog can be viewed as a subset of a logical language, we cannot forget that it is still a programming language, and issues of control are still important.

2.5 Recursive Predicate Definitions

The relations discussed above—author, logician, and so forth—are defined di-rectly in terms of other relations, which ultimately are defined in terms of the original database. However, it is not possible to give such definitions for relations that involve

chains of relationships of arbitrary lengths. To define such relations, we need to use recursive definitions in which a predicate is defined (possibly indirectly) in terms of itself.

As a simple illustration of the need for recursive definitions, consider the following database, which encodes a portion of the family tree of Bertrand Russell.

parent(katherine, bertrand). parent(amberley, bertrand).

parent(katherine, frank). parent(amberley, frank).

parent(katherine, rachel). parent(amberley, rachel).

parent(dora, kate). parent(bertrand, kate).

parent(dora, john). parent(bertrand, john).

parent(peter, conrad). parent(bertrand, conrad).

female(katherine). male(amberley).

female(rachel). male(frank).

female(dora). male(bertrand).

female(peter). male(conrad).

female(kate). male(john).

Here a literalparent(X,Y)is intended to mean thatXis a parent ofY. The infor-mation in this database is conveniently factored among theparent,male, andfemale predicates so that there is no duplication as there would be if the same information were expressed in terms of, for instance,father,mother,maleandfemale.

Exercise 2.4 Write Prolog clauses definingfather,grandmother,uncle,cousin, etc., in terms of the primitivesparent,male, andfemale.

Suppose we wanted to define a notion of ancestor. Intuitively, a personOldis an ancestor of a personYoungif there is some chain of parent relationships of arbitrary length connectingOldtoYoung. We could start by writing clauses like:

ancestor(Old, Young) :-parent(Old, Young).

ancestor(Old, Young) :-parent(Old, Middle), parent(Middle, Young).

ancestor(Old, Young) :-parent(Old, Middle), parent(Middle, Middle2), parent(Middle2, Young).

...

Clearly, no finite axiomatization in this style is possible. Instead, we defineancestor recursively. At the base, one’s closest ancestors are parents. All other ancestors are parents of closer ancestors. Stating this in Prolog, we have

Program 2.2

ancestor(Old,Young) :-parent(Old,Young).

2.5. Recursive Predicate Definitions

This digital edition of Prolog and Natural-Language Analysis is distributed at no charge for noncommercial use by Microtome Publishing.

21

ancestor(Old,Young) :-parent(Old,Middle), ancestor(Middle,Young).

The execution of the queryancestor(katherine, kate), under this definition of ancestor, proceeds as follows:

?- ancestor(katherine, kate).

(1) 0 Call: ancestor(katherine, kate) (2) 1 Call: parent(katherine, kate) (2) 1 Fail: parent(katherine, kate) (3) 1 Call: parent(katherine, Middle_3) (3) 1 Exit: parent(katherine, bertrand) (4) 1 Call: ancestor(bertrand, kate) (5) 2 Call: parent(bertrand, kate) (5) 2 Exit: parent(bertrand, kate) (4) 1 Exit: ancestor(bertrand, kate) (1) 0 Exit: ancestor(katherine, kate) yes

The reader should confirm that this definition of ancestor works appropriately by fol-lowing the trace and executing similar queries.

Exercise 2.5 What is the proof tree corresponding to this execution?

The reader with some knowledge of model theory for first-order logic might be wondering about our use of recursive predicate definitions like the one above. In gen-eral, the transitive closure of a binary relation is not first-order definable (Boolos and Jeffrey, 1980). That is, given a binary predicate p there is no first-order formula T (x,y) with free variables x and y such that for all interpretations of predicate symbols, con-stants and free variables, T (x,y) holds in the interpretation if and only if the values for x and y in the interpretation are in the transitive closure of the relation interpreting the predicate p. Thus, the above definition, and others like it used in the rest of this book, seem not to really define what they are supposed to define. The solution of this co-nundrum is that definite-clause programs must be interpreted with a specific model in mind, the least Herbrand model (van Emden and Kowalski, 1976; Lloyd, 1984) for the program, rather than in terms of arbitrary first-order models. In the intended model, a program like the above indeed defines the transitive closure of the base relation.

2.5.1 Variable Renaming

In previous examples we have ignored the issue of the scope of variable names. We have been implicitly assuming that several occurrences of variables with the same spelling all occurring in one clause are to be considered instances of the same variable.

Thus, in the first clause of Program 2.2, the two occurrences ofYoungare intended to notate the same variable. When one is bound in an assignment, they both are. However, these two occurrences and the two in the second clause are not intended to notate the same variable. For instance, in the trace above, each of these rules is used once in the proof, the first under the assignmentOld = bertrand, Young = kateand the

second under the assignmentOld = katherine, Middle = bertrand, Young = kate. These two assignments are incompatible, assigning different values toOld. Yet their use in the execution of the query is not inconsistent because they arose from different invocations of clauses. Thus we need some way of distinguishing variables in different clauses—or different invocations of the same clause—that happen to be spelled the same.

One way of doing so would be to require that the programmer always use different variables in each clause. But not only would this be cumbersome, it would not solve the problem for different invocations for the same clause, which recursive definitions make possible. Therefore, each invocation of a given clause in a proof conceptually requires the renaming of the variables in the clause to new variables. In this book, we will represent the variables in a clause invocation resulting from renaming by x_i where x is the textual name of the original variable and i is the number of the invocation. For instance, in the execution trace above, the third clause invocation has a variable that is an instance of the variableMiddlefrom the second clause of Program 2.2. It is therefore listed asMiddle_3in the trace. Thus variables from different invocations are guaranteed to be unique.

In practice, Prolog systems use less obvious (but more efficient) variable-renaming mechanisms. Typically, new variables are internally represented as an index into Pro-log’s working storage, and are displayed with the notation “_i” where i encodes the index.

2.5.2 Termination

Inancestorwe find our first example of a predicate whose definition has to be care-fully designed to avoid nontermination. The idea of the definition ofancestorgiven above is that in the recursive second clause the proof procedure will have to follow a specificparentlink in the family tree or graph before recurring to follow other links.

As the family graph is finite and acyclic, at some point we will run out ofparentlinks to explore and the procedure will terminate.

In contrast, the following definition is possibly more natural but causes nontermi-nation problems for the Prolog interpreter.

Program 2.3

ancestor(Old,Young) :-ancestor(Old,Middle), ancestor(Middle,Young).

ancestor(Old,Young) :-parent(Old,Young).

The definition can be read “an ancestor is a parent or an ancestor of an ancestor”

and includes directly an instance of the transitivity axiom schema which would be expressed in FOL as

R(x,y)R(y,z)R(x,z) .

However, when Prolog tries to proveancestor(x,z)for any terms x and z, it falls into an infinite loop, because the first subgoal it attempts to prove isancestor(x,Y_1), which in turn leads to an attempt to proveancestor(x,Y_2)and so on.

If the two clauses are interchanged, we have

Dans le document Prolog and Natural-Language Analysis D (Page 29-33)