220 likes | 328 Views
This resource delves into advanced topics in Prolog programming, focusing on lists, backtracking, and optimization techniques via the cut operator. It covers fundamental list structures, including empty lists, and how to construct sentences using context-free grammar (CFG) with Prolog. Learn how to implement date relations, merge ordered date lists, and enhance queries for greater efficiency using cuts. This guide is suitable for developers interested in enhancing their knowledge of Prolog and its powerful capabilities for logic programming.
E N D
Logic Programming – Part 2 Lists Backtracking Optimization (via the cut operator) Meta-Circular Interpreters
Lists – Basic Examples • [] – The empty list • [X,2,f(Y)] – A 3 element list • [X|Xs] – A list starting with X. Xs is a list as well. • Example - [3,5] may be written as [3|5|[]]
Lists – CFG with Prolog Question: Which sentences can be constructed using this grammar? s -> npvp np -> det n vp -> v np| v det -> a | the n -> woman | man v -> shoots
Lists – CFG with Prolog Lets make relations out of it: s(Z) :- np(X), vp(Y), append(X,Y,Z).np(Z) :- det(X), n(Y), append(X,Y,Z).vp(Z) :- v(X), np(Y), append(X,Y,Z).vp(Z) :- v(Z).det([the]).det([a]).n([woman]).n([man]).v([shoots]). s -> npvp np -> det n vp -> v np| v det -> a | the n -> woman | man v -> shoots
Lists – CFG with Prolog • We can ask simple queries like: • Prolog generates entire sentences! s([a,woman,shoots,a,man]).yes ?-s(X).X = [the,woman,shoots,the,woman] ; X = [the,woman,shoots,the,man] ;X = [the,woman,shoots,a,woman] ;X = [the,woman,shoots,a,man] ;X = [the,woman,shoots] … ?-s([the,man|X]). X = [the,man,shoots,the,woman] ;X = [the,man,shoots,the,man] ;X = [the,man,shoots,a,woman] …
Lists – CFG with Prolog • Question: Add a few rules to the grammar What should we change in the code? • Answer: we add the following code s -> npvp np -> det n | detadj n vp -> v np| v det -> a | the n -> woman | man v -> shoots adj -> vicious | marvelous np(Z) :- det(X), adj(W), n(Y), append([X,W,Y],Z). adj([vicious]). adj([marvelous]).
Lists – The date Relation • In this example we’ll work with dates • We assume, for simplicity that a date comprises of a week day and an hour • We define the possible week days and hours with lists: week_day(['Sun', 'Mon', 'Tue','Wed','Thu','Fri','Sat']). hour([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]).
Lists – The date Relation • Question: How can we tell if hour 2 is before hour 9? • Answer: • We can only do so by checking precedence in the lists above • A < relation isn’t really possible to implement (There’s a more detailed answer in the PS document) week_day(['Sun', 'Mon', 'Tue','Wed','Thu','Fri','Sat']). hour([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]).
Lists – The date Relation • Some queries: date([H,W]) :- hour(Hour_list), member(H, Hour_list), week_day(Weekday_list), member(W, Weekday_list). dateLT( date([_,W1]), date([_,W2]) ) :- week_day(Weekday_list), precedes(W1,W2,Weekday_list). dateLT( date([H1,W]), date([H2,W]) ) :- hour(Hour_list), precedes(H1,H2,Hour_list). date([1,'Sun']). true dateLT(date([5,'Mon']), date([1,'Tue'])). true
Lists – The date Relation • precedes is defined using append /2 • Notice that the first argument is a list of lists • This version of append is a strong pattern matcher precedes(X,Y,Z) :- append( [_,[X],_,[Y],_] , Z).
Lists – Merging date Lists • Merge 2 ordered date-lists % Signature: merge(Xs, Ys, Zs)/3 % purpose: Zs is an ordered list of dates obtained % by merging the ordered lists of dates Xs and Ys. merge([X|Xs] , [Y|Ys] , [X|Zs]) :- dateLT(X,Y), merge(Xs, [Y|Ys] ,Zs). merge([X|Xs] , [X|Ys] , [X,X|Zs]) :- merge(Xs, Ys, Zs). merge([X|Xs],[Y|Ys],[Y|Zs]) :- dateLT(Y,X), merge( [X|Xs] ,Ys, Zs). merge(Xs,[ ], Xs). merge([ ],Ys, Ys). ?- merge( [date([5,'Sun']), date([5,'Mon'])], X, [date([2, 'Sun']), date([5,'Sun']), date([5, 'Mon'])]). X = [date([2, 'Sun'])]
merge([d1,d3,d5],[d2,d3],Xs) {X_1=d1,Xs_1=[d3,d5], Y_1=d2,Ys_1=[d3],Xs=[d1|Zs_1] } Rule 1 dateLT(d1,d2), merge([d3,d5], [d2,d3] ,Zs_1) merge([d3,d5], [d2,d3] ,Zs_1) Rule 1 – failure branch… Rule 2 – failure branch… dateLT(d2,d3), merge([d3,d5], [d3] ,Zs_2) merge([d3,d5], [d3] ,Zs_2) Rule 1 – failure branch… { X_3=d3,Xs_3=[d5],Ys_3=[], Zs_2=[d3,d3|Zs_3] } Rule 2 merge([d5], [] ,Zs_3) true { Xs_4=[d5], Zs_3=[d5] } Fact 4 Rule 3 – failure branch… Rule 2 – failure branch… { X_2=d3,Xs_2=[d5], Y_2=d2,Ys_2=[d3], Zs_1=[d2|Zs_2]} Rule 3 Rule 3 – failure branch…
Backtracking Optimization - Cut The cut operator (denoted ‘!’) allows to prune trees from unwanted branches. • A cut prunes all the goals below it • A cut prunes all alternative solutions of goals to the left of it • A cut does not affect the goals to it’s right • The cut operator is a goal that always succeeds
merge([d1,d3,d5],[d2,d3],Xs) Rule 3 – failure branch… dateLT(d1,d2), !, merge([d3,d5], [d2,d3] ,Zs_1) Rule 2 – failure branch… !, merge([d3,d5], [d2,d3] ,Zs_1) merge([d3,d5], [d2,d3] ,Zs_1) Example - Merge with Cut • In the merge example, only 1 of the 3 first rules can be true. There is no reason to try to others. • Modify rule 1: merge([X|Xs] ,[Y|Ys], [X|Zs]) :- dateLT(X,Y), !, merge (Xs, [Y |Ys],Zs).
Another Example • How many results does this query return? • Why does this happen? The query fits both rules 4 and 5 • How can we avoid this? Add cut to rule 4 ?- merge([],[],X) . X = []; X = []; No merge(Xs, [ ],Xs) :- !.
Meta-Circular Interpreters • We have seen 3 different interpreters in class • Version 1 is trivial We can’t control the computation this way solve( A ) :- A.
Interpreter Version 2 clause finds the first rule unifying with A with body B • % Signature: solve(Goal)/1 • % Purpose: Goal is true if it is true when posed to the original program P. • solve(true). • solve( (A, B) ) :- solve(A), solve(B). • solve(A) :- A\=true, clause(A, B), solve(B). ?- clause( parent(X,isaac),Body). X = abraham Body = true ?- clause(ancestor(abraham, P),Body). P = Y, Body = parent(abraham, Y) ; P = Z, Body = parent(abraham, Y), ancestor(Y, Z)
solve(ancestor(abraham, P)) {<A_1 = ancestor(abraham, P)>} Rule 3 solve clause(ancestor(abraham, P), B_1), solve(B_1) { <B_1 = parent(abraham, P)>, <X_2 = abraham>, <Y_2 = P> } Rule 1 ancestor { <B_1 = parent(abraham,Y_2), ancestor(Y_2, P)> }Rule 2 ancestor solve(parent(abraham, P)) solve(parent(abraham,Y_2), ancestor(Y_2, P)) {<A_3 = parent(abraham,Y_2)> <B_3 = ancestor(Y_2, P>} Rule 2 solve {<A_3 = parent(abraham, P)>} Rule 3 solve solve( parent(abraham,Y_2)), solve(ancestor(Y_2, P)) clause(parent(abraham, P), B_3), solve(B_3). {<P = issac>, <B_3 =true>} Fact 1 parent {<A_4 = parent(abraham,Y_2)>} Rule 3 solve solve(true) clause(parent(abraham, Y_2), B_4), solve(B_4) solve(ancestor(Y_2, P)) Fact 1 solve true {<Y_2 = issac>, <B_4 =true>} Fact 1 parent solve(true) , solve(ancestor(issac, P)) {<P = issac>}
Interpreter Version 3 • In this version we control the goal selection order by using a stack of goals • Preprocessing – The given program is converted into a program with a single predicate rule • Queries are checked against the new program
Interpreter Version 3 % Signature: solve(Goal)/1% Purpose: Goal is true if it is true when posed to the original program P.1. solve(Goal) :- solve(Goal, []). % Signature: solve(Goal, Rest_of_goals)/21. solve([],[]).2. solve([],[G | Goals]):- solve(G, Goals).3. solve([A|B],Goals):- append(B, Goals, Goals1), solve(A, Goals1).4. solve( A, Goals) :- rule(A, B), solve(B, Goals). Sample converted program: %rule (Head, BodyList)/21. rule( member(X, [X|Xs] ), [] ).2. rule( member(X, [Y|Ys] ), [member(X, Ys)] ).
solve(member(X, [a, b, c])) {<Goal_1 = member(X, [a, b, c])>} Rule 1 solve solve(member(X, [a, b, c]), []) { <A_2 = member(X, [a, b, c]>, <Goals_1 = []> } Rule 4 solve rule(member(X, [a, b, c], B_2), solve(B_2, []) { <X_3=X>,<Y_3= a>,<Ys_3=[b, c]>,<B_2 = [member(X, [b,c])] > }Rule 2 rule { <X=a>,<X_3 = a>,<Xs_3=[b, c]>,<B_2 = []> } Rule 1 rule solve([],[]) solve([member(X, [b,c])], []) Rule 1 solve { <A_4= member(X, [b,c])>, <B_4=[]>, <Goals_4=[]> } Rule 3 rule true append([], [], Goals1_4), solve(member(X, [b,c]), Goals1_4). {<X=a>} { <Goals1_4=[]>} Rule of append solve(member(X, [b,c]), []). { <A_5=member(X,[b,c])>, <Goals_5=[]>} Rule 4 solve rule(member(X,[b,c]), B_5), solve(B_5, []) { <X=b>,<X_6 = b>, <Xs_6=[c]>,<B_5 = []> }Rule 1 rule solve([],[]) Rule 1 solve true {<X=b>}