Трассировка программы пролога в списке

#prolog #trace

Вопрос:

Я подумываю о том, чтобы собрать все следы программы пролога в список. Например, предположим, что у меня есть

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

test:-
    a,
    v(X).
 

Я хотел бы получить список формы [[a,v(1)],[a,v(2)]] (или что-то подобное, я всегда могу использовать findall в какой-то момент, чтобы получить все решения).

Очевидная идея, которая приходит мне в голову, состоит в том, чтобы предварительно обработать код пролога, чтобы добавить список в качестве аргумента к предикату (используя что-то вроде term_expansion/2 SWI), а затем после каждого вызова добавлять термин в список. Например:

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

Запрос будет findall(L,test(L),LO). получен LO = [[a, v(1)], [a, v(2)]] . Кто-нибудь знает, есть ли предикат, который уже делает это?

Комментарии:

1. Вам нужен полный след выполнения или только тот путь, который завершится успешно ? Потому что промежуточный goasl может потерпеть неудачу, вернуться и, наконец, добиться успеха.

2. Только успешные пути

Ответ №1:

Я думаю, что вы можете сделать что-то вроде этого:

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

Запрос:

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

Комментарии:

1. Обратите внимание, что почти во всех системах пролога clause/2 может использоваться только с динамическими предикатами.

2. Спасибо, это то, что я искал. Я думаю , что нет необходимости писать call(Goal) , просто Goal достаточно.

Ответ №2:

Это решение подключается к интерфейсу отладчика SWI, поэтому его можно взломать, чтобы отслеживать каждую цель с помощью встроенного трассировщика (сохраняет текущую трассировку в динамической базе данных). Кажется, это прекрасно работает для простых запросов, подобных вашему примеру.

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

Пробные прогоны:

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

Ответ №3:

ответы слаго хороши, но вложенные предикаты не расширяются. Я изменил его следующим образом:

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

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

successful_path(Goal, Res) :-
    clause(Goal,Body),
    ( Body = true -> 
        Res = [Goal] ; 
        successful_path(Body,Res)
    ).
 

Таким образом, также расширяются подцели. Это работает с прологом SWI, так как я использовал callable(Goal) (я не знаю, является ли он стандартным или нет). В противном случае его можно просто заменить call(Goal) или, что еще лучше, заменить Goal .