Присоединиться к двум спискам в прологе? - PullRequest
0 голосов
/ 30 июня 2019

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

joinL([22,33,44],[1,2,3],L)

Это покажет L = [22,33,44,1,2,3].

Чтобы сделать это, я попытался написатьПредикат выглядит следующим образом:

joinL([],L2,L2).
joinL([H|T],L2,L):-joinL(T,L2,L),L = [H|L].

Но когда я запрашиваю

joinL([22,33,44],[1,2,3],L)

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

Я хочу спросить: «Как мой код стал неправильным?», Я НЕ спрашиваю «Как написать предикат, объединяющий два списка в прологе?»потому что я могу легко гуглить, и когда сравниваю с моим кодом, я с любопытством хочу узнать, почему я ошибаюсь с моим кодом.Может кто-нибудь мне помочь!Спасибо всем за чтение и ответ на мой вопрос!

1 Ответ

2 голосов
/ 30 июня 2019

Проблема в том, что вы используете = так же, как если бы вы использовали назначение:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...