0
votes

I'm thinking about getting all the traces of a prolog program in a list. For example, suppose I have

a.
v(1).
v(2).

test:-
    a,
    v(X).

I would like to obtain a list of the form [[a,v(1)],[a,v(2)]] (or something similar, I can always use findall at some time to get all the solutions).

The obvious idea that comes to my mind is to pre process the prolog code to add a list as argument to the predicate (using something like term_expansion/2 in SWI) and then, after each call, add the term to the list. For example:

test(L0):-
    a,
    append([],[a],L1),
    v(X),
    append(L1,[v(X)],L0).

The query would be findall(L,test(L),LO). obtaining LO = [[a, v(1)], [a, v(2)]]. Does anyone knows if there a predicate that already does this?

2
You want the full trace of execution or only the path that succeeds ? Because intermediate goasl may fail and backtrack and finally succeed. - gusbro
Only the successful paths - damianodamiano

2 Answers

0
votes

I think you can do something like this:

successful_paths(Head, Paths) :-
    clause(Head, Body),
    findall(Path, successful_path(Body, Path), Paths).

successful_path((Goal, Goals), [Goal|Path]) :-
    !,
    call(Goal),
    successful_path(Goals, Path).

successful_path(Goal, [Goal]) :-
    call(Goal).

% Code to trace

a.
v(1).
v(2).

test:- a, v(_X).

Query:

?- successful_paths(test, Paths).
Paths = [[a, v(1)], [a, v(2)]].
0
votes

This solution taps onto SWI debugger interface so its sort of a hack to keep track every goal using the builtin tracer (keeps the current trace on the dynamic database). It seems to work fine for simple queries like your example.

:-dynamic '$trace'/1.
get_trace(Pred, L):-
   retractall('$trace'(_)),
   assertz('$trace'([])),
   trace_notrace(t,t),
   call(Pred),
   call('$trace'(Trace)),
   trace_notrace(nt,nt),
   reverse(Trace, RTrace),
   maplist(arg(2), RTrace, L).

trace_notrace(t,_):-
  trace.
trace_notrace(T, _):-
  nodebug,
  T=nt.
trace_notrace(nt, T):-
  trace,
  T=t.

prolog_trace_interception(call, Frame, _PC, continue) :-
  retract('$trace'(Trace)),
  assertz('$trace'([frame(Frame, _Goal)|Trace])),
  !.
prolog_trace_interception(exit, Frame, _PC, continue) :-
  prolog_frame_attribute(Frame, goal, Goal),
  prolog_trace_interception_goal(Goal, Goal1),
  retract('$trace'(Trace)),
  (   append(Head, [frame(Frame, _)|MTrace], Trace)
  ->  append(Head, [frame(Frame, Goal1)|MTrace], NTrace)
  ;    NTrace=Trace
  ),
  assertz('$trace'(NTrace)),
  !.
prolog_trace_interception(fail, Frame, _PC, continue) :-
  retract('$trace'(Trace)),
  append(_, [frame(Frame, _)|NTrace], Trace),
  assertz('$trace'(NTrace)),
  !.
prolog_trace_interception(redo(_), Frame, _PC, continue) :-
  retract('$trace'(Trace)),
  append(_, [frame(Frame, Goal)|NTrace], Trace),
  assertz('$trace'([frame(Frame, Goal)|NTrace])),
  !.
prolog_trace_interception(_, _, _, continue).
    
prolog_trace_interception_goal(Goal, Goal2):-
  (
     (Goal=(Module:Goal1), memberchk(Module, [system, lists, '$bags']))
  -> copy_term(Goal1, Goal2)
  ;  copy_term(Goal, Goal2)
  ).

Sample runs:

?- findall(L, get_trace(test, L), Traces).
Traces = [[test, a, v(1)], [test, a, v(2)]].

?- get_trace(member(X, [a,b]), Trace).
X = a,
Trace = [member(a, [a, b])] ;
X = b,
Trace = [member(b, [a, b])] ;
false.