CSC 324 Tutorial Week 11 ======================== Lists in Prolog =============== If you didn't get through all the lists stuff from last week (sections 3 and 4), please finish that off first. Question -------- Define a predicate allmax( +List, -Index, -Maximum) that given a list List holds if and only if Maximum is a maximum value in the list and Index is its index on the list (starting from 0). Your predicate should be able to find all maxima! That means if your list has more than one maximum, then pressing ';' after the first answer should provide all other possible answers (Index/Maximum combinations). For example, ?- allmax( [1,2,3,4,-3,4,2,1,4,0,4], Index, Max). Index = 3 Max = 4 ; Index = 5 Max = 4 ; Index = 3 Max = 4 ; Index = 8 Max = 4 ; Index = 3 Max = 4 ; Index = 5 Max = 4 ; Index = 3 Max = 4 ; Index = 10 Max = 4 ; No Answer ------ Start with just max of a list: max( [X], X). max( [H|L], X) :- max(L, SX), ( H >= SX, X = H ; H < SX, X = SX ). Add indexing: indmax( [X], 0, X). indmax( [H|L], N, X) :- indmax(L, SN, SX), ( H >= SX, X = H, N = 0 ; H < SX, X = SX, N is SN+1 ). And finally allow multiple indices to be returned: allmax( [X], 0, X). allmax( [H|L], N, X) :- allmax(L, SN, SX), ( H >= SX, X = H, N = 0 ; H =< SX, X = SX, N is SN+1 ). Question: Name a source of potential inefficiency? (A: after first rule is matched, on backtracking the second will be tried) How can this be fixed? (A: add a cut to the first rule: allmax( [X], 0, X) :- !.) Question: why do we get multiple copies of solutions back? (Prolog does allow us to remove the multiple copies if we want all solutions, using setof/3) Manipulating the Knowledge Base =============================== Normally you write the knowledge base as a set of facts and rules in some file (such as family.pl), and tell Prolog to "consult(family)." or "[family].". Recent versions of Prolog consider this data STATIC, and compiles it into a quickly accessible form. Perhaps you want to modify the knowledge base on-the-fly or without having to edit and reconsult the file. You can specify some relations as being DYNAMIC, permitting new facts and rules to be added, or let old facts and rules to be removed from the database. Declaring dynamic predicates (recall the /n specifies arity of the predicate): :- dynamic(parent/2). Asserting new facts about parent: ?- assert(parent(lucy,junior)). Yes Retracting old facts about parent: ?- retract(parent(albert, edward)). Yes With changes being made to the knowledge base, we might want to list everything that is defined. To list everything in the knowledge base: ?- listing. Negation by failure =================== Example. p(a). p(b). q(c). ?- \+p(X), q(X). No ?- q(X), \+p(X). X = c ; No Another example: bachelor(P) :- male(P), not(married(P)). male(henry). male(tom). married(tom). ?- bachelor(henry). Yes ?- bachelor(tom). No ?- bachelor(Who). Who = henry ; No ?- not(married(Who)). No The first three responses are correct and as expected. The answer to the fourth query might have been unexepected at first. But consider the goal ?- not(married(Who)) fails because for the variable binding Who = tom, the goal married(Who) succeeds, and so the negative goal fails. Thus, negative goals ?- not(g) with variables cannot be expected to produce bindings of the variables for which the goal g fails. Prolog Data Structures -- Binary Search Tree ============================================ We can represent a binary tree of integers as: tree(Root, Left, Right) where Left and Right are also binary trees such that all elements in Left are less than Root and all elements in Right are greater than or equal to Root. If a tree is null, it means that the tree is empty. Eg. tree(100, tree(50, null, null), tree(75, null, null)). tree(10, tree(6, tree(4, null, tree(5, null, null)), tree(8, null, null)), tree(12, null, null)). Write a predicate that traverses a binary search tree (as so represented) generating a sorted list of its contents. I.e., define traverse( +Tree, -SortedList) where Tree is a binary tree and SortedList is the sorted list of members of the tree (recall that the + prefix means Tree must be instantiated and the - prefix means SortedList need not be). Eg. ?- traverse(tree(100, tree(50, null, null), tree(75, null, null)), X). X = [50, 100, 75] ; No ?- traverse(tree(10, tree(6, tree(4, null, tree(5, null, null)), | tree(8, null, null)), tree(12, null, null)), X). X = [4, 5, 6, 8, 10, 12] ; No Solution: traverse(null, []). traverse(tree(Root, Left, Right), SortedList) :- traverse(Left, SortedListLeft), traverse(Right, SortedListRight), append(SortedListLeft, [Root | SortedListRight], SortedList).