A Prolog logicbase is a collection of modules. Each module contains a collection of predicates, defined by a functor (name) and arity (number of arguments). A predicate is a collection of one or more clauses.
There are at least two modules in every logicbase. These are the system module and the default user module, user. The system module contains the built-in predicates of the system. The user module contains the application or user predicates that are not specifically put in other modules. See the chapter on modules for details on using modules.
The modules in the system can be examined using current_module/1.
current_module/1 unifies MODULE with the currently defined modules in the logicbase. If MODULE is a variable, on backtracking, current_module/1 will return all the user-defined modules. For example, when the duckym sample is consulted in the listener:
?- current_module(M). M = user ; M = rules ; M = data ; no
Notice that the system modules are not listed. They all begin with 'amzi' and you should avoid that naming convention for your modules.
Each predicate in a module is defined by a functor and arity. The predicates can be listed using current_predicate/1.
current_predicate/1 unifies its argument with the functors(F) and arities(A) in the specified module(M). The module(M) must be bound to a valid module name. The example shows the first few predicates in the 'rules' module of the duckym sample.
?- current_predicate(rules:F/A). F = chase A = 1 ; F = connect A = 2 yes
There are four types of predicates that can be stored in the logicbase:
These, and other properties of predicates can be queried using predicate_property/2.
predicate_property/2 unifies PROPERTY with a property of the predicate in module M, with functor F and arity A. M:F/A must be bound. If just F/A is specified, the current module is used for M. If PROPERTY is a variable, on backtracking predicate_property/2 will return all properties of the predicate. The properties reported on are:
For example:
?- consult(duckym). yes ?- predicate_property(rules:loc/2, P). P = imported_from(data) ; P = defined_in(data) ; P = dynamic ; P = exported ; no % load extended file predicates lsx ?- loadlsx('aosutils.lsx'). yes ?- predicate_property(user:chdir/2, P). P = defined_in(user) ; P = extended ; no % look at some system predicates ?- predicate_property(write/2, P). P = imported_from(amzi_system) ; P = defined_in(amzi_system) ; P = static ; no
The above predicates can be used to collect lists of predicates. For example, here is a definition of a predicate that generates a list of all the dynamic predicates:
get_dynamic_preds(LIST) :- findall(M:F/A, ( current_module(M), current_predicate(M:F/A), predicate_property(M:F/A, dynamic) ), LIST ).
Running it with a consulted duckym.pro:
?- get_dynamic_preds(L). L = [user:get_dynamic_preds/1, user:go/0, user:init_state/0... rules:look/0, rules:look_connect/1... data:loc/2, data:nextto/2] yes
Static predicates become part of the logicbase by being separately compiled, and then loaded. Most commonly, the compiled files are linked into the application's .xpl file which is automatically loaded by arun, or which is loaded by a host language application calling the Logic Server API.
Individual .plm files, containing compiled predicates corresponding to .pro files, can also be loaded and unloaded under program control, or from the listener. The following predicate provide that capability.
load/1 is similar to consult, but it only loads object (.plm) files. If no file extension is given, and no files match the name, then it assumes the extension is PLM.
For example:
?- load([duck1, duck2]). % loads duck1.plm and duck2.plm
load/4 allows you to load a .plm file from a block of memory. It is designed to be called from Logic Server applications that have special storage of .plm files.
unload/1 allows you to 'unload' all of the clauses that resulted from a 'load' of the same file. This, along with multifile, lets you dynamically change the state of static predicates in the logicbase.
For example, consider a tech support application with a predicate rule/3. If a problem area is determined to be a printer problem, then the file printer_rules.plm can be loaded. Once the problem is resolved, it can be unloaded, and then a different set of rules loaded for the next problem.
When a file is consulted into the logicbase, its predicates are all entered as dynamic predicates. A predicate defined in a file that is compiled, can be kept as a dynamic predicate by using the dynamic directive.
Different storage option for dynamic predicates can also be specified. These are indexed and sorted, and both provide much faster access for predicates that have large numbers of clauses.
The directives affecting dynamic storage are:
The dynamic/1 directive specifies that the clauses of the predicate with functor F and arity A will be stored as dynamic. This directive is only needed by the compiler, to let it know which predicates NOT to compile. When a file is consulted, all predicates are dynamic.
Multiple comma separated F/A pairs can be entered with a single dynamic directive.
The indexed/1 directive lets you specify a key or composit key for a dynamic predicate. F is the functor of the predicate. Each Ix corresponds to an argument of the predicate. Each Ix has a value of either 1, indicating the argument is in the key, or 0, indicating the argument is not in the key. The key does not have to be unique.
When a goal refers to the indexed predicate, and the arguments making up the key are bound, then access to the clauses will be direct and very fast. If any of the arguments making up the key are not bound, then access will be no different than with normal, unindexed dynamic clauses.
For example, consider an application with a large number of clauses defining customers. The form of the clauses is:
customer(ID, LAST_NAME, FIRST_NAME, PHONE, ADDRESS).
If customers were retrieved primarily by ID, then this directive would make that first argument a key.
:- indexed customer(1,0,0,0,0).
If customers were retrieved primarily by the full name, then this directive would create a composit key from the second and third arguments.
:- indexed customer(0,1,1,0,0).
Benchmarks have shown that for predicates made up of a large number of clauses that are facts (have no bodies), indexed dynamic storage can be significantly faster than even compiled clauses, which include first argument indexing. This is because compiler indexing is designed for high performance for smaller numbers of clauses used as rules; whereas indexed dynamic storage is designed like database indexing for predicates which are more database like.
The indexed directive implies dynamic, so indexed predicates do not have to be declared dynamic as well.
The sorted directive allows you to specify that the clauses of a predicate with functor F and arity A, are stored in sorted order, according to standard sort order. This means that when retrieved, they will be retrieved in sort order.
This also means that retrieval can be almost as fast as for indexed predicates, when the left-most arguments of a goal query are bound. For example if the customer/5 predicate in the indexed section had its arguments rearranged like this:
customer(LAST_NAME, FIRST_NAME, ID, PHONE, ADDRESS)
Then the customers would display sorted by last name, then first (then ID, phone and address), and queries with either last name or last name and first name, would execute very fast. Queries where just ID was bound, would take the same time as for non-sorted dynamic predicates.
assert/1, asserta/1, and assertz/1 all have the same effect with sorted predicates, and that is to insert the new clause in sorted order.
Consider, for example this source file, petdata.pro:
:- sorted(pet/2). pet(dog, susie). pet(duck, leona). pet(hampster, gus). pet(dog, sam). pet(rabbit, buns). pet(fish, jones). pet(duck, bif). pet(dog, jasmine). pet(fish, dow).
Then consider this listener session, noting that the duck names returned from the findall are sorted.
?- [petdata]. yes ?- listing(pet). user:pet(dog, jasmine). user:pet(dog, sam). user:pet(dog, susie). user:pet(duck, bif). user:pet(duck, leona). user:pet(fish, dow). user:pet(fish, jones). user:pet(hampster, gus). user:pet(rabbit, buns). yes ?- assert( pet(duck,ivan) ). yes ?- findall(X, pet(duck,X), L). = H1 L = [bif, ivan, leona] yes
Multiple comma separated F/A pairs can be entered with a single sorted directive.
The clauses of dynamic predicates can be added and removed dynamically at run time.
consult/1 can be used to dynamically consult all the predicates in a source file.
listing/0,1,2 can be used to display dynamic predicates.
The following predicates provide finer control. Many have an argument of TERM, where TERM represents a clause. For a TERM to represent a clause, it must have a HEAD and may optionally have a BODY.
A HEAD can be:
The module specification is optional, and the current module used if none is provided.
A BODY is a list of goals. If a BODY is used, then it is separated from the HEAD by the neck operator, and the TERM is enclosed in parenthesis.
Examples of TERMs used with assert/1:
assert( ducks ). assert( duck(leona) ). assert( pets:duck(leona) ). assert( (pet(duck) :- sound(quack)) ).
abolish/1 removes all dynamic clauses for the predicate with functor F and arity A, in module M. The module specification is optional. abolish/1 is faster than retractall/1 when it is desired to remove all the clauses for a predicate that is not being used. For example:
?- abolish(expense/3).
removes all of the dynamic expense/3 clauses.
abolish/1 does not check if the predicate is being used, so if there are goals using the predicate which might be backtracked into, a system crash can result. retractall/1 is safer but slower, as it guarantees proper failure if there are any goals which might backtrack into the predicate.
asserta/1 adds clauses at the front of the list of clauses defining a predicate. See legal values for TERM above.
assertz is similar to asserta except the clause is added at the end of the list of clauses. See legal values for TERM above.
assert/1 is a synonym for assertz/1.
clause/2 succeeds if HEAD (see definition of HEAD and BODY above) can be unified with the head of a dynamic clause in the database, and BODY with its body. If clause is backtracked into, it will keep on succeeding as long as there is another clause whose head matches HEAD.
HEAD must be sufficiently instantiatedthe name and arity of the structure must be apparent in Head. For example if the duckym sample is consulted:
?- clause(rules:connect(X,Y), BODY). X = H1 Y = H2 BODY = nextto(H1, H2) ; X = H1 Y = H2 BODY = nextto(H2, H1) ; no ?- clause(go, X). X = done ; X = write(`>> `) ',' read(H85) ',' H85 \= quit ',' do(H85) ',' demons ',' '!' ',' go ; X = write(` Quitter `) ',' nl ; no
retract/1 removes clauses that unify with TERM. The same restrictions on TERM applies to retract as it does to assert. See introduction above for details.
The first clause in the predicate which matches TERM is removed. For example to remove a specific clause:
?- retract(pet(hampster, gus)). yes
To remove clauses that match a pattern, a variable can be used in one or more arguments. The variable is bound with the value from the retracted clause. Backtracking will look for other clauses that match the pattern. For example, to remove just some pet ducks:
?- retract(pet(duck, X)). X = bif; X = leona; X = ivan; no
retract/1 will fail if there are no clauses which match the pattern. If you want to ensure a certain pattern of clauses does not exist, use retractall/1 instead. If you want to remove all dynamic clauses for a predicate, use abolish/1.
retractall/1 is similar to retract/1 except all clauses which match TERM are removed, and it always succeeds. For example:
?- retractall(pet(duck,_)).
Because retractall/1 always succeeds, it is a better than retract/1 for making sure there are no clauses that match a particular pattern.
If it is desired to remove all of the dynamic clauses for a predicate, not just those that match a particular pattern, abolish/1 is more efficient.
Copyright ©1987-2011 Amzi! inc. All Rights Reserved. Amzi! is a registered trademark and Logic Server is a trademark of Amzi! inc.