Sei sulla pagina 1di 80

1

Module 5 Prolog

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

Abstract Data Types (ADTs) in Prolog


%%%%%%%%%%%%%%%%%%% stack operations %%%%%%%%%%%%%%% % These predicates give a simple, list based implementation of stacks % empty stack generates/tests an empty stack % BUILT IN TO SWI PROLOG %member(X,[X|T]). %member(X,[Y|T]):-member(X,T). empty_stack([]). % member_stack tests if an element is a member of a stack member_stack(E, S) :- member(E, S). % stack performs the push, pop and peek operations % to push an element onto the stack % ?- stack(a, [b,c,d], S). % S = [a,b,c,d] % To pop an element from the stack % ?- stack(Top, Rest, [a,b,c]). % Top = a, Rest = [b,c] % To peek at the top element on the stack % ?- stack(Top, _, [a,b,c]). % Top = a stack(E, S, [E|S]).

42 %%%%%%%%%%%%%%%%%%%% queue operation %%%%%%%%%%%%%% % These predicates give a simple, list based implementation of FIFO queues % empty queue generates/tests an empty queue

empty_queue([]). % member_queue tests if an element is a member of a queue member_queue(E, S) :- member(E, S). % add_to_queue adds a new element to the back of the queue add_to_queue(E, [], [E]). add_to_queue(E, [H|T], [H|Tnew]) :- add_to_queue(E, T, Tnew). % remove_from_queue removes the next element from the queue % Note that it can also be used to examine that element % without removing it remove_from_queue(E, [E|T], T). append_queue(First, Second, Concatenation) :append(First, Second, Concatenation).

%%%%%%%%%%%%%%%%%%%% set operations %%%%%%%%%%%%%%% % These predicates give a simple, list based implementation of sets % empty_set tests/generates an empty set. empty_set([]). member_set(E, S) :- member(E, S). % add_to_set adds a new member to a set, allowing each element % to appear only once add_to_set(X, S, S) :- member(X, S), !. add_to_set(X, S, [X|S]). remove_from_set(E, [], []).

43 remove_from_set(E, [E|T], T) :- !. remove_from_set(E, [H|T], [H|T_new]) :remove_from_set(E, T, T_new), !. % BUILT IN TO SWI PROLOG /* union([], S, S). union([H|T], S, S_new) :union(T, S, S2), add_to_set(H, S2, S_new). intersection([], _, []). intersection([H|T], S, [H|S_new]) :member_set(H, S), intersection(T, S, S_new),!. intersection([_|T], S, S_new) :intersection(T, S, S_new),!. */ set_diff([], _, []). set_diff([H|T], S, T_new) :member_set(H, S), set_diff(T, S, T_new),!. set_diff([H|T], S, [H|T_new]) :set_diff(T, S, T_new), !. subset([], _). subset([H|T], S) :member_set(H, S), subset(T, S). equal_set(S1, S2) :subset(S1, S2), subset(S2, S1). %%%%%%%%%%%%%%%%% priority queue operations %%%%%%%%%% % These predicates provide a simple list based implementation of a priority queue. % They assume a definition of precedes for the objects being handled empty_sort_queue([]). member_sort_queue(E, S) :- member(E, S). insert_sort_queue(State, [], [State]).

44 insert_sort_queue(State, [H | T], [State, H | T]) :precedes(State, H). insert_sort_queue(State, [H|T], [H | T_new]) :insert_sort_queue(State, T, T_new). remove_sort_queue(First, [First|Rest], Rest).

Semantic Nets and Frames in Prolog

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

Alternative search strategies


