![]() |
|||||||
Prolog++ toolkit - DetailsHow OOPS benefits from LogicThere are a number of advantages gained by using Prolog as a basis for an object-oriented language.
Adding OOPS to PrologPrograms may be developed using Prolog++ which augment standard Prolog in the following ways:
Prolog as a logic-based languageProlog is a declarative language. This means that the formal definition or declaration of a problem may be used as a functional program to solve that problem. In Prolog we declare the logical relationships of a given problem domain rather than state a step-by-step procedural recipe for solving the problem. This is useful as often we know various aspects of the problem but very little about how to find a solution. Prolog will attempt to find a solution for us given the information about the problem.The way Prolog does this is by using a built-in inference engine, which automatically infers the solution to a given query using the facts and rules as defined in the program. Prolog programs consist of facts and rules, which are referred to as clauses. A fact consists of a single assertion with no conditions: a fact is always true. A rule consists of a goal, whose truth is dependent upon a set of conditions or sub-goals. The goal of a rule is referred to as its head and the sub-goals as its body. Prolog attempts to prove a goal by using its backward chaining inference engine to match the initial goal with either a known fact or the head of a rule. If the goal is matched with a known fact the goal is proven. If the goal is matched with the head of a rule the original goal is then replaced with the sub-goals which form the body of the rule. Prolog then attempts to prove, in the same way, each of these sub-goals in turn. Although Prolog programs are thought of as declarative they can also have a procedural reading. Prolog is versatile: there is a style of Prolog programming which mimics the conventional procedural approach, but with less emphasis on the actual assignment statements. Prolog as a logic programming language has a sound theoretical basis, being modelled on the first-order predicate calculus. This means that theoretically a Prolog program, which may be read as a set of axioms and theorems, contains the possibility of being proven consistent or inconsistent, regardless of the implementation. In this way Prolog can be thought of as an automated theorem prover.
Variable TypesA feature of Prolog is its use of type free variables which can represent widely different data structures. This allows any variable in a clause to represent a number, a string, a list of numbers, a list of strings, a list of strings and numbers etc. Prolog variables can even be used to represent clauses. This last use of variables leads to a technique called meta-programming, where the clauses in one part of a program can manipulate other clauses as data. This 'meta-level' ability is one reason why Prolog has been widely used in the realm of expert systems.
Data StructuresAnother feature of Prolog is its ability to allow the dynamic assertion and retraction of rules and facts. This makes the Prolog rule and data base truly dynamic at run-time. This is essential for "learning systems" and other applications involving the introduction of new concepts, rules or facts at run-time.Memory is dynamically allocated and deallocated at run-time. The deallocation is automatically done by a built-in garbage collector. The programmer does not have to be concerned with the implementation, maintenance and bookkeeping normally associated with the creation and destruction of complex dynamic data structures and is freed to concentrate on stating the logic of the problem. Prolog is now in widespread use in industry and the ISO Prolog standard was published in 1995. The Prolog Management Group was formed in 1993 to promote and highlight the various commercial applications fo Prolog.
Interfacing to external code and dataProlog++ has direct access to Prolog, which in turn has support for procedures written in C, C++ and Pascal. This is platform specific, but, for instance, under Windows 95 and 3.1, there is extensive support for DLLs and DDE. LPA Prolog also has a comprehensive set of formatted I/O routines for accessing external structured data files, as well as a dedicated interface to most commercial databases via ODBC. This ability to integrate rules and data is essential in fielded systems.
Development facilitiesAt development time there are a range of aids and support facilities provided by Prolog++. These differ from platform to platform but generally include:
History and availabilityProlog++ is a mature product and was developed by Logic Programming Associates Ltd (LPA) in 1989 for 640K MS-DOS PCs. This accounts for its efficient and compact run-time system. Prolog++ is now available as an LPA product on Windows 95, Windows 3.1, Macintosh and MS-DOS machines.1994, Addison-Wesley published "Prolog++: The Power of Object-Oriented and Logic Programming" by Chris Moss. In 1995, LPA released v2.0 which introduced part-of hierarchies to supplement is-a hierarchies and other extensions.
NotationAs a brief introduction to some of the notation used within Prolog++ the following table is given. This is not intended to be an exhaustive set of all the available symbols, but rather an initial set to illustrate the sophistication of the system.
Method/Arity
Program StatementsThe program statements occurring between the open and close statements define the structure and contents of a class. They fall into two distinct categories:
Logical variablesProlog++ uses the same terminology for logical variables as Prolog (e.g. Element, Stack).
Instance variablesProlog++ provides two instance variables represented by specially reserved words of the language. They can be used in any context whatsoever, having the same syntactic status as any other Prolog term. The instance variables provided by Prolog++ are:
Is-a hierarchyThe is-a hierarchy of Prolog++ relates specific classes with more general classes through the mechanism of inheritance. The is-a relationship between classes is expressed within each class definition by declaring its super or parent class(es).
class stack. inherits buffer % inherits all characteristics of buffers ... end stack. class queue_with_statistics. inherits queue, % inherits from general queues buffer_with_statistics % as well as statistical buffer ... end queue_with_statistics.A strict hierarchy is one in which all classes have either a single super class or none at all. Such a hierarchy is said to exhibit singular inheritance. Hierarchies in which at least one class has several super classes is said to exhibit multiple inheritance.
The is-a hierarchy can be navigated by referring to super, sub, ancestral and descendant classes. This is expressed by a collection of reserved words:
Part-of hierarchyThe part-of hierarchy of Prolog++ relates class instances with oneanother such that they can be thought of either collectively or individually. The creation of a complex object such as a bicycle may involve the creation of several parts, including wheels, a frame and handlebars. These in turn may involve the creation of further sub-parts such as spokes and rims. The part-of relationship between class instances is expressed within each class definition by declaring its component parts.
class bicycle. parts frame, wheel * 2, seat, handle_bars. end bicycle. class unicycle. inherit bicycle. parts wheel, handle_bars * 0. end unicycle. class tandem. inherit bicycle. parts seat * 2, handle_bars * 2. end tandem.The parts declaration can be inherited. For example, in addition to the 2 seats and 2 sets of handle-bars a tandem will inherit a frame and 2 wheels from the bicycle class. The unicycle forces the handle-bars not to be inherited by explicitly declaring 0 for that part.
Composite instancesWhenever a new instance of a class is created the parts declaration of that class is inspected and the corresponding instances of the sub-parts are created. For example, consider the various cycle classes above and assume that the others are all sub-classes of cycle_component.. class cycle_component. public method when_created/0. when_created :- write( 'Created: ' ), write( self ), nl. end cycle_component. Accessing Part-of hierarchyThe part-of hierarchy can be navigated by referring to sub parts, super parts and the top-most ancestral part. This is expressed by a collection of reserved words:
AttributesThe attributes of a class correspond to the characteristics of the entity which it represents. They may or may not have default values. Attributes can be split into those which relate to individual instances of the class and those for the overall class itself. Each of these can be declared as private or public attributes.A public attribute is one whose value is accessible from outside the class in which it occurs, whereas a private attribute can only be accessed from within. In either case, the attribute's value can only be changed from within the class.
public class attribute
smallest . % the smallest instance in
. % this class; no default value
private instance attributes
contents = [] , . % default contents is empty list
size is 0 , . % the default size is zero
cumulative_size inherited .% cumulative size is inherited
. % from an ancestor class
An instance attribute is one which relates to an individual instance of a class. For example, consider a class representing customers of a bank. One attribute is the amount of work necessary to service each individual customer. A class attribute is one which relates to the overall class itself. For example, an atribute of a class of customer queues may be a pointer to the smallest queue. Each attribute in the declaration list can be assigned an explicit default value (arithmetic or non-arithmetic), an inherited default value or have no default.
AssignmentThere are two forms of attribute assignment in Prolog++, one which activates procedures and the other which does not. An assignment which invokes the daemon manager is referred to as a noisy assignment, and one which merely performs the update and does nothing else is referred to as a quiet assignment. They both have the same basic form:
:= is an example of a noisy assignment operator and :== is an example of a quiet assignment operator.
A Case Study In Fault DiagnosisThis case study will investigate the application of Prolog++ to the diagnosis of faults. It is intended to illustrate the technique of progressively sending messages through the hierarchy, starting from at a general level and grdually moving down to classes at the lower, more specific levels. The hierarchy is used to represent the implicit relationships between different classes of faults.
The ProblemWe would like to represent the causal-effect relationship between faults and symptoms. The particular domain, that of car maintenance, was chosen to illustrate the techniques in layman terms. Everybody is familiar, to a greater or lesser degree, with the problems that can occur with automobiles.The crucial factor in fault diagnosis is being able to quickly pinpoint the general area of the problem, before focusing in on the root cause. Identifying general aspects of the problem can avoid going down blind alleys, and more importantly avoids asking the user seemingly irrelevant questions. For the domain of car maintenance, an automobile can be dissected into several problem areas such as the fuel system, mechanical faults and electrical faults. Associated with each area is a collection of faults which may occur, and the symptoms which they cause. Some of the symptoms may be contradictory, in the sense that two symptoms cannot possibly occur simultaneously.
The ClassesFrom the above discussion two quite separate types of instance can be identified. The first concerns itself with fault diagnosis, including the ability to move from general terms down to specific terms. The second deals with the problem domain, in terms of the causal-effect relationships between actual faults and exhibited symptoms.The following example illustrates a hierarchy for identifying faults in automobiles. At the topmost level is the faults class containing the algorithm for finding faults, and which is inherited by all of the other classes in the hierarchy. At any point, however, a class has the option to override the default search algorithm with one that is specialised for its problem area. The following sections define the protocol (attributes, methods and functions) for the faults instance and for the domain specific objects.
The Domain Classes
class mechanical.
inherit fault.
end mechanical.
class engine.
inherit mechanical.
end engine.
class cylinders.
inherit engine.
end cylinders.
class fuel_system.
inherit fault.
end fuel_system.
class electrical.
inherit fault.
end electrical.
class lights.
inherit electrical.
end lights.
class starting.
inherit electrical.
end starting.
class starter_motor.
inherit starting.
end starter_motor.
class sparking.
inherit starting.
end sparking.
class plugs.
inherit sparking.
end plugs.
class distributor.
inherit sparking.
method fault // 1.
fault( f1001 ) = 'Condensation in the distributor cap'.
fault( f1002 ) = 'Faulty distributor arm'.
fault( f1003 ) = 'Worn distributor brushes'.
method symptom // 1.
symptom( s1001 ) = 'The starter turns but the engine doesnt fire'.
symptom( s1002 ) = 'The engine has difficulty starting'.
symptom( s1003 ) = 'The engine cuts out shortly after starting'.
symptom( s1004 ) = 'The engine cuts out at speed'.
method effect // 1.
effect( f1001 ) = s1001.
effect( f1002 ) = s1001.
effect( f1002 ) = s1004.
effect( f1003 ) = s1002.
effect( f1003 ) = s1003.
method contrary / 2.
contrary( s1002, s1001 ).
contrary( s1003, s1001 ).
end distributor.
The fault Class
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% CLASS : fault %
% COMMENT: How to search for and report faults %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
class fault .
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% DECLARATIONS %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
category
fault , % A class in the fault diagnosis
user .
public methods
findall / 0 . % Find and report all possible faults
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% DEFINITIONS %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% METHOD : findall/0 %
% COMMENT: Find and print all possible faults %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
findall :-
dynamic( told_by_user / 2 ),
forall (
( Where = self
; Where = descendant_class ),
Where <- find( Fault )
)
do (
write( 'Location : ' ), write( Where ), nl,
write( 'Possible Fault: ' ), write( Fault ), nl, nl
).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% METHOD : find/1 %
% COMMENT: Find a possible fault for some class %
% Explicit use of 'self' to avoid any early bindings! %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
find( Fault ) :-
public( fault // 1 ),
Fault = self@fault( FaultNumber ),
forall SymptomNumber = self@effect( FaultNumber )
do exhibited( SymptomNumber ).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% METHOD : exhibited/2 %
% COMMENT: Ask the user whether or not the symptom is %
% exhibited & remember %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
exhibited( SymptomNumber ) :-
told_by_user( SymptomNumber, Reply ),
!,
Reply = yes.
exhibited( SymptomNumber ) :-
cat( [self@symptom(SymptomNumber),'?'], Question, _ ),
( yesno( Question ) -> Reply = yes ; Reply = no ),
asserta( told_by_user( SymptomNumber, Reply ) ),
Reply = yes,
forall ( self <- contrary( SymptomNumber, ContrarySymptom )
; self <- contrary( ContrarySymptom, SymptomNumber ) )
do asserta( told_by_user( ContrarySymptom, no ) ).
end fault.
The OutputThe following outputs were taken from a fault diagnosis session.
?- faults <- findall . Location : distributor Possible Fault : Condensation in distributor cap Location : distributor Possible Fault : Faulty distributor arm No more solutions |