Presentation is loading. Please wait.

Presentation is loading. Please wait.

Logic Programming (cont’d): Lists

Similar presentations


Presentation on theme: "Logic Programming (cont’d): Lists"— Presentation transcript:

1 Logic Programming (cont’d): Lists
Example 2: Determining the order of specific times In logic programming, symbols have no values: ‘2’ and ‘9’ are names and not numbers. We cannot use the < relation to compare the order of numbers (it is infinite!). However, a finite ordered relation may be represented by positions in lists. We would like to implement an ordered relation between pairs of the form (hour, weekday). % 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 בתכנות הלוגי, לסמלים הדקדוקיים אין ערך. הסמלים 2 וְ-9 נקראים כשמות ולא כמספרים בעלי ערך. מסיבה זו, לא נוכל לייצג יחס סדר כמו > כיוון שהיחס הוא אינסופי, לא נוכל לציין את אוסף העובדות שמגדירות את הסדר על המספרים. עם זאת, כאשר מדובר במספר סופי של ערכים, ניתן להשתמש במיקום שלהם ברשימה מוגדרת מראש על מנת להגדיר סדר ביניהם. לשם כך, אנחנו מגדירים ADT של Hour ושל Weekday הכולל: בנאי: נייצג שעה על ידי הפנקטור h. נשים לב: מדובר במוסכמה שלנו ואין לה ביטוי בתוכנית פרט לעצם השימוש בפנקטור. ניתן לחשוב על הפנקטור כעל "בנאי" של ערכים מאותו טיפוס. פרדיקט מזהה ייצוג השעות האפשריות יחס-סדר הערות: תכנות לוגי הוא מודל תיאורטי של תכנות. לעומתו, PROLOG מבוססת על הרעיון של תכנות לוגי אך מרחיבה אותו. לדוגמא, היא כוללת מרכיב א-לוגי המאפשר להתייחס לסימבולים של מספרים כמספרים ומאפשר לבצע חישובים אריתמטיים. בהינתן תמיכה בפעולות אריתמטיות, ניתן לתמוך בפעולות כמו >, < וכו'. אפשרות אחרת היא לייצג מספרים על ידי church numerals ושימוש בפנקטורים. במקרה כזה, יתכן שנוכל להגדיר יחס סדר ביניהם. % 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

2 Logic Programming (cont’d): Lists
Example 2: Determining the order of specific times In logic programming, symbols have no values: ‘2’ and ‘9’ are names and not numbers. We cannot use the < relation to compare the order of numbers (it is infinite!). However, a finite ordered relation may be represented by positions in lists. We would like to implement an ordered relation between pairs of the form (hour, weekday). % Signature: hour_list(List)/1 % Purpose: Holds the ordered list of days 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: 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')]). כדי לייצג את כל השעות האפשריות, אנחנו משתמשים ביחס חד-מקומי הממומש על ידי עובדה יחידה. עובדה זו מגדירה את רשימת כל השעות הקיימות.

3 Logic Programming (cont’d): Lists
Example 2: Determining the order of specific times 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)]). weekday_list([d('Sun'),d('Mon'),d('Tue'),d('Wed'),d('Thu'),d('Fri'),d('Sat')]). % Signature: is_hour?(Hour)/1 % Purpose: Succeeds iff Hour is an hour of the weekday. is_hour(h(H)) :- hour_list(Hour_list), member(h(H),Hour_list). A predicate to identify hours: % 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). An order relation between hours: בהינתן העובדה המגדירה את רשימת השעות, אנו יכולים לממש את הפרדיקט hour_list: "h(H) היא שעה, כלומר מקיימת את is_hour, אם קיימת רשימת שעות Hour_list וגם h(H) נמצאת בה." נשים לב: אנו זקוקים לתנאי hour_list(Hour_list) כיוון שרק כך נוכל להתייחס לרשימה בה עלינו לבדוק האם השעה הנקובה קיימת. בתהליך היוניפיקציה, המשתנה Hour_list יוחלף באותה רשימה ובפיתוח עץ ההוכחה זו תהיה הרשימה עבורה נבדוק האם member(h(H),Hour_list). דבר דומה מתרחש במימוש הפרדיקט hour_order: "h(H1) קודמת ל - h(H2) אם שתיהן שעות וגם קיימת רשימה שעות Hour_list וגם h(H1) מופיעה לפני h(H2) ברשימה."

