Обратный отсчет от числа с использованием Пролога

Тривиальный вопрос, но я хочу, чтобы программа возвращала список чисел, меньших или равных заданному числу. Например, CountD(4,L). должен дать [4,3,2,1]. Это то, что у меня есть до сих пор:

CountD(1,[]).
CountD(Num,List):- [Num|List], CountD(M,List), Num is M+1.

person user137379    schedule 01.12.2014    source источник


Ответы (3)


ответ Бориса идеален. Я просто хотел бы объяснить, почему ваша исходная программа и программа другого ответа не завершаются.

На первый взгляд вроде работает, в итоге получаем ответ:

?- countD(4,L).
L = [4, 3, 2, 1]

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

?- countD(4,L).
L = [4, 3, 2, 1] ;
**LOOPS**

Лучший способ понять завершение в Прологе — это сосредоточиться на соответствующих частях вашей программы. Преимущество Пролога в том, что, хотя его фактический поток управления довольно сложен, часто бывает достаточно посмотреть на очень маленькую часть вашей программы, чтобы определить определенные свойства. Так что не надо "все сразу понимать".

Вместо того, чтобы смотреть на все подряд, я возьму следующий фрагмент отказа:

countD(0,[]) :- false.
countD(Num,List) :-
   List = [Num|L],
   countD(M,L), false,
   Num is M+1.

В первой строке при вставке false факт фактически удаляется. Таким образом, независимо от того, что именно он описывает, 0 или 1 не имеют никакого влияния на завершение этого предиката. В общем, незавершение любого среза отказа подразумевает не завершение исходной программы.

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

Чтобы решить эту проблему, мы можем сразу сказать, что добавление дополнительного предложения не улучшит завершение (при условии, что программа чистая, монотонная). Нам нужно что-то изменить в видимой части. Борис сделал это, добавив некоторую проверку для Num перед фактической рекурсией.

person false    schedule 01.12.2014

Это тоже решение:

countd(N, [N|List0]) :-
    succ(N0, N),
    countd(N0, List0).
countd(0, []).

Здесь важной строкой является succ(N0, N). Это будет успешным только для N > 0 и завершится ошибкой, если второй аргумент равен 0 (это также вызовет ошибку, если N не является неотрицательным целым числом).

Когда succ(N0, N) терпит неудачу, будет оцениваться второе предложение. Это будет успешным, только если его аргументы равны 0 и пустой список.

person Community    schedule 01.12.2014
comment
@false Я на самом деле этого не знал - person ; 02.12.2014

countD(0,[]).
countD(Num,List) :- List = [Num|L], countD(M,L), Num is M+1.

ОБНОВЛЕНО:

Исправляет проблемы, обнаруженные @false и @boris в комментариях:

countD(Num,[Num|L]) :- Num > 0, M is Num-1, countD(M,L).
countD(0,[]).
person Grzegorz Adam Kowalski    schedule 01.12.2014
comment
@false, это так. Вы сами проверяли? Я сделал. - person Grzegorz Adam Kowalski; 02.12.2014
comment
Здесь все еще есть ненужная точка выбора... Чтобы избавиться от нее, либо поменяйте порядок предложений, либо используйте сокращение. - person ; 02.12.2014
comment
@GrzegorzAdamKowalski Теперь вы заметите, что ваша окончательная версия идентична моей окончательной версии :) succ(X0, X) ведет себя точно так же, как X > 0, X0 is X - 1 - person ; 02.12.2014
comment
@Boris: относительно точно: succ/2 настаивает на целых числах, даже на натуральных числах. (>)/2 и (is)/2 разрешают выражения. Таким образом, X = 1+1, X > 0 удается, но X = 1+1, succ(X,Y) является type_error(integer,1+1). succ(-1,Y) и domain_error(not_less_than_zero,-1) - person false; 03.12.2014