2009-07-08 3 views
4

Я пытаюсь реализовать предикат findall в Prolog (да, я знаю, что он встроен, это для назначения).Правило PROLOG возвращает только первое совпадение

Он записывается следующим образом:

my_findall(N,P,Pred,L) :- Pred, not(new(N,P)), !, assert(new(N,P)), my_findall(N1,P1,Pred,L1), L=[N,P,L1], retract(new(N,P)). 
my_findall(_,_,_, []). 

По какой-то причине это только дает мне первое решение и останавливается там, как будто второй вызов my_findall терпит неудачу. Насколько я понимаю, механизм возврата в исходное положение должен охватывать все возможные варианты, которые должны включать в себя все опции для вызова Pred (N, P), поэтому, несмотря на то, что второй вызов должен завершиться с первой попытки (первый вариант, который пытался использовать Pred, уже утверждалось), он должен сначала попробовать все другие варианты, прежде чем отказаться от my_findall ((,), _, []).

Если это не так, как это работает, существует ли способ заставить такое поведение полностью не переписывать решение?

+0

С каким прологом вы работаете? – liori

+0

Встроенные находки - findall/3 и findall/4. Какой из них вы пытаетесь реализовать? – Kaarel

ответ

5

Ваш Pred содержит несвязанные переменные. Когда в первой итерации вы вызываете Pred, эти переменные связаны с первыми возможными значениями. В вашем рекурсивном шаге Pred уже имеет связанные переменные, и они не могут изменять значения. Итак ... это решение не сработает.

след от SWI-Prolog (я должен был переименовать новый/2 к пункту/2 по некоторым причинам):

Первый уровень (вызов: my_findall (A, B, член (р (А, В), [p (1,2), p (3,4)]), L).).

Call: (7) my_findall(_G819, _G820, member(p(_G819, _G820), [p(1, 2), p(3, 4)]), _G840) ? creep 
    Call: (8) lists:member(p(_G819, _G820), [p(1, 2), p(3, 4)]) ? creep 
    Exit: (8) lists:member(p(1, 2), [p(1, 2), p(3, 4)]) ? creep 

Мы получили p (A, B) = p (1,2). В этот момент связан с 1, B обязан 2.

^ Call: (8) not(item(1, 2)) ? creep 
    Call: (9) item(1, 2) ? creep 
    Fail: (9) item(1, 2) ? creep 
^ Exit: (8) not(item(1, 2)) ? creep 

Хорошо, нет пункта (1,2) в базе данных.

^ Call: (8) assert(item(1, 2)) ? creep 
^ Exit: (8) assert(item(1, 2)) ? creep 

Теперь элемент (1,2) является истинным. Рекурсивный вызов:

Call: (8) my_findall(_L215, _L216, member(p(1, 2), [p(1, 2), p(3, 4)]), _L199) ? creep 

Давайте еще одно решение сделать Pred:

Call: (9) lists:member(p(1, 2), [p(1, 2), p(3, 4)]) ? creep 
          ^^^^^^^ 

Посмотреть подчеркнутую часть?

Для этой методики вы, вероятно, должны скопировать Pred, рекурсивно меняя N и P на новые переменные. Для каждой итерации вам нужно «создать» новую пару N и P. Проверьте copy_term/2 (http://www.swi-prolog.org/pldoc/doc_for?object=copy_term%2f2).

Смежные вопросы