Предикат, который генерирует целые числа и проверяет, является ли целое число

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

intgr(1).
intgr(X) :- intgr(Y), X is Y+1.

Это сгенерирует все целые числа, но при использовании в качестве intgr(8) он обнаружит, что оно допустимо, а затем зациклится навсегда. Есть идеи, как это решить?

2 ответа

Решение

Для генерации натуральных чисел вы можете легко написать хвост-рекурсивный (и, следовательно, более экономичный) предикат. Например:

next_integer(I) :-
    next_integer(1, I).

next_integer(I, I).
next_integer(I, J) :-
    I2 is I + 1,
    next_integer(I2, J).

Но обратите внимание, что несколько компиляторов Prolog уже предоставили, либо как встроенный предикат, либо как предикат библиотеки, стандарт де-факто between/3 Предикат, который позволяет перечислять целые числа в заданном интервале. Например:

?- between(1, 3, I).
I = 1 ;
I = 2 ;
I = 3.

В зависимости от реализации, вы также можете использовать этот предикат для проверки, принадлежит ли целое число заданному интервалу, не оставляя ложных точек выбора. Например:

?- between(1, 3, 2).
true.

Возвращаясь к вашему вопросу, ваш предикат будет зацикливаться на обратном отслеживании при тестировании, так как в нем нет ничего, что говорит о том, что передаваемый вами аргумент не будет найден снова, если вы продолжите добавлять его на каждом проходе. Одним из решений будет проверка аргумента с использованием стандартного встроенного предиката var/1 и вырезать первое решение, когда аргумент связан. Например:

next_integer(I) :-
    (   var(I) ->
        next_integer(1, I).
    ;   integer(I) ->
        next_integer(1, I),
        !
    ;   fail    % or error
    )

next_integer(I, I).
next_integer(I, J) :-
    I2 is I + 1,
    next_integer(I2, J).

Надеюсь это поможет.

Вот первая причина, почему ваша программа не завершается. Чтобы увидеть это, я добавлю цели false в вашу программу:

intgr (1): - неверно.
intgr (X): - intgr (Y), false, X - Y + 1.

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

Если этот фрагмент (fail-slice) не завершается, то и исходная программа не прерывается.

В этом фрагменте, вероятно, легче увидеть: нет способа, которым эта программа может завершиться. Переменная X происходит только один раз в голове. В видимой части программы нет дальнейших ссылок, и, таким образом, никто не заботится о конкретной реализации X, То есть: независимо от того, что вы указали в качестве аргумента intgr/1 Ваше определение будет зациклено. Что-нибудь! intgr(-1) петли, intgr(stop) петли, intgr(X) петли, intgr(1) петли.

Для получения дополнительной информации об ошибках-срезах и их использовании см. Тег fail-slice.

Чтобы устранить проблему, нужно что-то изменить в видимой части вашей программы. Не смотрите на зачеркнутые участки - они не смогут улучшить завершение.

я хотел бы использовать length(_, N) который включает в себя 0. Вот еще один способ использования library(clpfd):

?- X #> 0.

И если вы настаиваете на перечислении решений:

nat(0).
nat(X0) :-
   X0 #> 0,
   X1 #= X0-1,
   nat(X1).
Другие вопросы по тегам