Basic depth first search algorithm go(Start, Goal) :empty_stack(Empty_been_list), stack(Start, Empty_been_list, Been_list), path(Start, Goal, Been_list). % path implements a depth first search in PROLOG % Current state = goal, print out been list path(Goal, Goal, Been_list) :reverse_print_stack(Been_list). path(State, Goal, Been_list) :mov(State, Next), % not(unsafe(Next)), not(member_stack(Next, Been_list)), stack(Next, Been_list, New_been_list), path(Next, Goal, New_been_list), !. reverse_print_stack(S) :empty_stack(S). reverse_print_stack(S) :stack(E, Rest, S), reverse_print_stack(Rest), write(E), nl. Breadth first search algorithm state_record(State, Parent, [State, Parent]). go(Start, Goal) :empty_queue(Empty_open), state_record(Start, nil, State), add_to_queue(State, Empty_open, Open), empty_set(Closed), path(Open, Closed, Goal). path(Open,_,_) :- empty_queue(Open), write('graph searched, no solution found'). path(Open, Closed, Goal) :remove_from_queue(Next_record, Open, _),

69 state_record(State, _, Next_record), State = Goal, write('Solution path is: '), nl, printsolution(Next_record, Closed). path(Open, Closed, Goal) :remove_from_queue(Next_record, Open, Rest_of_open), (bagof(Child, moves(Next_record, Open, Closed, Child), Children); Children = []), add_list_to_queue(Children, Rest_of_open, New_open), add_to_set(Next_record, Closed, New_closed), path(New_open, New_closed, Goal),!. moves(State_record, Open, Closed, Child_record) :state_record(State, _, State_record), mov(State, Next), % not (unsafe(Next)), state_record(Next, _, Test), not(member_queue(Test, Open)), not(member_set(Test, Closed)), state_record(Next, State, Child_record). printsolution(State_record, _):state_record(State,nil, State_record), write(State), nl. printsolution(State_record, Closed) :state_record(State, Parent, State_record), state_record(Parent, Grand_parent, Parent_record), member(Parent_record, Closed), printsolution(Parent_record, Closed), write(State), nl. add_list_to_queue([], Queue, Queue). add_list_to_queue([H|T], Queue, New_queue) :add_to_queue(H, Queue, Temp_queue), add_list_to_queue(T, Temp_queue, New_queue). Best first search algorithm %%%%% operations for state records %%%%%%% % These predicates define state records as an adt % A state is just a [State, Parent, G_value, H_value, F_value] tuple. % Note that this predicate is both a generator and % a destructor of records, depending on what is bound

70 % precedes is required by the priority queue algorithms state_record(State, Parent, G, H, F, [State, Parent, G, H, F]). precedes([_,_,_,_,F1], [_,_,_,_,F2]) :- F1 =< F2. % go initializes Open and Closed and calls path go(Start, Goal) :empty_set(Closed), empty_sort_queue(Empty_open), heuristic(Start, Goal, H), state_record(Start, nil, 0, H, H, First_record), insert_sort_queue(First_record, Empty_open, Open), path(Open,Closed, Goal). % Path performs a best first search, % maintaining Open as a priority queue, and Closed as a set. % Open is empty; no solution found path(Open,_,_) :empty_sort_queue(Open), write("graph searched, no solution found"). % The next record is a goal % Print out the list of visited states path(Open, Closed, Goal) :remove_sort_queue(First_record, Open, _), state_record(State, _, _, _, _, First_record), State = Goal, write('Solution path is: '), nl, printsolution(First_record, Closed). % The next record is not equal to the goal % Generate its children, add to open and continue % Note that bagof in AAIS prolog fails if its goal fails, % I needed to use the or to make it return an empty list in this case path(Open, Closed, Goal) :remove_sort_queue(First_record, Open, Rest_of_open), bagof(Child, moves(First_record, Open, Closed, Child, Goal), Children), insert_list(Children, Rest_of_open, New_open), add_to_set(First_record, Closed, New_closed), path(New_open, New_closed, Goal),!.

