Проблема в том, что вы используете =
так же, как если бы вы использовали назначение:
L = [H|L]
В языке с изменяющимся состоянием это означает, что все, что хранится в L
(который должен быть списком), становится новым списком, сделанным путем прикрепления H
вперед: [H|L]
В Прологе это говорит о том, что мы знаем о L
то, что он равен [H|L]
- равен самому себе с H
, прикрепленным вперед. Это невозможно для любого L
, хотя (на самом деле, если L
является бесконечным списком, содержащим только H
, но механизм проверки Пролога не достаточно хорош, чтобы справиться с , что ) , В этом препятствии поиск пролога завершается неудачей и возвращает «ложь» - нет решения для логической программы, которую вы ввели.
(еще после кофе)
Вот как об этом подумать:
Хорошо, я хотел бы изложить некоторые логические факты о проблеме «конкатенации списков», чтобы на основе этих логических фактов и с учетом двух полностью определенных списков L1
, L2
можно было выполнить пробный поиск Пролога. достаточно определить, как должен выглядеть составной список LJ
, чтобы вывести его полностью!
Мы решили указать предикат joinL(list1,list2,joinedlist)
, чтобы выразить это.
Сначала мы рассмотрим специальный край:
joinL([],L2,LJ) :- LJ = L2.
Итак, заявлено, что отношение joinL между пустым списком [] и объединенным списком «LJ» таково, что «LJ» обязательно равно «L2».
логическое чтение :
(LJ = L2) → joinL ([], L2, LJ)
Оперативное чтение :
Чтобы доказать joinL([],L2,LJ)
, вы должны доказать LJ = L2
(который может быть проверен, если LJ
и L2
уже известны, или может быть добавлен к известным ограничениям решения, если нет.
Существует также чтение резолюции SLD, где вы добавляете отрицание joinL([],L2,LJ)
к вашему набору логических фактов, а затем пытаетесь доказать ⊥ (противоречие также известно как пустое утверждение), используя разрешение, но у меня есть не нашел такой точки зрения особенно полезной.
В любом случае, давайте расскажем больше о крайних случаях:
joinL([],L2,LJ) :- LJ = L2.
joinL(L1,[],LJ) :- LJ = L1.
joinL([],[],LJ) :- LJ = [].
Это позволит уже движку Prolog proof полностью определять LJ
всякий раз, когда любой из L1
и L2
является пустым списком.
Обычно он сокращается до:
joinL([],L,L).
joinL(L,[],L).
joinL([],[],[]).
(Вышеуказанное сокращение будет невозможно, например, в Picat )
И третье утверждение можно отбросить, потому что два других «включают его» - они охватывают этот случай в более общем плане. Таким образом:
joinL([],L,L).
joinL(L,[],L).
Теперь для случая непустых списков. Обширная часть логического программирования касается индуктивных (или рекурсивных) определений предикатов (см. this ), так что давайте пойдем:
joinL([H|T],L2,LJ) :- LJ = [H|LX], joinL(T,L2,LX).
Опять же, это просто спецификация, где мы говорим, что объединение непустого списка [H|T]
и любого списка L2
- это список LJ
, такой, что LJ
состоит из H
и списка LX
и LX
- это объединение T
и L2
.
Это полезно для механизма проверки Пролога, потому что он дает больше информации о LJ
(на самом деле, он определяет, что является первым элементом LJ
) и уменьшает проблему, чтобы узнать больше, используя тот же предикат, но проблема немного ближе к базовому случаю с пустым списком: joinL(T,L2,LX)
. Если доказательство пройдет по этому маршруту, оно в конечном итоге достигнет joinL([],L2,LX)
, узнайте, что L2 = LX
, и сможете успешно вернуться со своего спуска.
joinL([H|T],L2,LJ) :- LJ = [H|LX], joinL(T,L2,LX).
обычно сокращается до
joinL([H|T],L2,[H|LX]) :- joinL(T,L2,LX).
Похоже, мы покрыли все:
joinL([],L,L).
joinL(L,[],L).
joinL([H|T],L2,[H|LX]) :- joinL(T,L2,LX).
Мы можем даже отбросить второе утверждение, так как оно покрыто рекурсивным спуском с L2, всегда равным '[]'. Это дает нам более короткую программу, которая без необходимости записывает циклы, когда L2 равен '[]':
joinL([],L,L).
joinL([H|T],L2,[H|LX]) :- joinL(T,L2,LX).
Давайте проверим это.Нужно использовать модульные тесты , но я не могу быть обеспокоен сейчас, а просто запустите их в SWISH .Давайте посмотрим, что Пролог может узнать о X
:
joinL([],[],X). % X = []
joinL([1,2],[],X). % X = [1,2]
joinL([],[1,2],X). % X = [1,2]
joinL([3,4],[1,2],X). % X = [3,4,1,2]
joinL([1,2],[3,4],X). % X = [1,2,3,4]
Можно полностью ограничить результат, превратив Пролог в средство проверки:
joinL([3,4],[1,2],[3,4,1,2]). % true
joinL([3,4],[1,2],[1,1,1,1]). % false
Иногда предикат тоже работает в обратном направлении,но часто требуется более тщательный дизайн.Не здесь:
joinL([3,4],L2,[3,4,1,2]). % L2 = [1, 2]
Для этого Пролог предлагает второе решение может существовать, но его, конечно, нет:
joinL(L1,[3,4],[1,2,3,4]). % L1 = [1, 2]
Найти мне что-то невозможное:
joinL(L1,[3,4],[1,2,100,100]). % false