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