Sie sind auf Seite 1von 19

Comput. Lang. Vol. 18, No. 1, pp. 57-75, 1993 0096-0551/93 $5.00 + 0.

00
Printed in Great Britain. All rights reserved Copyright © 1992 Pergamon Press Ltd

A L T E R I N G A N D APPLYING PREDICATES

H. JUSTIN COVEN~"
Computer Science Department, Arizona State University, Tempe, AZ 85287-5406, U.S.A.

(Received 7 March 1991)

Abstract--A set of tools in Prolog for manipulating predicates as first-class objects is described. These
tools are higher-level tools than assert, retract and querying. The first group of these tools are analogs
to the Lisp tools apply, function, lambda, and programming cliches such as mapcar. The second group
of tools is for editing and manipulating predicates. Prolog provides a simpler structure for predicates than
Lisp has for functions and thus a small set of tools for adding to, retrieving from, removingfrom, replacing
to, and exchanging within predicates can be provided for Prolog predicates but not for Lisp functions.

Prolog First-class predicates Lisp Programming cliche Applying predicates

1. I N T R O D U C T I O N

In this article we will develop a set of tools in Prolog for altering and applying Prolog predicates.
The uses of these tools will be at least 3-fold: (1) they will be used as a programming cliche [1]
that can apply a function to an entire list, one element at a time; (2) they will be used to construct
unnamed predicates as the program is running and then call or apply those predicates; and (3) they
will be used as a set of editing tools that can manipulate predicates in the database without loading
or unloading a file into the database. (1) and (2) Give Prolog m a n y of the powerful program
manipulating language features that languages like Lisp have; (3) goes beyond what Lisp has by
providing a set of tools for manipulating predicates (Lisp's parallel is functions) without having
to deal with the details of clause manipulation. Prolog provides a simpler structure of predicates
than Lisp has of functions and thus a relatively small set of tools can be created for the abstract
manipulation of predicate structure while it is difficult to do the same for Lisp.
For any language to mature it needs to develop a strong set of environmental tools. For most
languages these tools are usually pragmatic and operational in nature. Because Prolog has a close
relationship to logic many language developers have tried to remain as close to a logical foundation
as possible. Due to this, tools unrelated to logic have been shied away from and only been included
when absolutely necessary.
One of our fundamental philosophical beliefs in the development of language tools is that
any mathematical system such as logic or functions is an abstraction for doing computation.
These abstractions have some properties that make the computation more tractable for the human
mind.
Our goal in developing language tools is in contrast to Ref. [2] where the goal was to determine
if the addition of higher-order tools would give Prolog more computational power than it already
had without those tools. In Ref. [2] Warren was concerned with whether ordinary Prolog could
be used to represent the same computational problem that higher-order tools were used to represent.
This is not our concern; we are concerned with creating programming language tools that make
programming problems more tractable for the h u m a n mind. Analogously we are not concerned
with whether we can do the same problem using assembly language or machine language or a higher
level language, but rather with whether a p r o g r a m m e r can program with the different programming
language tools more easily.
It is c o m m o n knowledge that programmers on average program the same number of lines of
code during any period of time no matter what language they use. Thus it is wise to create language
abstractions that each execute a number of lines of a lower level language. The trick is to create
instructions that execute c o m m o n sets of lines of code. If there are few c o m m o n sets of lines of

fPresent address: Computer Science Department, Bellarmine College, Louisville, KY 40205-0671, U.S.A.

57
58 H. JUSTINCOVES

code, then a few new language tools can be created. We describe such a set of tools for manipulating
a Prolog database.
In Section 2 we develop a Prolog analog to Lisp's "lambda", "apply", "mapcar" and other
programming cliches. In Section 3 we describe how to use these tools to manipulate a program's
self-representation. The set of tools described in Section 4 is a set of environment tools for editing
a Prolog database.

2. APPLY AND P R O G R A M M I N G C L I C H E

In this section we develop a set of programming cliches analogous to those that exist in Common
Lisp [1]. The cliches we will be building up to are the mapcar and associated functions that apply
a given function to an entire list of arguments one list element at a time. In Prolog there is a clear
concept of what a list is, but not well defined concepts of what a function is or how to apply it.
Trying to query a Prolog database is analogous to making a function call, when a query like "?-
likes(john, sally)" is made it is as if the function "likes" is called with the arguments "john" and
"sally". These arguments are paired with the formal parameters " X " and " Y " of a clause in the
database (the called function) such as: "likes (X, Y):- boy(X), girl(Y)". The body of the clause
"boy(X), girl(Y)" can be considered the body of the function. Thus we consider a query to be a
function call and a clause in the database to be the function called.

2.1. Apply
In Lisp when a function is applied there are two arguments: a function defined in some way and
a set of arguments the function is applied to.
If we want to apply a predicate that exists in the database to some given arguments, then we
could query for that predicate with the given arguments. This will unify the arguments with the
formal parameters of the called predicate in the database and then query (execute) the body of the
predicate in the database. Thus we need to make a tool that will make a query of the predicate
attached to the given arguments. We create the predicate "apply" that takes two arguments--the
first argument is a predicate to be applied, the second argument is the list of given arguments the
predicate is to be applied to. If the predicate is a user defined predicate on a system built-in predicate
then the predicate needs to be defined by name only and the definition of apply is as follows:
apply clause 1
apply(Name, Arguments):- Query = . . [Name [Arguments], Query.
The "univ" operator " = . . " is used to create a functor with the name being the first argument
of apply " N a m e " and the arguments being the second argument of apply "Arguments". The
created functor "Query" is then queried (the above code is possible because predicate variables like
"Query" exist in many current Prolog systems such as Cprolog or Quintus Prolog).
With the above "likes" clause in the database along with the clauses: "boy(tom)" and
"girl(sally)", we could make the following query:
?- apply(likes, [tom, sally]).
yes
This example is given to demonstrate the use of "apply". Certainly we could make the equivalent
query "?-likes(tom, sally)". However with the "apply" predicate the predicate being applied (the
first argument of "apply") could be a variable and there could be a whole stream of reasoning
leading up to the apply (the apply could be embedded deep into the body of a program). In the
next subsection we will develop and "apply" mechanism that will go further than just allowing a
variable predicate, it will allow the predicate to be constructed in a program (as will be seen in
Section 3).

2.2. Unnamed predicates


If the function being applied is not a user-defined or system built-in predicate we must develop
a representation for it. This is similar to the unnamed or "lambda" representation of Lisp. In this
case we do not need a predicate name to identify the function we need only the formal parameters
Altering and applying predicates 59

and the body of the function to be applied. We will use the symbol "lambda" to identify an
unnamed predicate. "lambda" Will be the name of a functor that has two arguments, the first being
the formal parameters of an unnamed predicate and the second being the body of an unnamed
predicate. A possible "lambda" functor representation of an unnamed predicate is as follows:
lambda([A,B,C], (C is A + B))
Here it is as if we have an unnamed predicate with three arguments and a body composed of
"C is A + B " . If this were named and sitting in the database it may appear as "un-
named(A, B, C) : - C is A + B". In order for our apply predicate to handle this case we add the
following clause:
apply clause 2
apply(lambda(Arguments, Body), Arguments) : - Body.
Here the formal parameters in the lambda (the first appearance o f " A r g u m e n t s " ) are unified with
the arguments the lambda is being applied to (the second occurrence of "Arguments"). Finally the
body of the lambda is executed (queried).
With only the previously defined "apply" clauses in the database we could use the following
query:
?-- apply(lambda([A,B,C,], (C is A + B)), [1,2,Return]).
A=I,
B=2,
C=3,
Return = 3
yes

2.3. Programming cliches


We can now use the definitions of "apply" and "lambda" to create a suite of programming
cliches. The first predicate we wish to create is "mapcar". In Lisp "mapcar" applies a function to
each element of a list and returns a list of the result of each application of the function to each
element in the list. In Lisp "mapcar" only takes two arguments a list and the function to apply
to each element of the list. In Lisp functions return values, however in Prolog values are not
returned from queries to the database. Due to this the "mapcar" for Prolog needs a third argument
the list of return values. To implement "mapcar" we need to recursively traverse a list applying
a function to each element. We can use the above defined "apply" predicate to do the applying.
mapcar([FirstlRest], Function, [FirstReturnlRestReturn]) : -
apply(Function, [First, FirstReturn]),
map_list(Rest, Function, RestReturn).
mapcar([ ], _, []).
Note that the function being applied has an argument for a return value. It is necessary to supply
a return argument to apply for the same reason that a return list is provided for "mapcar", Prolog
does not return values on queries. If a returned value is needed it must be passed as an argument. In
the earlier uses of apply we did not need returned values, thus we postponed mention of it until now.
As an example use of mapcar we provide below a simplified language translation example. We
provide two dictionary predicates: "eng_chinese dict" which translates form English to Chinese;
and "eng_german_dict" which translates from English to German.
eng_german_dict(i,ich).
eng_german__dict(you,du).
eng_german_dict(like,gefalle).
eng__chinese_dict(i,wo).
eng_chinese dict(you,ni).
eng_chinese_dict(he,ta).
eng_chinese_dict(like,'xi huan').
60 H. JUSTINCOVEN

We can query the mapcar predicate with two different sets of arguments in order to translate
from English to German and then English to Chinese.
? - mapcar([i, like, you], eng_german_dict, Return).
Return = [ich, gefalle, du]
yes
? - mapcar([i, like, you], eng_chinese_dict, Return).
Return = [wo, xi huan, nil]
yes
In Lisp it is possible that the function that is being passed into "mapcar" may take
more than one argument. To handle this Lisp's "mapcar" allows itself to be called with a
variable number of arguments with all additional arguments being lists. Mapcar will
recurse through each list at the same time, taking the first value of each list and applying the
function to the set of first elements from each list. This is possible because within Common
Lisp functions can take a variable number of arguments. In Prolog any given predicate has a
fixed number of arguments. Thus we force "mapcar" to take one argument list and a return
list. This forces every function being passed into "mapcar" to take exactly two arguments:
one passed in argument; and a return value. The only ways around this restriction would be
to either create a definition of mapcar with clauses for every possible number of arguments, or
to add the capability of variable argument predicates to Prolog. The former choice is ungainly
but straightforward, the latter choice being nontrivial and counter to the standard definition of
Prolog.
We can go on to define several other common programming cliches that exist in Lisp.
"remove-if-not" goes through a list applying a function to each element, if the function returns
"nil" ("no" in Prolog) then the element is removed from the list.
remove-if-not([FirstlRest], Function, [Firstl RestReturn]) : -
apply(Function, First), !,
remove-if-not(Rest, Function, RestReturn).
remove-if-not([_lRest], Function, RestReturn) : -
remove-if-not(Rest, Function, RestReturn).
remove-if-not([], _, []).
In Lisp "count-if" takes two arguments a list and a function. It counts the number of elements
in a list that satisfy the function. Here satisfy means that the function is applied to an element in
the list and returns a non-nil value (in Prolog it would be satisfied).
"find-if" Also takes a list and a function as arguments. The first element of the list that
the function satisfies is the returned value. The third arguments of "remove-if-not", "count-if"
and "find-if" are the returned values. The predicates "find-if" and "count-if" appear in the
Appendix.
The choice of returning an empty list [] as the return value when no element of the list satisfies
the function is an arbitrary choice (this is done by making the third argument of the first clause
the empty list []). It would also be appropriate for the predicate to fail in this case (the first clause
could be removed).
Numerous other similar programming cliches can be created depending upon the desires
of the programmer. Common Lisp [3] describes many additional programming cliches to choose
from.

2.4. Multiple clause lambdas


Prolog is different from Lisp in that the definition of predicates unlike a definition of functions
can be distributed. A predicate can be defined by numerous different clauses. How can we define
our "lambda" and "apply" to handle this situation? We choose to have a lambda functor with one
argument. This argument will be a list of lists. Each list within the list will represent one clause.
Each clause will be represented by a two element list. The first element being a list of arguments
of the clause head and the second element being the body of the clause.
Altering and applying predicates 61

The lambda representation for:

likes(X, Y) : - boy(X), girl(Y).


likes(tom, sally).
Would be:
lambda([[[X,Y], (boy(X), girl(Y))],
[[tom, sally], true]])
"true" being the default body when there is no body as in the second clause of "likes". This is
the same convention that is used by the built-in predicate "clause".
To handle this definition of lambda we have to add a couple of clauses to apply:
apply clause 3
apply(lambda([Clause 11-]), Args) : -
Function = . . [lambdalClausel],
apply(Function, Args).

apply clause 4
apply(lambda([_[Rest]), Args) : -
apply(lambda(Rest), Args).

We simply apply different clause one after another recursively until one succeeds. If the program
backtracks, "apply" will appropriately backtrack and retry the rest of the clauses of the "lambda".
As an example we can backtrack on the following apply:
?-- apply(lambda([[[one], true],[[two], true]]), [X]).
X = one;
X = two;
no
Here we have backtracked twice on two retries the first retry succeeding and setting " X " to "two".

3. SELF M O D I F Y I N G P R O G R A M S : M O T I V A T I O N S FOR D E V E L O P I N G THE


P R O P O S E D L A N G U A G E TOOLS

With the new tools "lambda", and "apply", our programs now have the power to create and
modify their own code, because they can create predicates and then run them. Without these tools,
in order to do the same things the programmer would be forced to assert and then call predicates.
Programmers can now avoid the extra conceptual task of writing codes for asserting, determining
names, and managing the database. Additionally the programmer can work with entire predicates
and does not have to deal with asserting and retracting single clauses at a time. This saves the
programmer from conceptualizing about extra programming steps. Programming cliches, by
reducing the conceptual involvement, simplify the task of the programmer.
The ability to write computer programs that manipulate themselves is a powerful tool. There
are many intriguing issues involved with the ability of programs to manipulate themselves. The
tools presented in the previous and next sectiofis are intended as a powerful set of instructions that
will make this task (writing programs that manipulate themselves) simpler. In this section we will
describe a trivial example of a program that manipulates itself and then applies part of itself. ]'his
example is intended to give the flavor of how this task can be done using our tools. We then go
on to discuss, in this section, places where this ability can be useful.

3.1. An example
A program could create a lambda structure from arguments passed in as in "build-lambda"
shown below:
build-lambda(Conj 1, Conj2, Argl, Arg2,
lambda([Argl, Arg2], (Conj 1, Conj2))).
62 H. JUSTINCOVEN

Code could be added to perform tests to determine the proper construction of lambda. Different
conjuncts (here we define "conjuncts" to be the different functors that are conjoined together in
the body of a clause) and arguments could be tested and/or chosen from to create a lambda. All
the tools in Prolog that allow the creation of a functor can be used to create a lambda. The created
lambda could then be applied:
build-apply(Conj 1, Conj2, Argl, Arg2) : -
build-lambda(Conj 1, Conj2, Argl, Arg2, Lambda),
apply(Lambda, [tom, sally]).
We can call this program to construct an unnamed predicate and then apply that unnamed
predicate:
? - build-apply(likes(X, Y), boy(X), X, Y).
Which create the lambda structure:
lambda([X,Y], (likes(X,Y), boy(X))).
And would match the second clause of apply and call:
likes(tom, sally), boy(tom).
Which in the database we have built up would succeed and return:
X = tom,
Y = sally
yes

3.2. Further uses


The tools we are proposing in this paper do more than just give the ability to construct new
predicates, they give the ability to observe and modify existing predicates. With this power
programs can try out different predicates to see which is most appropriate in solving a given
problem. The predicate can be modified in ways to solve the given problem better. This is
considered a form of Machine Learning. A new predicate is created (or learned) that will solve the
given problem. The predicate or method for solving the problem is learned.
We have also been experimenting with the language Lisp to create a set of functions that aid
in the process of modifying functions. In our explorations it seems that the set of tools to do this
is much more straightforward for Prolog than for Lisp. In Lisp functions go to undefined depths,
functions can be lists embedded within lists to any depth. In Prolog there is a flat list of conjuncts
enabling a more straightforward set of program manipulation tools. We will demonstrate a small
set of predicates that help modify Prolog predicates in Section 4.
Human beings reason about how they solve problems, they reason about and modify their own
thought processes. We are developing sets of tools that allow a program to reason about and
modify itself. In this paper we describe tools for manipulating functions, in other experiments we
are developing tools for manipulating control structures, interpreters and other features of a
programming system [4].

4. M O D I F Y I N G P R E D I C A T E S AND E D I T I N G TOOLS

In this section we develop a set of tools that modify predicates in ways that humans think of
modifying predicates. Predicates are treated as sets of clauses, the same way they are treated in
the above described apply predicate. These tools can be viewed as a set of tools for user editing
of the database, as a set of tools for a program to manipulate itself, or as a set of tools for a program
to manipulate unnamed predicates.
Some of the more common ways humans modify things are by adding to them, viewing parts
of them, removing parts, replacing parts, and exchanging parts. We will be creating a set of tools
that do these things to entire predicates, clauses, and conjuncts. The first tool (predicate) that we
describe is "addclause" which as its name implies adds a clause to a specified predicate.
Altering and applying predicates 63

All of the following tools that we propose are only able to work upon dynamic predicates they
cannot work upon static predicates. Consulting and reconsulting files create static predicates which
our tools can not operate upon. However this problem can be solved by using the reconsult and
consult defined in Section 7.13 of the standard Prolog reference text [5]. Additionally there are tools
such as the "save" of Quintus Prolog that allow the p r o g r a m m e r to store the curent database into
a file. Thus the tools defined below can be used to manipulate a storable database.
We will first define all of our tools in relation to named predicates and after we have developed
all of our tools this way we will show how it is simple to extend these to unnamed predicates. The
implementations for unnamed predicates is significantly simpler than for the named predicates.

4.1. Adding clauses


Addclause takes three arguments "addclause(PredName, Position, Lambda)". The first argu-
ment " P r e d N a m e " is the name of the predicate that is supposed to be matched in the database.
The second argument "Position" is the position within the database with respect to the other
clauses of the named predicate that the new clause is to be placed. If Position = 3 then the new
clause is supposed to be placed after the appearance of the first two clause of that predicate in the
database. If there are not two clauses that match the indicated predicate then a warning should
be returned with no change to the database occurring. The third argument " L a m b d a " is a lambda
structure that describes the clause that is to be added. The arguments of the clause to be added
are the argument section (first argument) of the lambda structure and the body of the new clause
is the body section of the lambda structure (second argument). Note that since a predicate is
identified by both the name and number of arguments when the clauses of the to be added to
predicate are searched for in the database both the predicate name located in " P r e d N a m e " and
the number of arguments indicated in the argument section of the lambda structure " L a m b d a " are
used.
As a demonstration of how this predicate could be used assume that five clauses of a predicate
have been asserted as indicated below:
9 __ assertz(pred 12 : - cond 1).
9 _ assertz(pred 12 : - cond2).
9 _ assertz(predl2 : - cond3).
? - assertz(predl2 : - cond4).
?-- assertz(pred 12 : - cond5).
If we were to then use the addclause predicate to add a new clause:
? - addclause(predl2, 4, lambda([ ], newcond)).
yes
The database would be changed by placing a new clause in the database after the third clause of
the indicated predicate as shown by the below listing:
9__ listing(pred 12).
pred 12 : -- cond 1.
pred 12 : -- cond2.
predl2 : - cond3.
predl2 :-- newcond.
pred 12 : -- cond4.
predl2 :-- cond5.
yes
The key to implementing this predicate it to write a recursive predicate that recursively retracts
the clauses of the indicated predicate as a counter for the clause position is decremented until the
indicated position is reached, then the new clause is asserted. After the inner recursive calls return
the retracted clauses are reasserted. All assertions of clauses are to the front of the database so
that correct order is maintained. The recursive part of "addclause" is the second clause of
"addclause" indicated below while the base case of asserting the new clause is the first clause of
"addclause" indicated below. " N e w C o u n t " is the decrementation of "Count".
CL t8/l E
64 H. JUSTINCOVEN

addclause(PredName, 1, lambda(Args, Body)) : -


PredHead = . . [PredNamelArgs],
asserta((PredHead: - Body)), !.

addclause(PredName, Count, lambda(Args, Body)) : -


NewCount is Count - 1 ,
constructargs(Args, ConstructedArgs),
PredHead = .. [PredNamelConstructedArgs],
retract((PredHead : - FoundBody)), !,
addclause(PredName, NewCount, lambda(Args, Body)),
asserta((PredHead:- FoundBody)).

addclause(_, _, _ ) : -
printstring("Warning clause number out of bounds").

constructargs([ ], []).

constructargs([_lRest], [XlReturnRest]) : -
constructargs(Rest, ReturnRest).

Although the lambda structure indicates the number of arguments of the specified predicate,
care must be taken so that the arguments of each clause are not matched with each other. This
is the job of "constructargs". As each clause is retracted it must be saved in variables
(ConstructedArgs and FoundBody), "ConstructedArgs" can not be the same as the arguments of
the new clause being added nor can those arguments be the same for each retracted clause.
"constructargs" creates a list of uninstantiated arguments that is the same size of the clause to be
added. Thus when a clause is retracted its arguments can be saved in that list of uninstantiated
arguments.
The third clause of "addclause" prevents the predicate from failing. If the indicated position is
out of bounds a warning should be reported. This also allows the retracted clauses to be reasserted.
Since an improperly written clause would cause a Prolog predicate to unexpectedly fail we feel that
indication of proper position of clauses should also be strict. We don't just allow the clause to be
added last if the index is out of bounds.

4.2. Retrieving clauses


The predicate that retrieves specific clauses of predicates is called "getclause(PredHead, Position,
Lambda)". The second and third arguments are the same as for "addclause". However the first
argument is not just the name of the predicate but also includes arguments. Because "getclause"
uses the third argument " L a m b d a " as the returned clause, this argument must be a variable to start
with. If this is a variable it can not be used to indicate the number of arguments of the predicate
being searched for. Thus the need to indicate the number of arguments somewhere else. We choose
to indicate this in the first argument of "getclause". A predicate head can use the anonymous
variable such as: predl5(_,_,_).
"getclause" Is implemented in an identical fashion as "addclause" was implemented and is
given in the appendix. There is one variation from "addclause" that should be mentioned: the
third clause of "getclause" {getclause(_,_,true):-printstring("Warning...} that reports
the warning needs to determine what should be returned for the lambda structure (third
argument) if no searched for clause was found. We could leave the " L a m b d a " variable (the
third argument) uninstantiated, but if it were tested or used later it might instantiate itself and
void the test or use. It seems inappropriate to force every user of this predicate to test if
the " L a m b d a " argument is an instantiated variable after every usage of "getclause". Here we
could return "lambda([ ], true)" which indicates an empty body ("true") with no arguments in
the head. Unfortunately it is possible that there are predicate clauses of this form that we
are actually "getting". Thus we choose to return "true" which does not match the expected form
of a lambda structure and can easily indicate that the "getclause" could not find an appropriate
clause.
Altering and applying predicates 65

4.3. Lisp's "function": retrieving and adding entire predicates


The next predicate we describe is "function(PredHead, Lambda)" which is an analog to the Lisp
function "function". In Lisp "function" will return the functional representation of its argument.
If a function name is the argument then "function" will retrieve the lambda structure associated
with it. This is exactly what our "function" does. Given a function or predicate name "function"
will return the lambda structure associated with it in the database into the " L a m b d a " argument.
Note that here as in "getclause" the predicate is identified using a predicate head not just a predicate
name. The lambda structure that is returned is the second lambda structure we discussed above,
which is a list of clauses. The implementation strategy is similar to "addclause" and "getclause"
and appears in the Appendix.
We provided "function" because it is an analog to the set of tools that exist in Lisp rather than
because it fits into our scheme of different tools. In the more general scheme of developing tools for
manipulating functions our "function" would really be part of "getclauses". Instead of having a
predicate that gets only one clause it would be appropriate to build a more versatile predicate that
can get a set of clauses. Instead of one index indicating the position of the clause, the predicate could
have two indices indicating a range of clauses. A " b " and an "e" could represent the first and last
clauses respectively. At this time we have not implemented this more involved predicate. This predicate
could be used to do the jobs of both "function", "getclause", along with having more general uses.
To complement "function" we provide "addfunction", which has the opposite task of asserting
an entire predicate not just a single clause. "addfunction" Uses the lambda convention we have
introduced earlier and appears in the appendix. "addfunction" takes two arguments--a predicate
name and a lambda structure that has a list of clauses in it.

4.4. Adding and retrieving conjuncts


One may not want to add an entire clause but rather just add a conjunct within the body of
a clause. We provide "addconjofclause(PredHead, ClauseNum, ConjunctNum, Conjunct)" to do
this. The first argument is a predicate head, since only part of a clause is being replaced we can
not have an entire lambda as an argument and thus we can't get the argument structure to match
from the lambda structure, therefore we need to use a predicate head. The next two arguments
indicate the clause position and the conjunct position within that clause. The final argument is the
actual conjunct to be inserted in the appropriate clause. If we had the single clause predicate
"predl : - a,b,c,d,e" we could do the following:
? - addconjofclause(predl, 2, z).
yes
9._ listing(pred 1).
predl : - a,z,b,c,d,e.
yes
The implementation is very similar to that of "addclause" and "getclause" with the addition of
code to recursively traverse down a conjunction of conjuncts. The code to do this is below in
"createbody". The code to recursively traverse down conjuncts is different from that for recursively
traversing lists and is worth noting because it is not commonly done. As is expected parentheses
and commas are used instead of square brackets and bars. However, with conjuncts the base case
and recursive step are differentiated by matching one conjunct versus matching more than one
conjunct. The base and recursive cases differ at zero versus more than zero for lists. For lists the
recursive clause can match a list of any size waiting until the position indicator reaches one.
However for conjuncts the recursive step can only match two or more conjuncts. If a counter
reaches its base case and there are not two or more conjuncts a separate clause must be provided
for this case. This can be seen in the third clause of "createbody" which is called by "addconjof-
clause" to handle placing of the conjunct.
addconjofclause(PredHead, 1, ConjunctNum, C o n j u n c t ) : -
retract((PredHead : - FoundBody)), !,
createbody(FoundBody, Conjunct, ConjunctNum, NewBody),
asserta((PredHead : - NewBody)).
66 H. JUSTINCOVEN

addconjofclause(PredHead, Count, ConjunctNum, Conjunct) : -


NewCount is Count - 1 ,
copypred(PredHead, CopyPredHead),
retract((CopyPredHead : - FoundBody)), !,
addconjofclause(PredHead, NewCount, ConjunctNum, Conjunct),
asserta((CopyPredHead:-Foundbody)).

addconjofclause(_, _, _, _) : -
printstring("Warning clause index out of bounds").

createbody((OldConjuncts, AddedConjunct, 1, (AddedConjunct, OldConjuncts)) : - !.

createbody((FirstConjunct, RestConjuncts), AddedConjunct, Count,


(FirstConjunct, RestReturn)) : -
NewCount is C o u n t - 1,
createbody(RestConjuncts, AddedConjunct, NewCount, RestReturn), !.

/*match one conjunct and not at the base case */


createbody(LastConjunct, AddedConjunct, 2, (LastConjunct, RestReturn)) : -
createbody(true, AddedConjunct, 1, RestReturn), !.

createbody(OldConjunct, _, _, OldConjunct) : -
printstring("Warning conjunct index out of bounds").
"getconjofclause" Retrieves a specified conjunct of a specified clause. This predicate has the same
arguments as "addconjofclause" except that the fourth argument will return a conjunct instead of
providing a conjunct to be placed. The implementation technique is identical to that for
"addconjofclause". If you have predl : - a,b,c,d,e, in the database then the following query would
behave as indicated:
? - getconjofclause(predl, 1, 4, X).
X=d
yes
Using "addconjofclause" it is possible to add a set of conjuncts into a clause instead of being
limited to adding only one conjunct at a time. This can be done by simply providing a conjunction
of conjuncts as the fourth argument instead of just one conjunct. However using the syntax and
implementation we have given we can not get a set of conjuncts. It should be possible to add the
feature of dealing with sets of conjuncts to a later version of this set of tools just as it should be
possible to deal with sets of clauses in later versions.
When we place a new conjunct into the middle of an old clause we may want the arguments
in the conjunct to match up with arguments in either the head or other conjuncts of the given clause.
If we are trying to match the head this can be done using the first argument of "addconjofclause".
To match arguments of other conjuncts, those conjuncts must be replaced at the same time. We
provide "replaceclause" and "replaceconjofclause" to do tasks like this. Because our tools do not
work with sets of clauses we would have to use "replaceclause" to do this task.

4.5. Removing, replacing and exchanging clauses, conjuncts and predicates


"replaceclause" Removes a clause specified by position and puts a clause specified by lambda struc-
ture in its place. "replaceconjofclause" Does the same thing but for conjuncts of clauses. Implemen-
tations of both of these predicates appear in the Appendix. The implementations for "removeclause"
and "removeconjofclause" whose names are self-explanatory also appear in the Appendix.
One may want analogs of "addfunction" and "function" for removing or replacing entire
predicates. For removing entire predicates there is the predicate "retractall" that appears in the
standard text [5], to replace entire predicates one can use a combination of "retractall" and
"addfunction".
Altering and applyingpredicates 67

Besides adding, getting, removing and replacing predicates, clauses and conjunctions, one may
want to exchange clauses or conjuncts within a predicate. We might want to move a clause or
conjunct from before another clause or conjunct to after it. We provide the predicates "exchange-
clause(PredHead, FromClause, ToClause)" and "exconjofclause(PredHead, FromClause, From-
Conj, ToClause, ToConj)" to handle these tasks. The implementations of these are similar but
slightly more involved than the implementations we have already given for "addclause" and the
other predicates. They too appear in the Appendix.
As an example use, if we have "predl : - w,x,y,z" and "predl : - a,b,c,d" in the database, then
the following would occur:
9_ exchangeconjofclause(predl, 1, 3, 2, 1).
yes
? - listing(pred 1).
predl : - w,x,a,z.
predl : - y,b,c,d.
yes

4.6. Editing unnamed predicates


So far in this section we have described a set of tools for modifying named predicates in the
database. This same set of tools could be extended to work with unnamed lambda structures. The
implementation for "addclause" is below and is simpler than the implementation for working with
named predicates. The main reason for the simplicity is that clauses do not have to be added and
retracted. Although the implementation is simpler, it is perhaps the set of tools for adding, getting,
removing, replacing and exchanging that work upon lambda structures that offers the greatest
promise of power for program manipulating programs. Although a human would work with
predicates in the database, it is more likely that a program would be passing around predicates
as lambdas and modify and apply those lambdas.
addclause(lambda(LambdaList), lambda(Agrs, Body),
Count, lambda(Return)):-
addclause-aux(LambdaList, [Args, Body], Count,
Return).
addclause-aux(Rest, New, 1, [Newl Rest]) : - !.
addclause-aux([FirstlRest], New, Count, [FirstlReturn]) : -
NewCount is Count - 1 ,
addclause-aux(Rest, New, NewCount, Return), !.
addclause-aux(OneE1, _, _, OneE1) : -
printstring("Warning index out of bounds").

4. 7. Pattern matching
Besides adding tools for working with sets of conjuncts and clauses you could also add some
pattern matching, instead of giving numbers for the position of the conjuncts or the clauses you
could give patterns to match. A program that was self-modifying predicates would likely know
parts of predicates by composition structure not by position numbers. Thus it is useful to have a
set of tools similar to what we have already developed that operate by matching clauses and
conjunctions instead of just having tools that work with indices. This is analogous to Icon [6] which
has tools to match parts of strings along with having tools that work with indices of strings. Icon
works with strings, the tools we give here work with predicates. As an examples we have
"addclausebefore(PredName, LambdaToPlace, LambdaToMatch)". The first argument is a predi-
cate name, the second is the lambda structure of the clause that is to be added, and the third
argument is a lambda structure that is to be matched. The new clause is to be placed before the
clause that is matched.
68 H. JUSTINCOVEN

We have chosen to create a predicate that places the new clause before instead of after, so that we
can place clauses at both the very beginning and very end of the predicate. To place at the beginning
we can provide a match argument which is the anonymous variable which will match anything--
specifically the first clause of the predicate--and thus the new clause will be placed before the first
clause. If the matching clause does not match anything then the new clause will be placed after all of the
existing clauses. Using the above database created for "predl2". We could execute the following query:

? - addclausebefore(predl2, lambda([ ], beforecond),


lambda([ ], cond3)).
yes
? - listing(pred 12).
pred 12 : - cond 1.
pred 12 : - cond2.
predl2 : - beforecond.
predl2 : - cond3.
pred 12 : - newcond.
predl2 : - cond4.
predl2 : - cond5.
yes

The implementation of "addclausebefore" differs a little from "addclause" and appears in the
Appendix.

4.8. Determining a final representation


In developing this set of tools we have been using two different representations. Not all of the
tools work with both of these representations. Some work with a list of clauses in a lambda structure
and others work with a lambda structure of two arguments. There is a precedent for allowing two
different representations: lists have not one but several different representations. Lists can be
represented as functors ".(A,B)", as lists "[A, B]", or as lists with bars "[AI[B]]". In a final production
version we could allow both of our lambda conventions to remain and thus we would have to add
to the implementation of each tool so that it could handle both representations. We developed both
conventions in this paper for the purpose of having a progressive development of tools and for
uniformity with the conventions of the Lisp language. In the final product we may instead wish
to have one simpler representation. We could drop the outer lambda functor and instead use
only the inner list of our previous lambda structure as the new lambda structure (e.g.
[[Args, Conjuncts], [Args2, Conjuncts2]]). We may also wish to change the names of many of the
tools so that they are shorter and more convenient to use--the names we chose here were for
understandability purposes.

4.9. An editor
The tools we have provided in this section can be used as an effective set of editing tools for
manipulating the database as a programmer is writing code into the database and experimenting
in developing Prolog code. This type of environment differs from that of using an editor such as
"emacs" because files do not need to be loaded continually. Instead of editing a file we are editing
the database. It is possible that a full database screen editor could be created even with syntax
checking built into the editor. We could extend the current tools to a screen editor, but it would
be more appropriate to create this type of tool internal to a Prolog implementation. However the
editing advantages of these tools are only a nice side effect in the development of tools for a program
to manipulate itself.

5. S U M M A R Y

In this paper we have developed a set of tools for manipulating and using Prolog predicates.
Language tools are built to do common tasks that are usually done by many instructions. With
Altering and applying predicates 69

the tools the same tasks can be d o n e with one instruction instead o f many. The p r o g r a m m e r can
think abstractly a b o u t the task with a single instruction instead o f having to w o r r y a b o u t n u m e r o u s
details o f the task. In Section 2 we developed a set o f tools similar to those o f Lisp for the applying
o f predicates (functions). In Section 3 we discussed h o w a p r o g r a m ' s self-manipulation o f code can
be a very powerful tool. In Section 4 we developed a set o f tools for manipulating Prolog predicates.
This set o f tools could be used as a database editing package or as the foundation o f a set o f tools
for a p r o g r a m ' s self-manipulation o f its own predicates.
The advantage o f having such tools is that instead o f writing codes for each o f the steps o f
asserting; retracting; altering; querying; naming; m a n a g i n g the database; and dealing with
n u m e r o u s clauses o f predicates, they are n o w put into single instructions that do all o f these tasks
at once. The tools are meaningful in that they are tasks that p r o g r a m m e r s typically want to do.
There is also a small set o f these tools. In Lisp it is not as easy to create such a small set o f tools
because Lisp functions can be lists embedded within other lists to an unlimited depth.
As indicated in Section 4 further w o r k would include the extension o f the given tools to fully
handle pattern matching, working with sets o f conjuncts and clauses, working with lambdas, and
simplifying the syntax. We would also wish to experiment with these tools in the development o f
learning programs, i.e. p r o g r a m s that themselves experiment with the creation o f different
predicates for the solving o f specific problems.

Acknowledgements--I would like to thank Ed Ashcroft for his discussions on this topic. I would also like to thank the
students of a "CSC202 Functional Programming Languages Laboratory: Prolog" course I taught at Arizona State
University in the spring of 1990 for their experimentation in and comments on the implementation of some of these ~ools.

REFERENCES

1. Winston, P. H. and Horn, B. K. P. LISP 3rd Edition. NY: Addison-Wesley; 1989.


2. Warren, D. H. D. Higher-order extensions to PROLOG: are they needed? Machine Intelligence 10 (Edited by Hayes,
J. E., Michie, D. and Pao, Y. H.), pp 441-454; Chichester, West Sussex: Ellis Horwood; 1982.
3. Steele, G. L. Jr. Common Lisp: The Language. Bedford, MA: Digitial Press; 1984.
4. Coven, H. J. A Descriptive-Operational Semantics for Prescribing Programming Languages with "Reflective" capabilities.
Arizona State University; 1991.
5. Clocksin, W. F. and Mellish, C. S. Programming in Prolog: Third, Revised and Extended Edition. NY: Springer-Verlag;
1987.
6. Griswold, R. E. and Griswold, M. T. The Icon Programming Language. Englewood Cliffs, NJ: Prentice-Hall; 1983.

APPENDIX

/* programming cliche */

count-if([], Function, 0).

count-if([FirstlRest], Function, Count) :-


apply(Function, First), !,
count-if(Rest, Function, OldCount),
Count is OldCount + 1.

count-if([_lRest], Function, OldCount) :-


count-if(Rest, Function, OldCount).

find-if([], Function, []).

find-if([FirstlRest], Function, First) :-


apply(Function, First), !.

find-if([_lRest], Function, Return) :-


find-if(Rest, Function, Return).
70 H. JUSTINCOVEN

/* getclause */

getclause(PredHead, 1, lambda(Args, Body)) :-


PredHead =.. [_lArgs],
clause(PredHead, Body), !.

getclause(PredHead, Count, Lambda) :-


NewCount is Count -1,
copypred(PredHead, CopyPredHead),
retract(( CopyPredHead :- FoundBody)), [,
getclause(PredHead, NewCount, Lambda),
asserta((CopyPredHead:- FoundBody)).

getclause(. . . . true) :-
printstring(
"Warning clause number out of bounds").

copypred(PredHead, CopyPredHead) :-
PredHead =.. [PredNamelArgs],
constructargs(Args, ConstructedArgs),

CopyPredHead =.. [PredNamelConstructedArgs].

/* Function */

function(PredHead, lambda(LambdaList)) :-
function-aux(PredHead, LambdaList).

function-aux(PredHead, [[CopyArgs, FoundBody]lRest]):


PredHead =.. [PredNamelArgs],
constructargs(Args, CopyArgs),
CopyPredHead =.. [PredNamelCopyArgs],
retract(( CopyPredHead :- FoundBody)), !,
function-aux(PredHead, Rest),
asserta((CopyPredHead:- FoundBody)).

function-aux(_, []).

/* Add Function */

addfunction(PredName, lambda(LambdaList)):-
addfunction-aux(PredName, LambdaList).

addfunction-aux(PredName, [[Args,Body]lRest]):-
PredHead =.. [PredNamelArgs],
addfunction-aux(PredName, Rest),
asserta((PredHead :- Body)), !.

addfunction-aux(_, []).

/* get a conjunct from within a clause of a predicate */


getconjofclause(PredHead, 1, ConjunctNum, Conjunct) :-
clause(PredHead, FoundBody), l,
grabconjunct(FoundBody, ConjunctNum, Conjunct).
Altering and applying predicates 71

getconjofclause(PredHead, Count,
ConjunctNum, Conjunct) :-
NewCount is Count -1,
copypred(PredHead, CopyPredHead),
retract(( CopyPredHead :- FoundBody)), !,
getconjofclause(PredHead,
NewCount, ConjunctNum, Conjunct),
asserta((CopyPredHead:- FoundBody)).
getconjofclause( . . . . . . true) :-
printstring("Warning clause index out of bounds").
grabconjunct((Conjunct, _), 1, Conjunct) :- !.
grabconjunct(Conjunct, 1, Conjunct) :- !.
grabconjunct((_, Rest), Count, Conjunct) :-
NewCount is Count - I,
grabconjunct(Rest, NewCount, Conjunct), !.

grabconjunct( . . . . true) :-
printstring("Warning conjunct index out of bounds").

/*remove Clauses */

removeclause(PredHead, 1) :-
retract((PredHead : - _ ) ) , !.

removeclause(PredHead, Count) :-
NewCount is Count -1,
copypred(PredHead, CopyPredHead),
retract(( CopyPredHead :- FoundBody)), t,
removeclause(PredHead, NewCount),
asserta((CopyPredHead:- FoundBody)).

removeclause(_, _) :-
printstring("Warning clause index out of bounds").

/* Remove Conjuncts */

removeconjofclause(PredHead, 1, ConjunctNum) :-
retract((PredHead :- FoundBody)), !,
removeconjunct(FoundBody, ConjunctNum, NewBody),
asserta((PredHead :- NewBody)).

removeconjofclause(PredHead, Count, ConjunctNum) :-


NewCount is Count -1,

copypred(PredHead, CopyPredHead),
retract(( CopyPredHead :- FoundBody)), I,
removeconjofclause(PredHead, NewCount, ConjunctNum),
asserta((CopyPredHead:- FoundBody)).

removeconjofclause( . . . . . ) :-
printstring("Warning clause index out of bounds").

removeconjunct((First, Rest), 1, Rest) :- !.

removeconjunct(First, 1, true) :- !.

removeconjunct((First, Rest), Count, (First, Return)) :-


NewCount is Count - 1,
removeconjunct(Rest, NewCount, Return), !.
72 H. JUSTINCOVEN

removeeonjunct(One, _, One) :-
printstring("Warning conjunct index out of bounds").

/*Replace Clauses*/

replaeeelause(PredName, 1, lambda(Args, Body)) :-


constructargs(Args, CopyArgs),
CopyPredHead =.. [PredNamelCopyArgs],
retract(( CopyPredHead :- _)), I,
PredHead =.. [PredNamelArgs],
asserta((PredHead:- Body)).

replaceclause(PredName, Count, lambda(Args, Body)) :-


NewCount is Count -1,
constructargs(Args, CopyArgs),
CopyPredHead =.. [PredNamelCopyArgs],
retract(( CopyPredHead :- FoundBody)), !,
replaceclause(PredName, NewCount, lambda(Args, Body)),
asserta((CopyPredHead:- FoundBody)).

replaceclause( . . . . . ) :-
printstring("Warning clause index out of bounds").

/* replace conjuncts */

replaceconjofclause(PredHead, 1, ConjunctNum, Conjunct) :-

retract((PredHead :- FoundBody)), !,
replaceconjunct(FoundBody, ConjunctNum, Conjunct, NewBody),
asserta((PredHead :- NewBody)).

replaceconjofclause(PredHead, Count, ConjunctNum, Conjunct) :-


NewCount is Count -1,
copypred(PredHead, CopyPredHead),
retract(( CopyPredHead :- FoundBody)), !,
replaceconjofclause(PredHead, NewCount, ConjunctNum,
Conjunct),
asserta((CopyPredHead:- FoundBody)).

replaceeonjofclause(. . . . . . . ) :-
printstring("Warning clause index out of bounds").

replaceconjunct((First, Rest), 1, Conjunct, (Conjunct, Rest)) :- [.

replaceconjunct(First, 1, Conjunct, Conjunct) :- !.

replaceconjunct((First, Rest), Count, Conjunct, (First, Return)) :-


NewCount is Count - 1,
replaceeonjunet(Rest, NewCount, Conjunct, Return), !.

replaceconjunct(One . . . . . One) :-
printstring("Warning conjunct index out of bounds").

/* Exchange Clauses*/

/* There are two recursive steps to this predicate: l) get the first
clause; 2) get the second clause placing the first in its place. Upon
returning from the second recursive step place the second clause
in place of the first.
*/
Altering and applying predicates 73

exchangeclause(PredHead, 1, ToClauseNum) :-
PredHead =.. [PredNamelArgs],
retract(( PredHead :- FoundBody)), !,
NewToCount is ToClauseNum -1,
replaceelauseandreturn(PredName, NewToCount,
larnbda(Args, FoundBody), lambda(RArgs, RBody)),
NPredHead =.. [PredNamelRArgs],
asserta((NPredHead:- RBody)).

exchangeclause(PredHead, FromClause, ToClause) :-


NewFClause is FromClause -1,
NewTClause is ToClause -1,
copypred(PredHead, CopyPredHead),
retract(( CopyPredHead :- FoundBody)), !,
exchangeelause(PredHead, NewFClause, NewTClause),
asserta((CopyPredHead:- FoundBody)).

exchangeclause(. . . . . ) :-
printstring("Warning FromClause index out of bounds").

replaceclauseandreturn(PredName, 1,
lambda(Args, Body), lambda(RArgs, RBody)) :-
PredHead =.. [PredNamelArgs],
constructargs(Args, RArgs),
CopyPredHead =.. [PredNamelRArgs],
retract(( CopyPredHead :- RBody)), t,
asserta((PredHead:- Body)).

replaceclauseandreturn(PredName, Count, lambda(Args, Body),


Return) :-
NewCount is Count -1,
constructargs(Args, CopyArgs),
CopyPredHead =.. [PredNamelCopyArgs],
retract(( CopyPredHead :- FoundBody)), !,
replaceclauseandreturn(PredName, NewCount,
lambda(Args, Body), Return),
asserta((CopyPredHead:- FoundBody)).

replaceclause(. . . . Lambda, Lambda) :-


printstring("Warning ToClause index out of bounds").

/* Exchange Conjuncts */

/* There are four recursive steps to this predicate: 1) get the first
clause; 2) get the conjunct of the first clause; 3) get the second
clause; 4) get the conjunct of the second clause placing the first
conjunct in its place and asserting it. Return the second conjunct
and on recursive returns place the second conjunct in place of the
first conjunct and assert the newly created clause.
*/

exchangeconjofclause(PredHead, 1, FCoNum, TCINum, TCoNum) :-


retract(( PredHead :- FoundBody)), 1,
exconjandreturn(PredHead, FCoNum, FoundBody,
NewBody, TCINum, TCoNum, TCoNum),
asserta((PredHead:- NewBody)).
74 H. JUSTINCOVEN

exchangeconjofclause(PredHead, FromClause, FromConj, ToClause,


ToConj) :-
NewFClause is FromClause -1,
NewTClause is ToClause -1,
eopypred(PredHead, CopyPredHead),
retract(( CopyPredHead :- FoundBody)), t,
exchangeconj ofelause(PredHead, NewFClause,
FromConj, NewTClause, ToConj),
asserta((CopyPredHead :- FoundBody)).

exchangeclause(. . . . . . . . . ) :-
printstring("Warning FromClause index out of bounds").

exconjandreturn(PredHead, 1, (First, Rest), (New, NewRest), 1, _,


Temp) :-
NTemp is Temp -1,
replaceconjret(Rest, NTemp, First, NewRest, New).

exconjandreturn(PredHead, 1, (First, Rest), (New, Rest), ToClause,


ToConj, _) :-
NTClause is ToClause -1,
replaeeconjandreturn(PredHead, NTClause, ToConj, First, New),
!.

exeonjandreturn(_, 1, One, One, 1. . . . ) :- !,


printstring("Warning ToConjunct index out of bounds").

exconjandreturn(PredHead, 1, First, New, ToClause, ToConj, _) :-


NTClause is ToClause -1,
replaeeconjandreturn(PredHead, NTClause, ToConj, First, New),
I.

exconjandreturn(PredHead, Count, (First, Rest),


(First, Return), ToClause, ToConj, T) :-
NewCount is Count - 1,

N T i s T - 1,
exconjandreturn(PredHead, NewCount, Rest, Return,
ToClause, ToConj, NT),
!.

exconjandreturn(_, _, One, One . . . . . . ) :-


printstring("Warning FromConjunct index out of bounds").

replaceconjandreturn(PredHead, 1, ConjunctNum, Conjunct, Return) :


copypred(PredHead, CopyPredHead),
retract((CopyPredHead :- FoundBody)), !,
replaceconjret(FoundBody, ConjunctNum, Conjunct, NewBody,
Return),
asserta((PredHead :- NewBody)).

replaceconjandreturn(PredHead, Count, ConjunctNum, Conjunct,


Return) :-
NewCount is Count -1,
copypred(PredHead, CopyPredHead),
retract(( CopyPredHead :- FoundBody)), !,
replaceconjandreturn(PredHead, NewCount, ConjunctNum,
Conjunct, Return),
asserta((CopyPredHead:- FoundBody)).
Altering and applying predicates 75

replaceeonjandreturn(_. . . . . . Conjunct, Conjunct) :-


printstring("Warning ToClause index out of bounds").

replaceconjret((First, Rest), 1, Conjunct, (Conjunct, Rest), First) :- !.

replaceconjret(First, 1, Conjunct, Conjunct, First) :- !.

replaceconjret((First, Rest), Count, Conjunct, (First, Return),


FromReturn) :-
NewCount is Count - 1,
replaceconjret(Rest, NewCount, Conjunct, Return, FromReturn),
!.

replaceconjret(One, _, Conjunct, One, Conjunct) :-


printstring("Warning ToConjunct index out of bounds").

/* Pattern Matching */

addclausebefore(PredName, lambda(Args, Body),


lambda(BeforeArgs, BeforeBody)) :-
constructargs(Args, CopyArgs),
PredHead =.. [PredNamelCopyArgs],
retract(( PredHead :- FoundBody)), !,
testequal(PredName, Args, Body,
BeforeArgs, BeforeBody,
CopyArgs, FoundBody).

testequal(PredName, Args,Body,BeforeArgs, BeforeBody,


BeforeArgs, BeforeBody) :-
!,
PredHead =.. [PredNamelBeforeArgs],
asserta((PredHead :- BeforeBody)),
AddedHead =.. [PredNamelArgs],
asserta((AddedHead :- Body)).

testequal(PredName, Args, Body, BeforeArgs, BeforeBody,


CopyArgs, FoundBody) :-
addclausebefore(PredName, lambda(Args, Body),
lambda(BeforeArgs, BeforeBody)),
PredHead =.. [PredNamelCopyArgs],
asserta((PredHead :- FoundBody)).

addclausebefore(PredName, lambda(Args, Body), _) :-


PredHead =.. [PredNamelArgs],
asserta((PredHead:- Body)).

About the Author--H. JUSTIN COVEN received a B.S. Degree in Mathematics from the University of
Arizona in 1983, an M.S. degree in Computer Science from the University of Arizona in 1985 and a Ph.D.
in Computer Science from Arizona State University in 1991. He then joined the faculty of Bellarmine
College, where he is now Assistant Professor in the Department of Computer Science. His research
activities center around the development of reflective language facilities that enable programming
languages to manipulate and access their internal structures. Further research activity will include the
usage of these facilities to develop intelligent programs that reason about themselves. Dr Coven is a
member of the Association for Computing Machinery, The Cognitive Science Society, the American
Association for Artificial Intelligence and Computer Professionals for Social Responsibility.

Das könnte Ihnen auch gefallen