4 Logic Programming (cont’d): Lists
Example 2: Determining the order of specific times 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)]). weekday_list([d('Sun'),d('Mon'),d('Tue'),d('Wed'),d('Thu'),d('Fri'),d('Sat')]). % Signature: is_weekday?(Day)/1 % Purpose: Success iff Weekday is a weekday of the week. is_weekday?(d(D)) :- weekday_list(Weekday_list), member(d(D),Weekday_list). A predicate to identify weekdays: % 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). An order relation between weekdays: The predicate ‘precedes’ is defined using the built-in relation ‘append/3’: % 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).

5 Logic Programming (cont’d): Lists
Example 2: Determining the order of specific times % Type: Time. % Signature: time(Hour,WeekDay)/2 % Example: time(h(18),d(Tue)). % 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)). A predicate to identify times: % 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))) :- 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))) :- is_time(time(h(H1),d(D))), is_time(time(h(H2),d(D))), hour_order(h(H1),h(H2)). An order relation between times: היחס time_order: במקרה ששני הזמנים הם בימים שונים, מספיק לבדוק את יחס הסדר לפי החלק המציין את היום. אחרת, מדובר בזמנים באותו יום ואז יש לבדוק את יחס הסדר לפי החלק המציין את השעה ביום. נשים לב: כאשר D1!=D2, הכלל הראשון של time_order ייכשל כיוון שהיוניפיקציה לא תצליח עבור החלק האחרון שבגוף הכלל.

6 Logic Programming (cont’d): Lists
Example 4: Merging sorted lists % Signature: lt(Obj1,Obj2)/2 % Purpose: Obj1 precedes Obj2 by some comparison criteria. lt(Time1,Time2) :- time_order(Time1,Time2). % Signature: merge(Xs,Ys,Zs)/3 % Purpose: Zs is the sorted merge of the sorted lists Xs and Ys. Assumes % a predicate "lt" of order between 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(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'))] הערה: המימוש של יחס הסדר הכללי lt הוא רק מימוש אפשרי אחד. ניתן להוסיף מימושים שיכלילו את יחס הסדר lt על ידי הוספת כללים מתאימים. נקרא את הכלל הראשון באופן הבא: רשימת ה-Z-ים, ובראשה האיבר הראשון ברשימת ה–X-ים, היא מיזוג של שתי הרשימות האחרות אם: ראש רשימת ה-X-ים קטן מראש רשימת ה-Y-ים וגם באופן רקורסיבי, שארית ה-Z-ים היא מיזוג של שארית ה-X-ים ושל ה-Y-ים.

7 Logic Programming (cont’d): Lists
Example 5: Merging sorted lists ?-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) merge([X|Xs],[Y|Ys],[X|Zs]) :- lt(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]) :- lt(Y,X), merge([X|Xs],Ys,Zs). merge(Xs,[],Xs). merge([],Ys,Ys). 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 2 – failure branch… 3 – failure branch… lt(t1,t2), merge([t3,t5],[t2,t3],Zs_1) * merge([t3,t5],[t2,t3],Zs_1) 2 – 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) * הניסיון להוכיח את המטרה השלישית המודגשת בעץ נכשל כשמנסים להשתמש בכלל הראשון ובכלל השני. עבור הכלל הראשון, ההוכחה תיכשל כאשר לא יתקיים lt(t3,t2). עבור הכלל השני, ההוכחה תיכשל כבר בניסיון לבצע יוניפיקציה של המטרה עם ראש הכלל (t3!=t2). merge([t3,t5],[t3],Zs_2) 1 – failure branch… {X_3=t3,Xs_3=[t5], Ys_3=[],Zs_2=[t3,t3|Zs_3]} 2 3 – failure branch… merge([t5],[],Zs_3) true {Xs_4=[t5], Zs_3=[t5]} 4

8 Logic Programming (cont’d): Backtracking optimization
Example 7: Optimize the procedure merge. When none of the first two params is [], only one of the rules can be valid, Since either lt(Y,X), or lt(X,Y) or X==Y holds, exclusively. Once one of the above holds, we can skip further rule selections. merge([X|Xs],[Y|Ys],[X|Zs]) :- lt(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]) :- lt(Y,X), merge([X|Xs],Ys,Zs). merge([t1,t3,t5],[t2,t3],Xs) lt(t1,t2), !, merge([t3,t5],[t2,t3],Zs_1) 3 – failure branch… אינטואיטיבית, השימוש באופרטור בגוף כלל של יחס A פירושו: "אנו מרוצים מההצבה הנוכחית" ומעוניינים להמשיך את ההוכחה רק איתה. לא ננסה להשתמש בכללים של A המופיעים מאוחר יותר (חיתוך הענפים מימין באותה רמה). לא נבצע backtracking לשאילתות בגוף הכלל הנוכחי המופיעות לפני האופרטור. נתייחס רק לשארית המטרות בגוף הכלל וננסה להוכיחן תחת ההצבה הנוכחית. במקרה של כישלון: אם לא הצלחנו להשתמש בהצבה כפי שהיא, ההוכחה נכשלה ולא ננסה הצבה אחרת. במקום זאת, נוכל לנסות לחזור לשלב לפני היוניפיקציה עם הכלל בו הופיע האופרטור. * 2 – failure branch… !, merge([t3,t5],[t2,t3],Zs_1) merge([t3,t5],[t2,t3],Zs_1)

