CS计算机代考程序代写 prolog Declarative Programming

Declarative Programming
Debugging Prolog Programs

Geraint A. Wiggins

Professor of Computational Creativity
Department of Computer Science

Vrije Universiteit Brussel

The basic Prolog debugger

I When programs go wrong we need a means of working
out why

I One way to do this is to analyse the logic of the program
(declarative debugging)

I Declarative debugging is an active research area, but we
do not yet have the technology to do it really well

I Another way is to look at the program’s procedural
behaviour

I This is less helpful, but relatively easy to do

The basic Prolog debugger (2)

I To use the debugger, we need
I Prolog, with our program loaded;
I a copy of the program, on paper or on screen;
I a clear idea of what the program is supposed to do;
I an example query, for which the program does not

exhibit the correct behaviour

I To switch on the basic debugger, use the command

?- trace.

I To switch it off again, use

?- notrace.

The basic Prolog debugger (3)
I Once the debugger is switched on, any query you ask of

Prolog will be “explained” as it is proven
I We think of each predicate in a program as having four

ways in and out, or ports. These are:
Call – where control goes in on the first attempt

to prove a goal;
Succeed – where control comes out when the goal

succeeds;
Retry – where control goes in on subsequent

attempts to prove a goal (i.e., after
backtracking)

Fail – where control comes out when the goal
fails.

I Some systems define a further port:
Exception – where control comes out on an error (e.g.,

instantiation error).

Tracing program execution
I The Prolog debugger will now stop and tell us what it is

doing, each time it passes a port. For example:
?- trace.

{The debugger will first creep —
showing everything (trace)}

true

{trace}
?- append( [a], [b], X ).

1 1 Call: append([a],[b], 89) ?

2 2 Call: append([],[b], 355) ?

2 2 Exit: append([],[b],[b]) ?

1 1 Exit: append([a],[b],[a,b]) ?

X = [a,b] ?

true

{trace}
?-

Tracing program execution (2)

{trace}
| ?- append( X, Y, [a] ).

1 1 Call: append( 69, 83,[a]) ?

1 1 Exit: append([],[a],[a]) ?

X = [],

Y = [a] ? ;

1 1 Redo: append([],[a],[a]) ?

2 2 Call: append( 365, 83,[]) ?

2 2 Exit: append([],[],[]) ?

1 1 Exit: append([a],[],[a]) ?

X = [a],

Y = [] ? ;

1 1 Redo: append([a],[],[a]) ?

2 2 Redo: append([],[],[]) ?

2 2 Fail: append( 365, 83,[]) ?

1 1 Fail: append( 69, 83,[a]) ?

false

Tracing program execution (3)

I The main components of the trace output are:

2 2 Call: append( 365, 83,[]) ?

Invocation number – an identification number which is
unique to this invocation of a predicate;

Current depth – an indication of how deep the proof
process has gone;

Port indictor – tells us which port we are at;
Current goal – tells us the current goal and its

instantiation;
Prompt – asks for what to do next.

Tracing program execution (4)

I There are several useful options to apply (mostly single
character commands):

c Creep (also carriage return) – take one proof step to the
next port;

s Skip – jump to the next exit port (Succeed or Fail) from
this invocation of this predicate;

a Abort – drop out of execution and return to the Prolog
prompt;

n Nodebug – switch off tracing and proceed as for normal
execution;

r Retry – go back to the call whose invocation number is
given

? Help – print out a list of available commands.

I Note that some commands (e.g., Skip) only make sense
at some ports (e.g., input ports), and so will not work at
others.

Spying on problem predicates

I In large programs, it is often not feasible to trace all the
way through to an error

I Often, we have a clear idea of which predicate is going
wrong, so we want to go straight to it, even in small
programs

I To do this, we use spy points

I We can set the debugger to wait until it encounters a spy
point in the program, and then it will switch on.

Spying on problem predicates (2)
I Example:

append( [], L, L ).

append( [H|T], L, [H|Z] ) :-
append( T, L, Z ).

test( X, XX ) :- append( X, X, XX ).

I Here, we can set a spy point on append/3:

?- spy append.

{The debugger will first leap —
showing spypoints (debug)}

{Spypoint placed on user:append/3}

true

{debug}
?-

Spying on problem predicates (2)

{debug}
?- test( X, [a,B,c,D,e,F] ).

+ 2 2 Call: append( 69, 69,[a, 91,c, 111,e, 131]) ?

+ 3 3 Call: append( 471,[a| 471],[ 91,c, 111,e, 131]) ?

+ 4 4 Call: append( 676,[a, 91| 676],[c, 111,e, 131]) ?

+ 5 5 Call: append( 881,[a, 91,c| 881],[ 111,e, 131]) ?

+ 5 5 Exit: append([],[a,e,c],[a,e,c]) ?

+ 4 4 Exit: append([c],[a,e,c],[c,a,e,c]) ?

+ 3 3 Exit: append([e,c],[a,e,c],[e,c,a,e,c]) ?

+ 2 2 Exit: append([a,e,c],[a,e,c],[a,e,c,a,e,c]) ?

1 1 Exit: test([a,e,c],[a,e,c,a,e,c]) ?

B = e,

D = a,

F = c,

X = [a,e,c] ?

I + means “there is a spy point here”

Spying on problem predicates (3)

I The same commands as before work in debug mode, plus

l Leap – switch off the debugger until the next spy point
is found;

+ Add spypoint to the current predicate;
– Remove spypoint from the current predicate.

I It is possible to choose which debug ports stop the
execution, using the leash/1 predicate. See the manual
for details.