Database Manipulation Section 7. 4
Prolog Knowledge Base n Knowledge base = database – set of facts/rules in the program Can add/subtract facts and rules at run time n Adding facts/rules n – assert, asserta, assertz n Subtracting facts/rules – retract, retractall
Asserting a Fact n Just tell prolog that it’s so ? - raining. ERROR: Undefined procedure: raining/0 ? - assert(raining). Yes ? - raining. Yes n Prolog didn’t know about raining/0
Preparing Prolog for a Predicate n Need to tell Prolog that the predicate exists – helpful also to say that it may have rules added n Give a directive in the program file – a command to Prolog : - dynamic raining/0. n Says that raining/0 is a predicate – prevents the error message
Dynamic Predicates n Can be done during session ? - dynamic foggy/0. Yes ? - foggy. No n Note how we call goals in a file : - dynamic foggy/0. – can do that with any query
Asserting Rules n Just give rule instead of fact – use parentheses if rule is compound (to prevent confusion about the comma) ? - assert(raining). ? - assert(bad : - raining ; foggy). ? - assert(nice : - (sunshine, + cold)). ? - bad. Yes
Assertion Order n assert/1 puts the fact/rule in the database – order is implementation dependent – (SWI-Prolog puts it at the end) asserta/1 puts fact/rule in front n assertz/1 puts fact/rule at end n
Assertion Order ? - assert(num(1)). ? - assertz(num(2)). ? - asserta(num(0)). ? - assertz(num(3)). ? - num(X). X=0; X=1; X=2; X=3 Adds num(1) to database Adds num(2) to end of DB Adds num(0) to front of DB Adds num(3) to end of DB num(0). num(1). num(2). num(3).
Assertion Order Exercise ? - asserta(what(1)). ? - assertz(what(2)). ? - asserta(what(3)). ? - assertz(what(4)). ? - asserta(what(5)). ? - what(X). X=? ;
Exercise n Write a predicate that asks the user for a person’s parents & asserts those facts ? - add_parents(mark). Who is mark’s father? bob. Who is mark’s mother? isabel. Yes ? - father(mark, Dad), mother(mark, Mom). Dad = bob, Mom = isabel
Solution ask_parents(Person) : ask_for_who(Person, father, Dad), ask_for_who(Person, mother, Mom), assert(father(Person, Dad)), assert(mother(Person, Mom)). ask_for_who(Person, Role, Name) : write(‘Who is ’), write(Person), write(‘’s ’), write(Role), write(‘? ’), read(Name).
Retraction n Tell Prolog to remove a fact/rule ? - raining. Yes ? - retract(raining). Yes ? - raining. No
Retracting Rules n As for asserting rules – use parentheses if body is compound – body may be a variable/partly instantiated ? - retract(bad : - raining ; foggy). ? - retract(nice : - Body). Body = sunshine, + raining Yes
Retraction Order n From first to last ? - retract(num(N)), retract(num(M)). N=0 M=1 Yes n retract fails if no clause matches
Retracting All Clauses n rectractall/1 retracts multiple clauses – all clauses with head matching the argument ? - num(N). Note: also gets rules N=0 ? - assert(bad : - member(1, )). Yes ? - retractall(num(N)). ? - bad. Yes ? - num(N). ? - retractall(bad). No Yes ? - bad. No
Asserting and Retracting n Used for AI programs that learn – create a new rule & add it to the database – forget an old rule n Can also be used for efficiency – asserta solutions previously found – found before general code called
Naïve Fibonacci fib(1, 1). fib(2, 1). fib(N, F) : N > 2, N 1 is N – 1, fib(N 1, F 1), N 2 is N – 2, fib(N 2, F 2), F is F 1 + F 2.
Trace fib(5, F) fib(5, F 0) fib(4, F 1) fib(3, F 2) fib(2, F 3) F 3 = 1 fib(1, F 4) F 4 = 1 fib(2, F 5) F 5 = 1 fib(3, F 2) fib(3, F) gets calculated again extra work done fib(2, F 3) F 3 = 1 much worse as #s get bigger fib(1, F 4) F 4 = 1
Assertional Fibonacci (8. 5. 4) fib 2(1, 1). fib 2(2, 1). fib 2(N, F) : N > 2, N 1 is N – 1, fib 2(N 1, F 1), N 2 is N – 2, fib 2(N 2, F 2), F is F 1 + F 2, asserta( fib 2(N, F) ). % remember the result % at the beginning
Trace fib 2(5, F) fib 2(5, F 0) fib 2(4, F 1) fib 2(3, F 2) fib 2(2, F 3) F 3 = 1 fib 2(1, F 4) F 4 = 1 Saves work from asserta( fib 2(3, 2) ) calculating fib(3) fib 2(2, F 5) F 5 = 1 asserta( fib 2(4, 3) ) fib 2(3, F 6) F 6 = 2 Matches asserted fact – no need asserta( fib 2(5, 5) ) to recalculate
Problems for Asserting Solutions n Backtracking gives multiple solutions – saved solution found first – then solution re-calculated – and re-asserted: solution there twice, now – next time there’ll be three solutions n Adding a cut to the solution might help – asserta( fib 2(F, N) : - ! ).
Generating Facts n Multiplication table make_table : + ( L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], member(X, L), member(Y, L), Z is X * Y, assert(product(X, Y, Z)), fail ).
Using the Multiplication Table n Now have 100 multiplication facts ? - product(X, Y, 12). X = 2, Y = 6 ; X = 3, Y = 4 ; X = 4, Y = 3 ; X = 6, Y = 2 ; No
Asserts on Calls in Progress n Suppose assert or assertz a clause of a predicate that’s in progress – is that clause considered for that call? strange(N) : assertz(strange(N)), fail. n Does strange(3) succeed or fail? – or throw an exception
Speed of Dynamic Predicates Dynamic predicates may be slower than static (dynamic = can change, static = can’t) n Prolog may have a better way for remembering solutions n – remember or record predicates to memorize – recall or recorded to recall – forget or erase to forget
Remembering Solutions n One clause (near front) to look for remembered solutions – cut if find one n Remember solution after it’s calculated
Remembering Fibonacci fib_mem(0, 1). fib_mem(1, 1). fib_mem(N, F) : - recorded(fibonacci, fib(N, F)), !. fib_mem(N, F) : N > 1, succ(N 1, N), fib_mem(N 1, F 1), succ(N 2, N 1), fib_mem(N 2, F 2), F is F 1 + F 2, recorda(fibonacci, fib(N, F)).
Recording n recorda/2, recordz/2 store 2 nd argument – use 1 st argument as a key – you can record same fact under multiple keys – key is integer or atom (or term – but no good) n recorded/2 retrieves fact – needs key – only retrieves facts under that key – matches fact – only those that match selected
Erasing/Forgetting recorded/3 also gives a memory location n Can use the location to erase the record n ? - recorded(fibonacci, fib(500, _), Mem), erase(Mem). Yes n Can only erase one item at a time – don’t erase anything more than once
Versions of Fibonacci n Vanilla version is very slow – and only works on a few numbers n Remembering version quite fast – even faster when asking a second time n Single recursion very fast – but second time takes as long as first ? - time(fib(20, F)), time(fib 2(20, F)), time(fib_mem(20, F)).
Looking at Code Program can inspect its own code n clause/2 returns clauses of program n – 1 st argument is the head of the rule – 2 nd argument is the body – at least one argument must be given
Clause/2 sibs(Sib 1, Sib 2) : - parent(Sib 1, P), parent(Sib 2, P), different(Sib 1, Sib 2). different(X, Y) : - X = Y. ? - clause(sibs(_, _), Body). Body = parent(_1, _2), parent(_3, _2), different(_1, _3) ? - clause(Head, _A = _B). Head = different(_1, _2) Note: this last one doesn’t seem to work in SWI-Prolog
Compound Bodies n Body is a term – a comma term ? - (a, b, c, d) = (X, Y). X=a Y = b, c, d n Needs to be in parentheses when 2 nd arg. ? - clause(sibs(_, _), (parent(_, _) ). Yes Is there a clause of sibs/2 with a body like parent(_, _), …
Bodies of Facts n Puts true/0 in for body of fact parent(mark, alex). parent(bob, mark). parent(X, brian) : - parent(X, mark). ? - clause(parent(X, Y), Body). X = mark, Y = alex, Body = true ; X = bob, Y = mark, Body = true ; X = _1, Y = brian, Body = parent(_1, mark)
Looking at Built-in Code n Some built-in predicates are available ? - clause(member(X, Y), Body). X = _1, Y = [_1|_2], Body = true ; X = _1, Y = [_2|_3], Body = member(_1, _3) n Others are hidden ? - clause(A, B), Body). ERROR: No permission to access private_procedure `clause/2'
Modifying Code n Replace every rule of the form – H : - …, different(X, Y), … n with – H : - …, X = Y, … (where the other bits don’t change) n Maybe for efficiency n – H should be specified (else errors ensue)
Method for Modifying Code n Find a rule with matching head – Note: predicate must be dynamic n If it has a different(X, Y) in it, – replace it with X = Y – retract old rule – assert new rule
Modification Predicate modify_rule(Head, Old, New) : clause(Head, Body), replace_code(Body, Old, New. Body), retract(Head : - Body), assert(Head : - New. Body). replace_code((Old, More), Old, New, (New, More)). replace_code((H, Old. T), Old, New, (H, New. T)) : replace_code(Old. T, Old, New. T). replace_code(Old, New, New).
Modifying Code ? - modify_rule(sib(_, _), different(X, Y), X=Y). before: sib(A, B) : parent(A, C), parent(B, C), different(A, B). n after: sib(A, B) : parent(A, C), parent(B, C), A = B. Problem – only modifies one clause!
Modifying All Clauses n Prolog prints bindings – ask for another solution – Prolog backs up to clause/2 & gets another rule – then changes that rule n Keep asking for answers until all done – it works – will stop when no rule has Old in it – it’s sort of like a loop
“Failure Driven Loops” n Need to make a change, then fail – Prolog will try another clause n Have predicate make a change, then fail – it will change all the rules before saying No n Says No even tho’ it worked! – if we put + in front, it’ll say Yes instead! – that’s more intuitive for user
Modify All modify_all(Head, Old, New) : + modify_and_fail(Head, Old, New) : modify_rule(Head, Old, New), fail. OR Just modify_all(Head, Old, New) : + (modify_rule(Head, Old, New), fail).
Remaining Problem n too_different/3 is not fully changed – replace_code only changes first Old to New – backtracking doesn’t pick up new clause n Try to fix by making replace_code change all – unexpected side-effect: all X=Y made the same – too_different(A, A, A) : - A=A, A=A. n Too complicated for us!
Next Time n Definite Clause Grammars