9 Logic Programming (cont’d): Backtracking optimization
The cut operator (!): Used to avoid redundant computations. Rule k: A :- B1, …Bi, !, Bi+1, …, Bn. Rule k+1: A :- … . ?- 𝐆 𝐪 , A, 𝑸 𝟏 , …, 𝑸 𝒎 . ?- A, 𝑸 𝟏 , …, 𝑸 𝒎 . Rule k Following selections of A rules are cut. ?- B1,…, Bi, !, Bi+1, …Bn,Q1’,…Qm’ * Further substitutions for goals before ‘!’ are cut. ?- !, Bi+1’, …Bn’,Q1’’,…Qn’’ ניתן לסכם את השפעת האופרטור בשני כללים: ברגע שהגענו ל-!, לא ייעשה שימוש בכללים המופיעים אחרי הכלל הנוכחי בתכנית. לא ננסה אפשרויות נוספות (backtracking) עבור מטרות שנמצאות משמאל לאופרטור. נשים לב: ה – cut לא משפיע על הצמתים שמופיעים אחריו, הם ימשיכו לייצר פתרונות כרגיל (Bi+1 והלאה..). עם זאת, במידה שאחד מבין Bi+1 והלאה ייכשל, יוכרז כישלון עבור כל A והחיפוש יימשך רמה אחת מעליו. כלומר, תיבדקנה אפשרויות נוספות עבור Gq. הערה: הגרשיים מציינים את חלקי השאילתה לאחר שהופעלה עליה ההצבה אשר הגענו אליה עד כה. ?- Bi+1’, …Bn’,Q1’’,…Qn’’

10 Logic Programming (cont’d): Backtracking optimization
Example 8: red VS green cuts. Green cut: Helps optimizing the program by avoiding redundant computations. Red cut: Omits possible solutions. Red cuts are undesired, unless specifically required (E.g., to provide only the first solution). Q: How many possible solutions does the following query have? ?- merge([],[],X). X = []; X = [] The query matches both facts 4 and 5: merge(Xs,[],Xs). merge([],Ys,Ys). Cut ירוק: האפשרויות שנחתכות ממילא לא היו מספקות פתרונות. מספר הפתרונות אינו משתנה, מדובר באופטימיזציה. Cut אדום: האפשרויות שנחכתות מספקות פתרונות. השימוש באופרטור משנה את מספר הפתרונות. To avoid this, we add ‘!’ to fact 4, rewriting it as a rule: merge(Xs,[],Xs):-!. merge([],Ys,Ys). A red cut! The number of solutions has changed!

11 LP Interpreter in Scheme: System description
שכבת syntax: בשכבה זו מודול יחיד, LP-AST, המגדיר ממשק נח לביטויים בתכנית LP. המודול כולל ADT לכל קטגוריה תחבירית בשפה. שכבת ADTs: המערכת משתמשת בשני ADTs, Substitution וְ – Term-equation המשמשים למימוש פעולת היוניפיקציה באלגוריתם Unify. בנוסף, ישנו ADT עבור "עץ-עצל": העץ מתויג, כלומר לכל צמת בעץ יש label, ולכל צומת בעץ n קדקדים. העומק של כל צומת יכול להיות אינסופי. לכן, הבנאי של העץ מספק את הבנים שלו בצורה עצלה, בתוך lambda. זאת, בדומה לשימוש ב – lambda ברשימות עצלות. השכבה העליונה, LP-Solver: כוללת את המודול Answer-Query המגדיר את הפונקציות Gsel, Rsel ואת האלגוריתם לבניה עץ ההוכחה.

12 LP Interpreter in Scheme: LP-AST
A programs is represented as a list of the abstract representations of its procedures. Note that this list actually represents a set. 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). % Signature: member(Element, List)/3 % Purpose: Element is a member in List. member(X, Ys) :- append(Zs, [X|Xs], Ys). Is represented as: ( ((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)) )) )

13 LP Interpreter in Scheme: LP-AST
A programs is represented as a list of the abstract representations of its procedures. Note that this list actually represents a set. Example2: (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))) Represents: ;% Signature: part(Name). ;part(a). ;part(b). ;part(c).