71 % moves generates all children of a state that are not already on open or closed. The only % wierd thing here is the construction of a state record, test, that has unbound variables in % all positions except the state. It is used to see if the next state matches something % already on open or closed, irrespective of that states parent or other attributes Also,I've % commented out unsafe since the way I've coded the water jugs problem I don't really % need it. moves(State_record, Open, Closed,Child, Goal) :state_record(State, _, G, _,_, State_record), mov(State, Next), % not(unsafe(Next)), state_record(Next, _, _, _, _, Test), not(member_sort_queue(Test, Open)), not(member_set(Test, Closed)), G_new is G + 1, heuristic(Next, Goal, H), F is G_new + H, state_record(Next, State, G_new, H, F, Child). %insert_list inserts a list of states obtained from a call to % bagof and inserts them in a priotrity queue, one at a time insert_list([], L, L). insert_list([State | Tail], L, New_L) :insert_sort_queue(State, L, L2), insert_list(Tail, L2, New_L). % Printsolution prints out the solution path by tracing % back through the states on closed using parent links. printsolution(Next_record, _):state_record(State, nil, _, _,_, Next_record), write(State), nl. printsolution(Next_record, Closed) :state_record(State, Parent, _, _,_, Next_record), state_record(Parent, Grand_parent, _, _, _, Parent_record), member_set(Parent_record, Closed), printsolution(Parent_record, Closed), write(State), nl.

Meta-interpreters in Prolog
Meta-programming, is programming where we treat programs as data. This is easy in Prolog, as Prolog programs are just Prolog terms. Programs can also be

72 considered as input data for other programs. Prolog programs are sequences of prolog terms, so prolog programs easily serve as input data. A prolog meta-interpreter uses program data as a basis for additional computations. In this section, several prolog metainterpreters are discussed that modify the computation of prolog goals. Because it is possible to directly access program code in Prolog, it is easy to write interpreter of Prolog in Prolog. Such interpreter is called a meta-interpreter. Metainterpreters are usually used to add some extra features to Prolog, e.g., to change build-in negation as failure to constructive negation. The meta-level constructs, especially clause, is very useful in building meta interpreters, i.e., Prolog interpreters in Prolog. The key idea is in defining a predicate, called solve(G) which solves goal G with respect to a program P. The simplest Prolog meta-interpreter is a following program: solve(Goal):-call(Goal). However, there is not advantage of using such meta-intepreter as it immediately calls Prolog interpreter. Much more popular is "vanilla" meta-interpreter that uses Prolog's build-in unification but enables access to search engine which can be easily modified (e.g., it is possible to change the order of goals' execution) solve(true). solve((A,B)):solve(A),solve(B). solve(A):clause(A,B),solve(B). Note, that vanilla meta-interpreter uses build-in predicate clause(H,B) which finds a clause in Prolog program with head that unifies with H and body B (if there is no body, then Body=true). The modified vanilla meta-interpreter can be used to compute "proof" of the computation: solve(true, fact). solve((A,B),(ProofA, ProofB)):solve(A, ProofA),solve(B, ProofB). solve(A, A-ProofB):clause(A,B),solve(B, ProofB). It is also possible to write a meta-interpreter that uses list of goals instead of traditional conjunction of goals. In some cases, this could be more natural as one does not need to traverse the structure of goal each time a primitive goal is being found. solve([]). solve([A|T]):clause(A,B),

73 add_to_list(B,T,NT), solve(NT). A clause such as: gp(X, Y) :- p(X, Z) , p(Z, Y). can be interpreted as data, where :- and , are just infix binary constructors. Also father(bob,mark). is represented as: father(bob,mark) :- true. Consider the following query ?- solve(gp(bob,sue), Proof). Proof = (gp(bob,sue) :(p(bob,mary) :(m(bob,mary) :- true)), (p(mary,sue) :(m(mary,sue) :- true))) ?- why(gp(bob,sue)). gp(bob,sue) is true because p(bob,mary) is true because m(bob,mary) is true because p(mary,sue) is true because m(mary,sue) is true yes A Simple Meta-interpreter solve(true) :-!. solve(not A) :- not(solve(A)). solve((A,B)) :- !,solve(A), solve(B). solve(A) :- clause(A,B), solve(B). p(X,Y) :- q(X), r(Y). q(X) :- s(X). r(X) :- t(X). s(a). t(b). t(c). Given below can be used as test cases for above meta-interpreter test1 :- solve(p(a,b)). test2 :- solve(p(X,Y)). test3 :- solve(p(f,g)).

