Вы хотите вычислить список с элементами, удовлетворяющими некоторым свойствам из данного списка.Списки в Прологе имеют очень простое представление.Пустой список представлен []
.Непустой список - это последовательность элементов, разделенных запятой.Например, [1,2,3]
.Prolog также предоставляет удобную нотацию для разделения списка между его головой (или первым элементом) и хвостом (список с оставшимися аргументами):
?- [1,2,3] = [Head| Tail].
Head = 1,
Tail = [2, 3].
Прогулка по списку (от его первого элемента до последнего элемента)) можно легко сделать с помощью простого рекурсивного предиката.Тривиальный случай, когда список пуст:
walk([]).
Если список не пуст, мы переходим к хвосту списка:
walk([Head| Tail]) :- walk(Tail).
Однако, если вы попробуете это определение предикатапрактически в любой системе Prolog он предупредит вас, что Head
является одноэлементной переменной .Это означает, что переменная появляется один раз в предложении предиката.Вы можете устранить предупреждение, заменив переменную Head
анонимной переменной (которую мы можем интерпретировать как переменную "пофиг").Таким образом, в настоящее время мы имеем:
walk([]).
walk([_| Tail]) :- walk(Tail).
Мы можем попробовать это с нашим примером списка:
?- walk([1,2,3]).
true.
Пролог является реляционным языком, что произойдет, если мы вызовемпредикат walk/1
с переменной вместо этого?
?- walk(List).
List = [] ;
List = [_4594] ;
List = [_4594, _4600] ;
List = [_4594, _4600, _4606]
...
Теперь вернемся к исходной проблеме: построение списка из элементов другого списка.Мы хотим обработать каждый элемент списка ввода и, если он удовлетворяет некоторому свойству, добавить его в список вывода.Нам нужны два аргумента.Простой случай (или base case) снова, когда список ввода пуст:
process([], []).
Общий случай (или рекурсивный регистр) будет:
process([Head| Tail], [Head| Tail2]) :-
property(Head),
process(Tail, Tail2).
в предположении предиката property/1
, который имеет значение true, когда его аргумент удовлетворяет некоторому свойству.В вашем случае, быть четным, отрицательным целым числом.Но не все элементы удовлетворят свойству.Чтобы справиться с этим случаем, нам нужно третье предложение, которое пропустит элемент, который не удовлетворяет свойству:
process([Head| Tail], List) :-
\+ property(Head),
process(Tail, List).
Предикат \+/1
- это стандарт Пролога Предикат отрицания : этоtrue, если его аргумент равен false.
Давайте попробуем наш предикат process/2
, определив предикат property/1
, который будет истинным, если аргумент является целым нулем:
property(0).
Примертогда вызов будет:
?- process([1,0,2,0,0,3,4,5], List).
List = [0, 0, 0] ;
false
Мы успешно написали предикат, который извлекает все нули из списка.Обратите внимание, что наш запрос имеет одно решение .Если мы введем ;
для запроса следующего решения в командной строке, интерпретатор верхнего уровня Prolog сообщит нам, что решений больше нет (точная распечатка зависит от выбранной системы Prolog; некоторые напечатают, например, no
вместо false
, но смысл тот же).
Можете ли вы теперь решить исходный вопрос, определив подходящий предикат property/1
?
Обновление
Вы можете объединить два рекурсивных предложения в одном, написав, например:
process([Head| Tail], List) :-
( % condition
property(Head) ->
% then
List = [Head| Tail2],
process(Tail, Tail2)
; % else
process(Tail, List)
).
В этом случае мы используем стандарт Prolog if-then-else управляющую конструкцию .Обратите внимание, однако, что эта конструкция делает неявное cut в условии.Т.е. мы берем только первое решение для предиката property/1
и отбрасываем любые другие потенциальные решения.Использование этой управляющей конструкции также не позволяет использовать предикат process/2
в reverse (например, вызывать его с несвязанным первым аргументом и связанным вторым аргументом) или использовать его для генерации парусловия, которые удовлетворяют отношению (например, вызывая его с обоими аргументами, не связанными).Эти проблемы могут быть или не быть существенными в зависимости от свойства, которое вы используете для фильтрации списка, и от деталей практической проблемы, которую вы решаете.Возможны более сложные альтернативы, но они выходят за рамки этого вводного ответа.