14 LP Interpreter in Scheme: Lazy-Tree
Represented as a “lazy list of trees“. The head is the root and the tail is a regular list of lazy-trees: (root (lambda () (list lzt1 lzt2 ... lztn))) The empty lazy-tree is represented by: empty-lzt A leaf is represented by: (root (lambda () empty-lzt)) Let n represent a node, and lzt1, ..., lztn represent lazy-trees, then the tree above is given by: (make-lzt n (lambda () (list (make-lzt n1 (lambda () (make-lzt ...))) (make-lzt n2 (lambda () (make-lzt ...))) (make-lzt nm (lambda () (make-lzt ...)))) )) עץ עצל ממומש על ידי "רשימה עצלה של עצים": ראש הרשימה הוא שורש העץ זנב הרשימה הוא רשימה רגילה של עצים עצלים. הערה: כפי שניתן להסיק מההגדרה, עץ עצל הוא סופי במספר הענפים שיכולים להיות בכל רמה, אך יתכן שיהיה בעל עומק אינסופי.

15 LP Interpreter in Scheme: Lazy-Tree-ADT
Constructors: make-lzt make-lzt-leaf empty-lzt expand-lzt (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 ... node-expander: הקלט עבורה הוא node (שורש עץ) והיא מייצרת רשימה של ערכים אשר יתכן שהם תלויים בערך השורש. expand-lzt, מייצרת עץ עצל: משתמשת ב – expander כדי לייצר רשימת ערכים אשר יתכן ויהיו תלויים בערך השורש. מייצרת עץ עצל המיוצג על ידי השורש, וכן ופונקציה אנונימית. בהפעלת הפונקציה האנונימית, הערכים שהתקבלו מהפעלת ה – expander משמשים כדי לייצר את רשימת העצים העצלים ברמה הבאה. כך, ניתן לפתח עץ על ידי הפעלות חוזרות של node-expander על הצאצאים בכל שכבה.

16 LP Interpreter in Scheme: Lazy-Tree-ADT
Operations: lzt-filter(lzt, filterP) Returns a list of nodes that satisfy the predicate; does not terminate on infinite lazy trees. lzt-find-first(lzt, filterP) Returns the first node that satisfies the 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. דוגמאות לשימוש בעץ עצל ניתן לראות בחומר ההרצאות באתר הקורס ובקובץ lazy-tree-ADT-tests.rkt.

17 LP Interpreter in Scheme: Lazy-Tree-ADT
Example: (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)))) )) (define lzt1 (let ((kb '(10 20))) (expand-lzt 1 (lambda (label) (if (> label 12) empty (map (lambda (kbi)(+ label kbi)) kb)))))) בדוגמא זו, ה – expander מקבל ערך ואם הוא לכל היותר 12, מחזיר רשימה ובה הערך מחובר לכל איבר ברשימה (20 10). כאשר expand-lzt מקבלת את השורש, 1, ואת ה – expander היא מייצרת עץ עצל: רשימת ה – labels ברמה הבאה אחרי השורש מתקבלת מהפעלת ה – expander על השורש: (21 11). העץ העצל מיוצג על ידי זוג ובו האיבר הראשון הוא השורש, 1 והאיבר השני הוא פרוצדורה. בהפעלת הפרוצדורה, נקבל את רשימת העצים העצלים אשר שורשם (21 11). > (lzt->take-branches lzt1 2) '(1 (11 (21) (31)) (21))

18 LP Interpreter in Scheme: Answer-Query
Creates a proof tree as a lazy tree, whose nodes are labeled by a 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. (define LP-node-expander (lambda (PT-node program) (let ((Q (PT-node->query PT-node)) (S (PT-node->sub PT-node))) (if (success-query? Q) empty (let* ((selected-goal (Gsel Q)) (rule-subs (Rsel selected-goal program)) (new-queries (map (lambda (rule-sub) (expand-query Q selected-goal rule-sub)) rule-subs)) (new-subs (sub-combine S (rule-sub->sub rule-sub))) rule-subs))) (map make-PT-node new-queries new-subs)) ))

19 LP Interpreter in Scheme: Answer-Query
Input: ((G1, G2, …, Gn), empty-sub) Program S: empty-sub Q: (G1, G2, …, Gn) PT-node Gsel G1 Program Rsel ((R1,S1),..., (Rn,Sn)) new-queries new-subs ((B(R1),G2,...)○S1 ... (B(Rn),G2,...,)○Sn) (S○S1, ... S○Sn) PT-node PT-node PT-node PT-node ...


Download ppt "Logic Programming (cont’d): Lists"

Similar presentations


Ads by Google