74 Meta-interpreter with user interaction solve(true) :-!. solve(not A) :- not(solve(A)). solve((A,B)) :- !,solve(A), solve(B). solve(A) :- clause(A,B), solve(B). solve(A) :- askuser(A). askuser(A):- write(A), write('? Enter true if the goal is true, false otherwise'), nl, read(true).

p(X,Y) :- q(X), r(Y). q(X) :- s(X). r(X) :- t(X). s(a). t(b). t(c). Given below can be used as test cases for above meta-interpreter test1 :- solve(p(a,b)). test2 :- solve(p(X,Y)). test3 :- solve(p(f,g)).

Previous University questions


4-mark questions 1. 2. 3. 4. 5. What is matching? What are abstract data types? With an example, explain how facts are represented in prolog? Explain how frames can be represented in prolog? Explain the use of assert and been predicate in prolog?

Answer: Predicates Clauses with the same clause name, the same number of arguments and defined in the same module are combined in the database and form the definition of a predicate. The common clause name is called the predicate name or functor of the predicate. The number of arguments is the arity. For example, the predicate fac/2 is defined by the collection of all clauses with the clause head fac(Arg1,Arg2), where Arg1 and Arg2 may be any terms.

75 The separate clauses of a predicate are connected by disjunction, i.e. by inclusive OR. Clauses with the same clause name but a different number of arguments belong to different predicates. Likewise, clauses which have the same clause name and the same arity but are associated with different modules belong to different predicates. The Prolog predicate concept can be compared to the subprogram concept in conventional programming languages; we therefore also speak of "calling" a predicate in Prolog. The predicate concept ensures a high degree of modularity in Prolog programs and thereby supports structured programming. Only predicates whose clauses have been included in the database with consult/1, reconsult/1 or with an assert predicate can be modified, i.e. clauses can be added (assert predicates) or removed (retract predicates), individual predicates can be deleted (abolish/1) or replaced (reconsult/1). Assert is used to add a new predicate to the current database. Been predicate is used to record previously visited states and avoid loops. The clauses of these predicates can be output with listing/0/1 and analyzed with clause predicates. Note however that to do so you must call the Prolog system with the debug option. Compiled predicates IF/Prolog offers a number of ways of compiling user-defined predicates and including them in the database. This can be done by the system's incremental compiler by using the assert and consult predicates. In addition, predicate definitions stored in files can be compiled by the predicate compile/1 or the external compiler (procmp command). Predicates compiled with consult/1, reconsult/1, compile/1 or procmp are normally no longer modifiable. However, you can inform the compiler with the dynamic/1 directive that certain predicates are to remain modifiable. Predicates which you have generated with assert predicates or which you have declared as modifiable with other compilation methods are compiled only to the extent where the compilation is reversible, i.e. where decompilation is possible. Whenever references are made in this manual to compiled predicates they should be taken to mean predicates which cannot be decompiled. These can no longer be modified by simply adding or removing clauses.

12-mark questions 1) Explain how recursive search is carried out in prolog with an example? 2) Write notes on: a. Meta predicates b. Meta interpreters 3) Brief on the concept of matching and evaluation in prolog with example?

76 4) With example explain how to represent semantic nets in prolog? 5) Explain various alternative search strategies? 6) Describe how recursive search is used on prolog to solve 3 x 3 knights problem? Answer: A knight can move two squares either horizontally or vertically followed by one square in an orthogonal direction as long as it does not move off the board. The attempt is to find a series of legal moves in which the knight lands on each square of the chessboard exactly once.

1 4 7
% 3x3 knight's tour :- dynamic been/1.

