נושאים: רשימות. 1..Scheme LP interpreter.2 עקרונות שפות תכנות אביב 2020. תרגול 12. תכנות לוגי חלק ב' 1. רשימות. רשימות בפרולוג: מיוצגות באמצעות,functor השקול לבנאי cons של.Scheme 1. ][ הוא קבוע המייצג את הרשימה ריקה. 2. [X Xs] - הוא ביטוי (term) המייצג רשימה ש- X הוא האיבר הראשון שלה, ו- Xs מייצג רשימה הכוללת את שאר האיברים. 3. רשימות באורך ידוע אפשר לייצג על ידי פירוט הביטויים המייצגים את אבריהן. למשל,.f(Y) קבוע 2, וביטוי X, מייצג רשימה בת שלושה איברים: משתנה ]X,2,f(Y)[ 4. הסימנים ) pipe ), פסיק וסוגריים מרובעים מהווים חלק מ" tostring " של הרשימות. למשל, [a A] הוא "tostring" של רשימה שהאיבר הראשון שלה הוא a והזנב הוא רשימת A. את הרשימה 5[ ]3, ניתן להציג בצורת ] []] [5.[3 דוגמה 1: מימוש דקדוק חסר הקשר בפרולוג. נתבונן בדקדוק הבא: S --> NP VP NP --> Det N VP --> V NP V Det --> a the N --> woman man V --> saw שאלה: מהי השפה המוגדרת על ידי דקדוק זה? לדוגמה, נוכל לגזור את המשפט man" a": woman saw the S --> NP VP --> Det N VP --> a N VP --> a woman VP --> a woman V NP --> --> a woman saw NP --> a woman saw Det N --> a woman saw the N --> --> a woman saw the man נמדל את הדקדוק באופן הבא: כל טרמינל יהפוך לקבוע, כל נון טרמינל למשתנה. רצף קטגוריות ייוצג כרשימה. כל קטגוריה הופכת לפרדיקט בן פרמטר אחד הטקסט שניתן לגזור בקטגוריה. את הטקסט עצמו נממש באמצעות רשימה של מילים.(terminals) 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). -1-
det([the]). det([a]). n([woman]). n([man]). v([saw]). אנו משתמשים בפרדיקט append/3 של פרולוג, המתאר את יחס השרשור בין שלוש רשימות האחרונה היא השרשור של שתי הראשונות. כדי להשתמש בתוכנית לגזירה, ניצור שאילתה מתאימה. למשל,?- s([a,woman,saw,a,man]). true?- s(x). X = [the, woman, saw, the, woman] ; X = [the, woman, saw, the, man] ; X = [the, woman, saw, a, woman] ; X = [the, woman, saw, a, man] ; X = [the, woman, saw]; "האם קיים משפט השייך לשפה של הדקדוק?": כלומר, אם השאילתה כוללת רק משתנים, החישוב מתפקד כ- generator עבור שאילתה זו יש 20 משפטים, מהם ראינו את חמשת הראשונים. של כל התשובות האפשריות.?- s([the,man X]). X = [saw, the, woman] ; X = [saw, the, man] ; X = [saw, a, woman] ; S --> NP VP NP --> Det N Det Adj N Adj --> vicious marvelous VP --> V NP V Det --> a the N --> woman man V --> saw נניח שהדקדוק כולל מספר כללים נוספים )השינויים מודגשים בקו תחתון(: np(z) :- det(x), adj(w), n(y), append([x,w,y],z). adj([vicious]). adj([marvelous]). נוסיף את הכלל והעובדות הבאים: -2-
של פרולוג, המתאר את יחס השרשור בין רשימות. הפרמטר הראשון השתמשנו בפרדיקט append/2 לפי הסדר. רשימה שמחברת את כל הרשימות הללו, והשני הוא רשימה של רשימות, למשל: alist([1, 2, 3, 4, 5]).?- alist(l), append([[1, 2], X, Y, [5]], L). L = [1, 2, 3, 4, 5], X = [], Y = [3, 4] ; L = [1, 2, 3, 4, 5], X = [3], Y = [4] ; L = [1, 2, 3, 4, 5], X = [3, 4], Y = [] ; false. לסיום: בחזרה לדיבוג. יש להיות זהירים מאוד בכתיבת הקוד, כי המשתנים בתכנות לוגי לא מוגדרים בשום מקום, ושימוש במשתנה לא מוגדר לא יגרור אזהרה. למשל, כתיבת תוכנית פשוטה find([elem Rest], Elem). find([something My_List], Elem) :- find (My_list, Elem) יכולה לגרום לבעיות, אם לא נזהר ונפספס כי My_list,My_List הם שני משתנים שונים לגמרי. כדאי להשתמש בדיבוג )הדפסות ו- trace (. במקרים שאינכם מבינים למה התוכנית לא עובדת. דוגמה 2: השוואת שעות בשבוע. דוגמה זו עוסקת בהשוואת זמנים המצוינים על ידי שעה ביממה ויום בשבוע. נציג ADT Type(,Abstract Data כלומר טיפוס חדש( המייצג זמן. נבנה שלושה ערכים: שעה, יום וזמן. נשתמש בפנקטורים בתור בנאי ערך:,h(Hour) time(hour, Weekday),d(Weekday) בהתאם. לצורך ההשוואה נספק רשימות סדורות של שעות ושל ימים. יחס הקדימות בין זמנים יקבע על פי מקומם ברשימות המסודרות של הזמנים. הפרדיקטים hour_list,weekday_list יתארו את הסדרים של ימי השבוע ושעות ביממה, בהתאמה. בתכנות הלוגי, בשונה משפות תכנות אחרות, לסמלים הדקדוקיים אין ערך. כלומר, הסמלים 2 ו- 9 נקראים כשמות, ואינם קשורים למספרים 2 ו- 9. מסיבה זו, אי אפשר להסתמך על יחס > בין מספרים. אילו היה מדובר ביחס סופי, אפשר היה לספק את כל העובדות עבורו )למשל, )0,3(<, )0,2(<, )0,1(<, אבל אנו נצטרך אינסוף עובדות בשביל כל המספרים האפשריים. לכן סדר השעות נקבע על ידי מיקומן ברשימת השעות. הערה: שפת פרולוג, בשונה מתכנות הלוגי, כוללת מרכיב א-לוגי של אריתמטיקה, המאפשר הכרה בסמלי מספרים כמספרים, ומאפשר חישובים אריתמטיים. % Type: Hour. % Constructor: functor h/1. % Signature: h(hour)/1. % Example: h(18). % Identifier: is_hour/1 % Set of elements: hour_list/1 % Order: hour_order/2-3-
% Signature: is_hour(hour)/1 % Purpose: Succeeds iff Hour is an hour of weekday. is_hour(h(h)) :- hour_list(hour_list), member(h(h),hour_list). % Signature: hour_list(list)/1 % Purpose: Holds the ordered list of weekday hours. hour_list([h(0),h(1),h(2),h(3),h(4),h(5),h(6),h(7),h(8), h(9),h(10),h(11),h(12),h(13),h(14),h(15),h(16),h(17), h(18),h(19),h(20),h(21),h(22),h(23)]). % Signature: hour_order(h1,h2)/2 % Purpose: hour H1 precedes the hour H2 in some weekday. hour_order(h(h1),h(h2)) :- is_hour(h(h1)), is_hour(h(h2)), hour_list(hour_list), precedes(h(h1),h(h2),hour_list). הפרדיקט precedes מוגדר על ידי שימוש ביחס השרשור הרב מקומי הבנוי בפרולוג: % Signature: precedes(a,b,list)/3 % Purpose: The element A precedes the element B in the List. precedes(a,b,list) :- append([_,[a],_,[b],_],list). הפרדיקט מזהה את כל האפשרויות על ידי,unification גם כש- A, B לא נתונים. אנו יכולים להוסיף גם :equals? equals(x, X). % Type: Weekday. % Constructor: functor d/1. % Signature: d(weekday)/1. % Example: d(tue). % Identifier: is_weekday/1 % Set of elements: weekday_list/1 % Order: weekday_order/2 % Signature: is_weekday(weekday)/1 % Purpose: Succeeds iff Weekday is a day of the week. is_weekday(d(d)) :- weekday_list(weekday_list), member(d(d),weekday_list). % Signature: weekday_list(list)/1 % Purpose: Holds the ordered list of week days. weekday_list([d(sun),d(mon),d(tue),d(wed),d(thu), d(fri),d(sat)]). % Signature: weekday_order(weekday1,weekday2)/2 % Purpose: Weekday1 precedes Weekday2 in some week. weekday_order(d(d1),d(d2)) :- is_weekday(d(d1)), is_weekday(d(d2)), weekday_list(weekday_list), precedes(d(d1),d(d2),weekday_list). -4-
% Type: Time. % Constructor: functor time/2. % Signature: time(hour,weekday)/2. % Example: time(h(18),d(tue)). % Identifier: is_time/1 % Order: time_order/2 % Signature: is_time(time)/1 % Purpose: Succeeds iff Time is an hour of some weekday. % Example:?- is_time(time(h(1),d(sun))). % true is_time(time(h(h),d(d))) :- is_hour(h(h)), is_weekday(d(d)). % Signature: time_order(t1,t2)/2 % Purpose: The time T1 precedes the time T2 in the week. % Example:?- time_order(time(h(5),d(mon)), % time(h(1),d(tue))). % true time_order(time(h(h1),d(d1)),time(h(h2),d(d2))) :- %1 is_time(time(h(h1),d(d1))), is_time(time(h(h2),d(d2))), weekday_order(d(d1),d(d2)). time_order(time(h(h1),d(d)),time(h(h2),d(d))) :- %2 is_time(time(h(h1),d(d))), is_time(time(h(h2),d(d))), hour_order(h(h1),h(h2)). מימשנו שלושה טיפוסים סופיים )24, 168 7, ערכים בהתאמה(. נשתמש בממשק הזה בהמשך. =\ הוא פרדיקט דוגמה 3 )חומר העשרה(: מחיקת איבר מרשימה. נממש פרוצדורה המגדירה את הקשר של מחיקת כל ההופעות של ביטוי מרשימה. הבנוי בפרולוג ופירושו שלילת = )המסמן.)unification כלומר ערכו true אם יוניפיקציה נכשלת. % Signature: delete(list,x,hasnoxs)/3 % Purpose: The list HasNoXs is the result of removing all % occurrences of X from the List. % Precondition: List should be bound. % Example:?- delete([2,3,2,4,5,2,4],2,x). % X = [3, 4, 5, 4] delete([],_,[]). delete([x Xs],Z,[X Ys]) :- X \= Z, delete(xs,z,ys). delete([x Xs],X,Ys) :- delete(xs,x,ys). דוגמה 4. מיזוג רשימות. נגדיר ממשק סדר של טיפוס מסוים, :less than % Signature: lt(obj1,obj2)/2 % Purpose: The object Obj1 precedes the object Obj2 % by some comparison criteria. -5-
lt(time1,time2) :- time_order(time1,time2). %1 למשל, נוכל לממש, נשתמש בממשק לבניית פרוצדורת לקוח למיזוג רשימות )למשל, כאחת מפעולת ה- mergesort (: ממוינות של ביטויים שקיים ביניהם יחס סדר % Signature: merge(xs,ys,zs)/3 % Purpose: Zs is the sorted merge of the sorted lists Xs and % Ys. The assumption is that there is a predicate % "lt" of order between the elements of Xs and Ys. merge([x Xs],[Y Ys],[X Zs]) :- lt(x,y), merge(xs,[y Ys],Zs). %1 merge([x Xs],[X Ys],[X,X Zs]) :- merge(xs,ys,zs). %2 merge([x Xs],[Y Ys],[Y Zs]) :- lt(y,x), merge([x Xs],Ys,Zs). %3 merge(xs,[ ],Xs). %4 merge([ ],Ys,Ys). %5?- merge([time(h(1),d(sun)),time(h(3),d(wed)), time(h(5),d(sat))], [time(h(2),d(sun)),time(h(3),d(wed))], Xs). Xs = [time(h(1), d(sun)), time(h(2), d(sun)), time(h(3), d(wed)), time(h(3), d(wed)), time(h(5), d(sat))]; false?- merge([time(h(5),d(sun)),time(h(5),d(mon))], X, [time(h(2),d(sun)),time(h(5),d(sun)), time(h(5),d(mon))]). X = [time(h(2),d(sun))] t1 = time(h(1), d(sun)) t2 = time(h(2), d(sun)) t3 = time(h(3), d(wed)) t5 = time(h(5), d(sat)) נסמן: -6-
merge([t1,t3,t5],[t2,t3],xs) {X_1=t1,Xs_1=[t3,t5], Y_1=t2,Ys_1=[t3], Xs=[t1 Zs_1]} 1 lt(t1,t2), merge([t3,t5],[t2,t3],zs_1) * merge([t3,t5],[t2,t3],zs_1) 3 failure branch 1 failure branch {X_2=t3,Xs_2=[t5], Y_2=t2,Ys_2=[t3], Zs_1=[t2 Zs_2]} 3 lt(t2,t3), merge([t3,t5],[t3],zs_2) * merge([t3,t5],[t3],zs_2) 1 failure branch {X_3=t3,Xs_3=[t5],Ys_3=[], Zs_2=[t3,t3 Zs_3]} 2 merge([t5],[],zs_3) 3 failure branch {Xs_4=[t5],Zs_3=[t5]} 4 true נבנה את התשובה מענף ההצלחה: {X_1=t1, Xs_1=[t3, t5], Y_1=t2, Ys_1=[t3], Xs=[t1 Zs_1]} º {X_2=t3, Xs_2=[t5], Y_2=t2, Ys_2=[t3], Zs_1=[t2 Zs_2]} º {X_3=t3, Xs_3=[t5], Ys_3=[], Zs_2=[t3, t3 Zs_3]} º {Xs_4=[t5], Zs_3=[t5]} /Xs = = {X_1=t1, Xs_1=[t3, t5], Y_1=t2, Ys_1=[t3], Xs=[t1 [t2 Zs_2]], X_2=t3, Xs_2=[t5], Y_2=t2, Ys_2=[t3], Zs_1=[t2 Zs_2]} º {X_3=t3, Xs_3=[t5], Ys_3=[], Zs_2=[t3, t3 Zs_3]} º {Xs_4=[t5], Zs_3=[t5]} /Xs = = {X_1=t1, Xs_1=[t3, t5], Y_1=t2, Ys_1=[t3], Xs=[t1 [t2 [t3, t3 Zs_3]]], X_2=t3, Xs_2=[t5], Y_2=t2, Ys_2=[t3], Zs_1=[t2 [t3, t3 Zs_3]], X_3=t3, Xs_3=[t5], Ys_3=[], Zs_2=[t3, t3 Zs_3]} º {Xs_4=[t5], Zs_3=[t5]} /Xs = -7-
= {X_1=t1, Xs_1=[t3, t5], Y_1=t2, Ys_1=[t3], Xs=[t1 [t2 [t3, t3 [t5]]]], X_2=t3, Xs_2=[t5], Y_2=t2, Ys_2=[t3], Zs_1=[t2 [t3, t3 [t5]]], X_3=t3, Xs_3=[t5], Ys_3=[], Zs_2=[t3, t3 [t5]], Xs_4=[t5], Zs_3=[t5]} /Xs = {Xs=[t1 [t2 [t3, t3 [t5]]]]} = {Xs=[t1, t2, t3, t3, t5]} = {Xs=[time(h(1), d(sun)), time(h(2), d(sun)), time(h(3), d(wed)), time(h(3), d(wed)), time(h(5), d(sat))]} דוגמה 5 )חומר העשרה(: בחירת איבר ברשימה. פרוצדורה המגדירה את הקשר של מחיקת ההופעה הראשונה של ביטוי מרשימה. % Signature: select(x,hasxs,onelessxs)/3 % Purpose: The list OneLessXs is the list X without % one occurrence of X. select(element, HasXs, OneLessXs) :- append([prefix, [Element], Suffix], HasXs), append(prefix, Suffix, OneLessXs).?- select(4,[2,3,2,4,5,2,4],x). X = [2, 3, 2, 5, 2, 4]; X = [2, 3, 2, 4, 5, 2]; false דוגמה 6 )חומר העשרה(: החלפת ערך בעץ. החלפת תוויות בעץ בינארי. 2 3 2 void void void void % Signature: replace(from,to,treefrom,treeto)/4 % Purpose: The binary tree TreeTo is result of replacing all % occurrences of From in binary tree TreeFrom by To. replace(_,_,void,void). replace(from,to,tree(node,lft,rht), tree(node1,lft1,rht1)) :- replace_help(from,to,node,node1), replace(from,to,lft,lft1), replace(from,to,rht,rht1). % Signature: replace_help(from,to,nodef,nodet)/4 % Purpose: If (NodeF = From) then NodeT is To % else NodeT is NodeF). -8-
replace_help(from,to,from,to). replace_help(from,_,nodef,nodef) :- From \= NodeF.?- replace(2,4,tree(2,tree(3,void,void),tree(2,void,void)),x). X = tree(4, tree(3, void, void), tree(4, void, void))?- replace(b,x,tree(a,tree(b,void,void), tree(c,tree(b,void,void),void)), tree(a,tree(c,void,void), tree(c,tree(c,void,void),void))). X = c אם נרצה להציג עץ בינארי כרשימה, נעשה את השינויים הבאים: העץ במקום Right) tree(node, Left, ייראה בתור Right] void,[node, Left, יהפוך להיות ][. אם נרצה עץ n -ארי ולא בינארי, נחליף את הקטע בקוד Right) (Left, ב- Children לרשימה של כל הילדים. נצטרך להוסיף פרוצדורה למעבר על העץ. LP-interpreter System description.2 The system consists of files (modules) in three layers: Syntax, ADTs, LP-solver. -9-
The overall system architecture is given in the following architecture diagram. 1. Syntax: This layer includes, so far, only an abstract syntax module LP-AST, which defines a convenient interface to all syntactic elements in an LP program. 2. ADTs: The LP related ADTs are Substitution and Term-equation, which are used for implementing a unification operation in Unify. lazy-tree-adt is an ADT of an n-ary labeled tree (has labels on internal nodes), whose depth might be not finite. Therefore, the constructor of such trees wraps the child-branches of a node with a lambda abstraction that enables laziness: Delays the construction of child-branches until requested. 3. LP-solver: The Answer-Query module, defines the LP Gsel and Rsel, and the algorithm for proof-tree construction and search. LP-AST A programs is represented as a list of the abstract representations of its procedures. Note that this list actually represents a set. For example, the program: Example1: % Signature: append(list1, List2, List3)/3 % Purpose: List3 is the concatenation of List1 and List2. append([], Xs, Xs). append([x Xs], Y, [X Zs] ) :- append(xs, Y, Zs). member(x, Ys) :- append(zs, [X Xs], Ys). Is represented as the list: ( ((append 3) (0 ((append empty (var Xs) (var Xs)) true)) (1 ((append (cons (var X) (var Xs)) (var Y) (cons (var X) (var Zs))) (append (var Xs) (var Y) (var Zs)))) ) ((member 2) (0 ((member (var X) (var Ys)) (append (var Zs) (cons (var X) (var Xs)) (var Ys)) )) ) Example2: ;% Signature: part(name). ;part(a). ;part(b). ;part(c). (define parta (make-fact '(part a))) (define partb (make-fact '(part b))) (define partc (make-fact '(part c))) (define part-proc (make-procedure (list parta partb partc))) ;red(a). ;green(b). ;yellow(c). -10-
(define reda (make-fact '(red a))) (define red-proc (make-procedure (list reda))) (define greenb (make-fact '(green b))) (define green-proc (make-procedure (list greenb))) (define yellowc (make-fact '(yellow c))) (define yellow-proc (make-procedure (list yellowc))) (define part-prog (make-program (list part-proc red-proc green-proc yellowproc))) Queries: ;?- part(x) (define query-partx (make-query (list '(part (var X))))) ;?- red(x) (define query-redx (make-query (list '(red (var X))))) ;?- part(x), red(x) (define query-part-redx (make-query (list '(part (var X)) '(red (var X))))) ;?- part(x), yellow(x) (define query-part-yellowx (make-query (list '(part (var X)) '(yellow (var X))))) For every syntactical category, the file includes an abstract-syntax ADT. For example, the compound-term abstract-syntax interface is: (define term->vars (lambda (term) (cond ((variable? term) (list term)) ((atomic-term? term) empty) ((compound-term? term) (flatmap term->vars (compound-term->args term)))))) Substitution-ADT The Substitution ADT and its operations: An adaptation of the substitution-adt module from the type inference system. The ADT consists of: Constructor: make-sub(variables,terms), which also checks for circularity. Getters: sub->variables, sub->terms, sub->get-var(sub,var) which returns the value of var, if defined, or error otherwise. Predicates: sub?, empty-sub?, non-empty-sub?, sub-equal? Operations: 1. extend-sub(sub,var,term) which extends sub with the binding var=term, 2. Application of a substitution to LP terms, atomic formulas and queries: sub-apply(sub,term), sub-apply-atomic-formula, sub-apply-query, sub-apply-rule 3. Restriction of a substitution: sub-restrict(sub, vars) 4. Substitution combination: sub-combine(sub1,sub2) -11-
Examples: > (sub-combine (make-sub '((var T7) (var T8)) '(Number [f (m (var T5) Number) (var T3)])) (make-sub '((var T5) (var T8)) '((var T7) Boolean))) '(sub ((var T5) (var T7) (var T8)) ((var T7) Number (f (m (var T7) Number) (var T3)))) > (sub-apply (make-sub '((var X)) '(1)) (make-compound-term 'f '((var X)))) '(f 1) Term-equation-ADT The term-equation ADT and its operations: An adaptation of the equation-adt module from the type inference system. The ADT consists of: Constructor: make-equation(term1, term2). Getters: equation->left, equation->right Predicates: equation? Unify The unification operation, for atomic formulas and for terms. This is an adaptation of the solve module from the type inference system. The unification algorithm uses the equationsolving method: 1. For atomic elements either compares if equal, different, or can create a substitution (non-circular); 2. For compound arguments with the same predicate or functor and the same arity, creates equations from corresponding elements, and repeats unification. Main procedure: unify-formulas. Equation solvers: solve-equations(equation-list), solve(equations, substitution) Helpers: unifiable-structure(equation), split-equation(equation) Examples: (test (unify-formulas 'true 'true) => '(sub () ())) (test (unify-formulas '(member (f (var X1)) (cons (f 2) empty)) '(member (var X) (var L))) => '(sub ((var L) (var X)) ((cons (f 2) empty) (f (var X1))))) Lazy-Tree-ADT A lazy tree is represented as a "lazy tree-list" whose head is the root-node and whose tail is a regular list of lazy-trees: (root (lambda () (list lzt1 lzt2... lztn))) This is a lazy representation for labeled trees with finite branching, but possibly infinite depth. -12-
empty-lzt represents the empty lazy-tree A leaf is represented by: (root (lambda () empty-lzt)) If n represents a node, and lzt1...lztn represent lazy-trees, then (make-lzt n (lambda () (list (make-lzt n1 (lambda () (make-lzt...))) (make-lzt n2 (lambda () (make-lzt...))) (make-lzt nm (lambda () (make-lzt...)))) )) represents the above lazy-tree. The ADT consists of: Constructors: make-lzt, empty-lzt, expand-lzt(node, node-expander). (define expand-lzt (lambda (root node-expander) (let ((child-nodes (node-expander root))) (make-lzt root (lambda () (map (lambda (node) (expand-lzt node node-expander)) child-nodes)))) )) Getters: lzt->root, leaf-data, lzt->branches, lzt->first-branch, lzt->rest-branches, lzt->take-branches(lzt,n), lzt->nth-level(lzt, n) Predicates: empty-lzt?, lzt?, composite-lzt? Operations: There are three procedures for scanning a lazy tree: lzt-filter(lzt, filterp) returns a list of nodes that satisfy the filter predicate; does not terminate on infinite lazy trees. (define lzt-filter (lambda (lzt filterp) (letrec ((collect (lambda (lzt) (let ((children (flatmap collect (lzt->branches lzt)))) (if (filterp (lzt->root lzt)) (cons (lzt->root lzt) children) children))))) (if (empty-lzt? lzt) empty (collect lzt))))) lzt-find-first(lzt, filterp) returns the first node that satisfies the filter predicate. Might not terminate for infinite lazy trees. lzt-filter->lzl(lzt, filterp) returns a lazy list of all nodes that satisfy the filter predicate. You can see examples of lazy tree usages in the course site, Class material, lazy-tree-adt-tests.rkt. -13-
Answer-query This is the main module, in which the answer-query algorithm is implemented, relying on Gsel and Rsel procedures. The main procedures in this module are: answer-query, which has two variants: answer-query-first and answer-query-lzl LP-node-expander, expand-query Gsel, Rsel Answer-query creates a proof tree as a lazy tree, whose nodes are labeled by a list of: Query and a substitution. The substitution is already the combination of all substitutions on the tree branches. The nodes of the proof tree are defined as the data structure PT-node, with the getters: PT-node->query and PT-node->sub. The proof tree is created using the expand-lzt constructor of lazy trees, using the procedure LP-node-expander, which performs the main actions of the LP interpreter: 1. Applying Gsel on the query. 2. Applying Rsel on the selected goal. 3. Creating the new queries for the child node. 4. Creating the new combined substitutions for the child nodes. (define LP-node-expander (lambda (PT-node program) (let ((query (PT-node->query PT-node)) (sub (PT-node->sub PT-node))) (if (success-query? query) empty (let* ((selected-goal (Gsel query)) (rule-subs (Rsel selected-goal program)) (new-queries (map (lambda (rule-sub) (expand-query query selected-goal rule-sub)) rule-subs)) (new-subs (map (lambda (rule-sub) (sub-combine sub (rule-sub->sub rule-sub))) rule-subs))) (map make-pt-node new-queries new-subs)))) )) ; Signature: expand-query(query, goal, rule-sub) ; Type: [Query * AtomicFormula * RuleSub -> Query] ; Purpose: Given a rule-sub (rule sub) ; and a query (G1... Gi-1 Goal Gi+1... Gn) ; where rule is ( Head -> Body ) ; and Unify(Goal, Head) = sub ; compute [G1... Gi-1 Body Gi+1... Gn] o sub (define expand-query (lambda (query goal rule-sub) (let ((prefix-suffix (split-list (query->goals query) goal))) -14-
(sub-apply-query (rule-sub->sub rule-sub) (make-query (append (car prefix-suffix) (rule->body (rule-sub->rule rule-sub)) (cdr prefix-suffix))))))) -15-