В логическом программировании рекурсия в предикате часто обрабатывается более чем одним правилом. Первое правило описывает базовый случай рекурсии, то есть его состояние остановки; другие правила, или, может быть, только второе, описывают рекурсивные шаги.
Итак, ваше правило is_on
(которое я переименовал contains
) обычно пишется следующим образом:
contains(Item, [Item | _]).
contains(Item, [_ | Tail]) :- contains(Item, Tail).
Предикат filter_double
может подвергаться аналогичному переписыванию. Прежде всего, пустой список будет соответствовать пустому результату.
filter_doubles([], []).
Затем, если Item
встречается в Rest
списка, вы повторяетесь по Rest
списка, отбрасывая это вхождение Item
.
filter_doubles([Item | Rest], Result) :-
contains(Item, Rest), !,
filter_doubles(Rest, Result).
Наконец, если Item
не встречается в Rest
списка (поскольку предыдущее правило уже проверено для этого случая), вы можете поместить это Item
в начало результата используя построение списка, и приступите к фильтрации Rest
списка.
filter_doubles([Item | Rest], [Item | Tail]) :- filter_doubles(Rest, Tail).
Обратите внимание, что при попытке выполнить накопление с помощью выражения, такого как Result = [Ah|Result]
, Prolog создает бесконечно рекурсивную структуру данных: Result
объединяется со списком, в котором Ah
в качестве заголовка и Result
в качестве хвоста, который объединяется со списком, имеющим Ah
в качестве головы и Result
в качестве хвоста, который объединяется со списком, имеющим Ah
в качестве головы и Result
в качестве хвоста, и так далее, и так далее, и так далее.