2 5 8

3 6 9

path(Z,Z). path(A,C):- move(A,B), not(been(B)), assert(been(B)), path(B,C). % initial call is path2(A,B,[A]) path2(Z,Z,Been). path2(A,C,Been):- move(A,B), not member(B,Been), path2(B,C,[B|Been]). % initial call is path3(A,B,[A]) AT MOST ONE SOLUTION path3(Z,Z,Been). path3(A,C,Been):- move(A,B), not member(B,Been), path3(B,C,[B|Been]),!.

move(1,6). move(1,8). move(2,7). move(2,9). move(3,4). move(3,8). move(4,3).

77 move(4,9). move(6,7). move(6,1). move(7,6). move(7,2). move(8,3). move(8,1). move(9,4). move(9,2). 7) Write the prolog code for the wolf, goat, and cabbage problem. Answer: This is an example of a production system in Prolog. A farmer with his wolf, goat, and cabbage come to the edge of a river they wish to cross. There is a boat at the river's edge, but, of course, only the farmer can row. The boat also can carry only two things (including the rower) at a time. Devise a sequence of crossings of the river so that all four arrive safely on the other side of the river. Remembering that If the wolf is ever left alone with the goat, the wolf will eat the goat. Similarly, if the goat is left alone with the cabbage, the goat will eat the cabbage. /* * This is the code for the Farmer, Wolf, Goat and Cabbage Problem * using the ADT Stack. * * Run this code by giving PROLOG a "go" goal. * For example, to find a path from the west bank to the east bank, * give PROLOG the query: * * go(state(w,w,w,w), state(e,e,e,e)). */ :- [adts]. /* consults (reconsults) file containing the various ADTs (Stack, Queue, etc.) */ go(Start,Goal) :empty_stack(Empty_been_stack), stack(Start,Empty_been_stack,Been_stack), path(Start,Goal,Been_stack). /* * Path predicates */ path(Goal,Goal,Been_stack) :-

78 write('Solution Path Is:' ), nl, reverse_print_stack(Been_stack). path(State,Goal,Been_stack) :move(State,Next_state), not(member_stack(Next_state,Been_stack)), stack(Next_state,Been_stack,New_been_stack), path(Next_state,Goal,New_been_stack),!. /* * Move predicates */ move(state(X,X,G,C), state(Y,Y,G,C)) :- opp(X,Y), not(unsafe(state(Y,Y,G,C))), writelist(['try farmer takes wolf',Y,Y,G,C]). move(state(X,W,X,C), state(Y,W,Y,C)) :- opp(X,Y), not(unsafe(state(Y,W,Y,C))), writelist(['try farmer takes goat',Y,W,Y,C]). move(state(X,W,G,X), state(Y,W,G,Y)) :- opp(X,Y), not(unsafe(state(Y,W,G,Y))), writelist(['try farmer takes cabbage',Y,W,G,Y]). move(state(X,W,G,C), state(Y,W,G,C)) :- opp(X,Y), not(unsafe(state(Y,W,G,C))), writelist(['try farmer takes self',Y,W,G,C]). move(state(F,W,G,C), state(F,W,G,C)) :- writelist([' BACKTRACK from:',F,W,G,C]), fail. /* * Unsafe predicates */ unsafe(state(X,Y,Y,C)) :- opp(X,Y). unsafe(state(X,W,Y,Y)) :- opp(X,Y). /* * Definitions of writelist, and opp. */ writelist([]) :- nl.

79 writelist([H|T]):- print(H), tab(1), /* "tab(n)" skips n spaces. */ writelist(T). opp(e,w). opp(w,e). reverse_print_stack(S) :empty_stack(S). reverse_print_stack(S) :stack(E, Rest, S), reverse_print_stack(Rest), write(E), nl.

Sample crossing for FWGC problem

Artificial Intelligence

19

80

State Space Representation of FWGC problem

Artificial Intelligence

20

Potrebbero piacerti anche