3

Я пишу какую-то арифметику Peano, чтобы лучше узнать Prolog. Ниже приведен код, который я придумал, и это, кажется, эквивалент того, что я видел в другом месте в Интернете:Stackoverflow in Prolog Peano Arithmetic

add(X,z,X). 
add(X,s(Y),s(Z)) :- add(X,Y,Z). 
mult(_,z,z). 
mult(X,s(Y),W) :- mult(X,Y,Z), add(X,Z,W). 

Однако, если я пытаюсь сделать простой запрос, как делители пар 0, I столкнуться с проблемами:

| ?- mult(X,Y,z). 

Y = z ? ; 

X = z 
Y = s(z) ? ; 

X = z 
Y = s(s(z)) ? ; 

X = z 
Y = s(s(s(z))) ? ; 

Fatal Error: global stack overflow (size: 32768 Kb, reached: 32765 Kb, environment variable used: GLOBALSZ) 

это действительно прослушивает меня, о том, как он может получить весь путь к Y = 3, но не Y = 4?

+1

Это действительно странное поведение, и трудно отследить ... Интересно, может ли артерия быть связана с ошибкой. – CapelliC

ответ

3

Переполнение стека происходит, потому что для вашего запроса предикат add/3 в конечном итоге вызывается с переменной как средний аргумент. Когда вы возвращаетесь в него, вы получаете цикл, который приводит к переполнению стека. Рассмотрим звонок add(X,Y,Z). Первое предложение дает вам первое решение, add(X,z,X). Но при возврате в исходное положение, когда вы используете второе предложение, вы объединяете свой запрос с add(X,s(Y),s(Z)) и рекурсивно вызываете add(X,Y,Z), туда, где вы начали (помните, что средний аргумент не создается, поэтому Y в s(Y) также не будет создан при вызове Вы можете получить первые четыре решения, как показано выше, только благодаря базовым случаям обоих предикатов. Когда использование этого базового предложения (при обратном отслеживании) исчерпано, вы попадаете в цикл, который я только что объяснил выше.

Попробуйте добавить следующий пункт в качестве первого пункта в add/3 предиката:

add(X,Y,Z) :- 
    write('Called: '), writeq(add(X,Y,Z)), nl, fail. 

Повторная запрос вы получите (надеюсь, вы быстро с Control-C):

| ?- mult(X,Y,z). 

Y = z ? ; 
Called: add(_279,z,z) 

X = z 
Y = s(z) ? ; 
Called: add(_279,z,_307) 
Called: add(_279,_279,z) 

X = z 
Y = s(s(z)) ? ; 
Called: add(_279,z,_309) 
Called: add(_279,_279,_309) 
Called: add(z,z,z) 

X = z 
Y = s(s(s(z))) ? ; 
Called: add(s(_307),_307,_309) 
Called: add(s(z),s(s(z)),z) 
Called: add(s(s(_311)),_311,_313) 
Called: add(s(s(z)),s(s(s(s(z)))),z) 
Called: add(s(s(s(_315))),_315,_317) 
Called: add(s(s(s(z))),s(s(s(s(s(s(z)))))),z) 
Called: add(s(s(s(s(_319)))),_319,_321) 
Called: add(s(s(s(s(z)))),s(s(s(s(s(s(s(s(z)))))))),z) 
Called: add(s(s(s(s(s(_323))))),_323,_325) 
... 

Надеется, что это помогает.

+2

Дополнительное примечание. Если вы внимательно посмотрите на начало бесконечных распечаток, вы заметите, что вы пытаетесь добавить возрастающие значения первых двух аргументов 'add/3', проверяя, если они складываются до' z' , Однако базовое предложение никогда не применяется ... –

+1

Это впечатляющий анализ! Внутренние работы Prologs настолько просты, но способ, которым они показывают себя, может быть ошеломляющим. :) Как вы считаете, лучший способ исправить ситуацию? Я могу добавить предикат, который говорит, что X и Y должны быть меньше или равны Z, но должен ли я помещать его в add или mult? –

