================================================== QUERYING AND UPDATING A PROLOG DATABASE Approach #2: Using assertions to manage the database ================================================== Recall that we are storing for each employee -- name -- id number -- wage rate -- manager (true/false) and that we are interested in the following operations: 1. extract the ID number of a particular employee 2. change the wage 3. add a new one to a list or database 4. remove an old one from a list or database 5. give everybody a raise 6. get a list of all well-paid managers (wage > 50) Recall in the first approach we stored the database as a list, each object in which was a structure of the form employee('Fred', 2, 100, false) and we stored the database using a single assertion. In this approach rather than store a single list in the database, we will make many assertions. For Fred, for example, we will say: employee(2). name(2, 'Fred'). wage(2, 10). This scheme relies on ID numbers being unique. Notice there's no need to state "manager(2, false)." Instead we will state "manager(N)" for those individuals who *are* managers, and don't state it for those that are not. For the employee list in the previous example, our database then looks like this: ====================================== employee(1). employee(2). employee(3). employee(0). name(1, 'Fred'). name(2, 'Sally'). name(3, 'Mary'). name(0, 'Bill'). wage(1, 15). wage(2, 25). wage(3, 10). wage(0, 1000). manager(2) manager(3) manager(0). ====================================== It's a little awkward having to have the information about a single employee spread all around the database, but that's the way Prolog likes it. Now let's perform the same operations as last time. 1. First there's no sense of an accessor, or "extracting" the ID number from an employee record, because there's no such thing as an employee record. The equivalent might be to find a name associated with a number, which is trivial: find_name(Id, Name) :- name(Id, Name). In fact you wouldn't even bother writing this predicate--- you would just use name/2 directly. (Notice you can use this same predicate "in reverse" to find the Id number given a name: find_id(Name, Id) :- name(Id, 'Fred') ================================ 2. To change the wage for a person given a number is equally trivial: change_wage(Id, S) :- retract(wage(Id, _)), assert(wage(Id, S)). To change the wage given a person's name is almost as easy: change_wage_for_name(Name, S) :- name(Id, Name), retract(wage(Id,_)), assert(wage(Id,S)). ========================================== 3. To add a new employee to the database we have two clauses, one for managers and one for non-managers. But we can be clever and "re-use" the code for the non-manager case. =================================== add_employee(Id, N, W, false) :- assert(employee(Id)), assert(name(Id, N)), assert(wage(Id, W)). add_employee(Id, N, W, true) :- add_employee(Id, N, W, false), assert(manager(Id)). =================================== Removing an employee is very easy too: we just retract all the assertions associated with that Id: =================================== remove_employee(Id) :- retract(employee(Id)), retract(name(Id, _)), retract(wage(Id, _)), retractall(manager(Id)). =================================== 5. Now giving a raise to everybody is a little more complicated because there's no list of all employees. What we have to do first is generate a list off all Id numbers. For that we use setof, which turns all answers to a given query into a list. We also cheat and use two predicates give_raise/0 and give_raise/1. Note the similarity with helper functions in Lisp: the first one gets the list of employee Ids, and invokes the second. =========================== give_raise :- setof(X, employee(X), L), give_raise(L). give_raise([]). give_raise([Id | Rest]) :- wage(Id, W), retract(wage(Id, W)), W1 is W * 1.1, assert(wage(Id, W1)). ============================ 6. It's easy to get a single well paid manager: ====================== rich_manager(X) :- manager(X), wage(X, W), W > 100. ====================== And to get the list of all of them, we just use the predicate above: ====================== rich_managers(L) :- setof(X, rich_manager(X), L). ======================