Reified call_with_time_limit / call_with_inference_limit
Я пытаюсь определить отношение callto_status(Goal, Status)
это всегда успешно и объединяет статус в соответствии с результатом вызова Goal
(другими словами, я хотел бы реализовать call_with_inference_limit/3
). Моя реализация использует SWI call_with_inference_limit/3
который имеет тот же интерфейс, что и call_with_time_limit/3
(что должно заставить его работать и в этом случае). Реализация call_with_..._limit
не отступает, поэтому я подумал, что лучше не создавать впечатление о замене ответа для цели.
Я ввел предикат-помощник derivable_st
для удобочитаемости. Он обрабатывает случай успеха и тайм-аута, но в противном случае не работает.
% if Goal succeeds, succeed with Status = true,
% if Goal times out, succeed with Status = timeout
% if Goal fails, fail
derivable_st(Goal, Status) :-
T = 10000, % set inference limit
% copy_term(Goal, G), % work on a copy of Goal, we don't want to report an answer substitution
call_with_inference_limit(G, T, R), % actual call to set inference limit
( R == !
-> Status = true % succeed deterministically, status = true
; R == true
-> Status = true % succeed non-deterministically, status = true
; ( R == inference_limit_exceeded % timeout
-> (
!, % make sure we do not backtrack after timeout
Status = timeout % status = timeout
)
; throw(unhandled_case) % this should never happen
)
).
Основной предикат оборачивается derivable_st
и обрабатывает случай сбоя и, возможно, выброшенные исключения (если есть). Мы могли бы хотеть выделить переполнения стека (которые случаются в случае слишком высоких пределов логического вывода), но сейчас мы просто сообщаем о любом исключении.
% if Goal succeeds, succeed with Status = true,
% if Goal times out, succeed with Status = timeout
% if Goal fails, succeed with Status = false
% if Goal throws an error, succeed with Status = exception(The_Exception)
% Goal must be sufficiently instantiated for call(Goal) but will stay unchanged
callto_status(Goal, Status) :-
catch(( derivable_st(Goal, S) % try to derive Goal
-> Status = S % in case of success / timeout, pass status on
; Status = false % in case of failure, pass failure status on, but succeed
),
Exception,
Status = exception(Exception) % wrap the exception into a status term
).
Предикат работает для некоторых простых тестовых случаев:
?- callto_reif( length(N,X), Status).
Status = true.
?- callto_reif( false, Status).
Status = false.
?- callto_reif( (length(N,X), false), Status).
Status = timeout.
Мой вопрос сейчас немного расплывчат: делает ли этот предикат то, что, как я утверждаю, он делает? Видите ли вы какие-либо ошибки / точки улучшения? Я благодарен за любой вклад!
Изменить: как предложено @false, закомментировано copy_term/2
0 ответов
Это более короткое решение:
callto_status(Goal, Status) :-
call_with_inference_limit(Goal, 10000, Status0),
(Status0 = ! -> !, Status = true; Status = Status0).
callto_status(_, false).
Вы видите, насколько полезен оригинал! статус - избежать ненужного выбора:
?- callto_status(member(X,[1,2,3]), Y).
X = 1,
Y = true
X = 2,
Y = true
X = 3,
Y = true.
?- callto_status(fail, Y).
Y = false.
Вы, конечно, также можете заменить Status0 = ! -> !, Status = true
по Status0 = ! -> Status = true
только. Тогда вы всегда будете получать оставшуюся точку выбора:
?- callto_status(member(X,[1,2,3]), Y).
X = 1,
Y = true
X = 2,
Y = true
X = 3,
Y = true
Y = false.
Из вопроса неясно, чего именно вы хотите.