1

Я знаю, что это очень старый пост, но я только начал изучать Пролог и найти вопрос увлекательным. Итак, вот мои два цента.

Я заметил, что если вы измените ваше правило

mult(X,s(Y),W) :- mult(X,Y,Z), add(X,Z,W). 

дизъюнкции

mult(X,s(Y),W) :- mult(X,Y,Z); add(X,Z,W). 

и запустить тот же запрос

?- mult(X,Y,z). 

Вы получите некоторые результаты, но, как вы видите, интерпретатор SWI Prolog не показывает много деталей после точки:

?- mult(X,Y,z). 
Y = z ; 
Y = s(z) ; 
Y = s(s(z)) ; 
Y = s(s(s(z))) ; 
Y = s(s(s(s(z)))) ; 
Y = s(s(s(s(s(z))))) ; 
Y = s(s(s(s(s(s(z)))))) ; 
Y = s(s(s(s(s(s(s(z))))))) ; 
Y = s(s(s(s(s(s(s(s(z)))))))) ; 
Y = s(s(s(s(s(s(s(s(s(z))))))))) ; 
Y = s(s(s(s(s(s(s(s(s(s(...)))))))))) ; 
Y = s(s(s(s(s(s(s(s(s(s(...)))))))))) ; 
Y = s(s(s(s(s(s(s(s(s(s(...)))))))))) ; 
Y = s(s(s(s(s(s(s(s(s(s(...)))))))))) ; 
Y = s(s(s(s(s(s(s(s(s(s(...)))))))))) ; 
Y = s(s(s(s(s(s(s(s(s(s(...)))))))))) ; 
Y = s(s(s(s(s(s(s(s(s(s(...)))))))))) ; 
Y = s(s(s(s(s(s(s(s(s(s(...)))))))))) . 

Проделав то же самое в Gnu Пролог выглядит намного лучше, мягко говоря:

| ?- mult(X,Y,z). 

Y = z ? ; 

Y = s(z) ? ; 

Y = s(s(z)) ? ; 

Y = s(s(s(z))) ? ; 

Y = s(s(s(s(z)))) ? ; 

Y = s(s(s(s(s(z))))) ? ; 

Y = s(s(s(s(s(s(z)))))) ? ; 

Y = s(s(s(s(s(s(s(z))))))) ? ; 

Y = s(s(s(s(s(s(s(s(z)))))))) ? ; 

Y = s(s(s(s(s(s(s(s(s(z))))))))) ? ; 

Y = s(s(s(s(s(s(s(s(s(s(z)))))))))) ? ; 

Y = s(s(s(s(s(s(s(s(s(s(s(z))))))))))) ? ; 

Y = s(s(s(s(s(s(s(s(s(s(s(s(z)))))))))))) ? ; 

Y = s(s(s(s(s(s(s(s(s(s(s(s(s(z))))))))))))) ? ; 

Y = s(s(s(s(s(s(s(s(s(s(s(s(s(s(z)))))))))))))) ? ; 

Y = s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(z))))))))))))))) ? ; 

Y = s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(s(z)))))))))))))))) ? ; 
... 

В SWI Prolog (и GNU Prolog), вы можете вызвать отладчик, который иллюстрирует немного больше отличный теоретический анализ Пауло Моура, относительно исходной задачи:

?- trace, mult(X,Y,z). 

Как вы видите, это, кажется, работает вечно, так что бы объяснить переполнение стека.Посмотрите результат следа here во всей красе. Вы можете проследить бесконечный цикл на вашем browser, выполнив описанную выше трассировку.

+1

Прежде всего, ваше новое определение слишком общее. 'mult (s (s (z)), s (z), s (s (s (z)))). Успешно. Это 2x1 = 3. – false

+0

Я ценю обратную связь, особенно когда она приходит от кого-то более осведомленного. Я должен был больше взглянуть на аксиомы Пеано, прежде чем ответить. Благодаря ! – milia