Как узнать, является ли число кратным другому

Глядя на код ниже:

multiple(X,0).
multiple(X,Y) :- lt(0,X), lt(0,Y), diff(Y,X,D), multiple(X,D).

Бывает, что-то не так. Для справки:
lt/2 определяет, меньше ли первый аргумент второго.
diff/3 определяет, равен ли третий аргумент первому аргументу минус второй.
lt/2 и diff /3 определены правильно.

Есть ли логическая ошибка в определении? Является ли предположение, что 0 кратным каждому числу, проблематичным или это логическая ошибка где-то еще? Я получаю правильные ответы, но я думаю, что запрос переходит в бесконечный цикл.

РЕДАКТИРОВАТЬ:
вот другие определения.

natNum(0).
natNum(s(X)) :- natNum(X).

lt(0,s(X)) :- natNum(X).
lt(s(X),s(Y)) :- lt(X,Y).

sum(0,X,X).
sum(s(X),Y,s(Z)) :- sum(X,Y,Z).

diff(X,Y,Z) :- sum(Z,Y,X).

?- multiple(X, s(s(s(s(s(s(0))))))).

где s(0) равно 1, s(s(0)) равно 2 и т. д. Он дает все желаемые ответы для X, но после последнего ответа он застревает. Я предполагаю, что в бесконечном рекурсивном цикле?


person Huzo    schedule 20.04.2019    source источник
comment
Что не работает? Какой запрос не работает? Вы trace.?   -  person Willem Van Onsem    schedule 20.04.2019
comment
?- multiple(X, s(s(s(s(s(s(0))))))). где s(0) равно 1, s(s(0)) равно 2 и т. д. Он дает все желаемые ответы для X, но после последнего ответа он застревает. Я предполагаю, что в бесконечном рекурсивном цикле? @WillemVanOnsem   -  person Huzo    schedule 20.04.2019
comment
Пожалуйста, добавьте определение lt/2 и diff/3. Как мы можем сказать, если мы не видим их!   -  person false    schedule 20.04.2019
comment
отредактировано. :) @ЛОЖЬ   -  person Huzo    schedule 20.04.2019
comment
Напишите X = s(_) вместо lt(0,X) (дважды)   -  person false    schedule 20.04.2019
comment
@false то же самое для Y = s(_) вместо lt(0,Y), верно?   -  person Huzo    schedule 20.04.2019


Ответы (1)


Что происходит в вашей программе? Это зацикливается навсегда или требуется только некоторое время, так как вы не обновляли свое оборудование в последние десятилетия? Мы не можем сказать. (На самом деле, мы могли бы сказать, глядя на вашу программу, но на данный момент она слишком сложна).

Что мы можем сделать с легкостью, так это сузить источник этих дорогостоящих усилий. И это, без глубокого понимания вашей программы. Начнем с запроса:

| ?- multiple(X, s(s(s(s(s(s(0))))))).
X = s(0) ? ;
X = s(s(0)) ? ;
X = s(s(s(0))) ? ;
X = s(s(s(s(s(s(0)))))) ? ;
** LOOPS or takes too long ***

Нет ли более простого способа сделать это? Весь этот ввод с запятой. Вместо этого просто добавьте false в свой запрос. Таким образом, найденные решения больше не отображаются, и мы можем сосредоточиться на этом раздражающем зацикливании. И, если мы готовы, вы также можете добавить в свою программу цели false! По таким целям количество выводов может уменьшиться (или остаться прежним). И если результирующий фрагмент (называемый failure-slice) зацикливается, то это причина, почему ваша исходная программа зацикливается:

multiple(_X,0) :- false.
multiple(X,Y) :- lt(0,X), false, lt(0,Y), diff(Y,X,D), multiple(X,D).

natNum(0) :- false.
natNum(s(X)) :- natNum(X), false.

lt(0,s(X)) :- natNum(X), false.
lt(s(X),s(Y)) :- false, lt(X,Y).

?- multiple(X, s(s(s(s(s(s(0))))))), false.
** LOOPS ***

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

Чтобы это исправить, нам нужно что-то изменить в оставшейся, видимой части. Я бы выбрал lt/2, первое предложение которого можно обобщить до lt(0, s(_)).

Но ждать! Почему можно обобщить требование, чтобы у нас было натуральное число? Посмотрите на факт multiple(X,0)., который вы написали. Вы также не требовали, чтобы X было натуральным числом. Такого рода чрезмерные обобщения часто встречаются в программах на Прологе. Они улучшают свойства завершения при относительно низкой цене: иногда они слишком общие, но все термины, которые дополнительно вписываются в обобщение, не являются натуральными числами. Это такие термины, как any или [a,b,c], поэтому, если они появляются где-то, вы знаете, что они не относятся к решениям.


Итак, идея заключалась в том, чтобы поместить в вашу программу false целей, чтобы результирующая программа (отказ-срез) по-прежнему зацикливалась. В худшем случае вы поместите false не в то место, и программа завершится. Путем проб и ошибок вы получаете минимальный процент неудач. Все те вещи, которые сейчас перечеркнуты, не имеют значения! В частности diff/3. Так что не нужно это понимать (на данный момент). Достаточно посмотреть на оставшуюся программу.

person false    schedule 20.04.2019
comment
Мне очень жаль, но я не мог понять. Что означает нацарапанная часть кода? Почему мы добавили false к нашим определениям? Не будет ли это просто игнорировать все эти определения? Я озадачен. - person Huzo; 20.04.2019
comment
Прочтите И если полученный фрагмент... В том-то и дело: Оставшаяся программа имеет много общего с вашей исходной программой, так как является причиной ее незавершения. - person false; 20.04.2019
comment
Хотите еще что-нибудь объяснить? - person false; 21.04.2019
comment
Разве это не будет просто игнорировать все эти определения? Да, срез сбоя не содержит diff/3, потому что проблема возникает уже независимо от него. - person false; 21.04.2019