Вы находитесь на странице: 1из 84

UNIT 5

5.1 Prolog
5.2 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]).

%%%%%%%%%%%%%%%%%%%% 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, [], []).

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]).

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).


5.3 Semantic Nets and Frames in Prolog
5.4 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, _),
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
% 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),!.

% 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.

5.5 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
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
meta-interpreters 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. Meta-
interpreters 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),
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)).

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. What is matching?
2. What are abstract data types?
3. With an example, explain how facts are represented in prolog?
4. Explain how frames can be represented in prolog?
5. 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.

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?
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 knight’s 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 2 3

4 5 6

7 8 9

% 3x3 knight's tour

:- dynamic been/1.

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).

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) :-
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.

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.
State

Вам также может понравиться