Декларативная интерпретация программы конкатенации списков на Прологе

У меня очень простая проблема: напишите программу Prolog, которая реализует функцию Prolog add, которая объединяет две строки и работает следующим образом:

  1. добавить ([a, b], [c, d], X). ---> X = [a, b, c, d]
  2. добавить ([a, b], X, [a, b, c, d]). ---> X = [c, d]
  3. добавить ([a, b], [X, d], [a, b, c, d]). ---> X = c
  4. добавить (X, Y, [a, b, c, d]). ---> X = [] и Y = [a, b, c, d)

Итак, у меня есть два следующих решения, и я не уверен, верна ли моя декларативная интерпретация:

1) РЕШЕНИЕ 1:

myappend1([],L,L).

myappend1([X|L1],L2,[X|L3]) :- myappend1(L1,L2,L3).

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

Факт говорит о том, что: если первый список (L1) пуст, а второй список (L2) не пуст, то это ИСТИНА, что конкатенация L1 * L2 равна L2

Если факт, что это неправда, это означает, что первый список не пуст, и поэтому конкатенация первого списка и второго списка неверна, это второй список

Итак, позвольте мне назвать первый список L1, второй список L2 и третий список L3, тогда правило отвечает ИСТИНА, если L3 является конкатенацией L1 и L2, в противном случае - ложью.

Я думаю, что декларативное значение этого правила таково: заголовок правила истинен, если истинно тело правила.

  • В заголовке извлеките первый элемент X из списка L1 и из списка L3 (и попытайтесь объединить, если оно соответствует, продолжайте, иначе это означает, что третий список это не конкатенация для первого и второго списка)
  • В теле вызовите функцию в первом списке без элемента X, втором списке и списке L3 (которые представляют конкатенацию)

Когда он достигает базового случая, в котором у меня есть продемонстрированный факт myappend1 ([], L, L)., который является правдой, программа выполняет возврат к предыдущему прошлому, и поскольку элемент X первого список, объединенный с элементом X третьего списка, он может сделать это также на этом вычислительном проходе он имеет значение ИСТИНА и вернуться назад, пока не достигнет первого утверждения

Это правильная декларативная интерпретация?

2) ВТОРОЕ РЕШЕНИЕ:

myappend2([],L,L).

myappend2(L1,L2,L3) :-  L1=[X|T],               % Dimostra questo predicato AND
                    L3=[X|L4],      % Dimostra questo predicato AND
            myappend2(T,L2,L4).     % Dimostra questa funzione

Как и в предыдущем решении, этот факт просто говорит о том, что: если первый список (L1) пуст, а второй список (L2) не пуст, то это ИСТИНА, что конкатенация L1 * L2 равна L2

Если факт, что это неправда, это означает, что первый список не пуст, и поэтому конкатенация первого списка и второго списка неверна, это второй список

Если факт, что это не так, Пролог вызывает правило, и это правило означает, что: заголовок правила истинен, если тело правила истинно.

В этом случае я могу прочитать это так:

Конкатенация L1 и L2 является L3 ИСТИНА, если верно, что:

  • Текущий первый элемент X L1 объединяется с текущим первым элементом списка конкатенации, и myappend2 вызывается в первом подсписке L2 и третьем подсписке, это правда

Это правильно?

для меня так сложно декларативно рассуждать :-(


person AndreaNobili    schedule 19.03.2013    source источник


Ответы (2)


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

Давайте начнем.

append([], L, L).

Вы сказали:

Если первый список (L1) пуст, а второй список (L2) не пуст, то ИСТИНА, что конкатенация L1 * L2 равна L2

Фактически это правило ничего не говорит о том, пуст ли L2 - или даже список! - или нет. Он просто говорит, что пустой список, добавленный к чему-то еще, - это что-то еще. Наблюдать:

?- append([], foo, X).
X = foo.

Декларативное прочтение здесь таково: «пустой список, добавленный к L, равен L.»

Если факт, что это неправда, это означает, что первый список не пуст, и поэтому конкатенация первого списка и второго списка неверна, это второй список

Да, это правильно, но Prolog не вникает так глубоко в тело. Он просто говорит: «Первый список не пуст, поэтому это правило не соответствует; продолжаем».

Следующее правило:

myappend1([X|L1], L2, [X|L3]) :- myappend1(L1,L2,L3).

Мне ваш комментарий кажется чрезмерно сложным. Я бы сказал, что это правило гласит: «myappend1 списка [X, за которым следует L1] - L2 - это список [X, за которым следует L3], если myappend1 списка L1 - L2 - это L3». Однако последствия этого чтения в точности такие, как вы описали.

Следовательно, ваше понимание того, что происходит в первой версии, правильное.

Второе решение механически точно такое же, как первое. Единственная разница в том, что мы переместили объединение из заголовка предложения в тело. Эта версия, на мой взгляд, явно уступает, потому что все, что в ней было сделано, - это дополнительная работа для читателя.

Я думаю, что проблема, с которой вы столкнулись, заключается в том, что ваши декларативные рассуждения тесно связаны с вычислительной машиной Пролога. Более чистое декларативное чтение, подобное тому, что я предоставил, проще и больше похоже на то, что говорит Пролог (и меньше связано с тем, как он оценивается).

Вам потребуется практика, чтобы разделить эти понятия, но я думаю, что это поможет вам поправиться (очевидно, это то, что вас беспокоит). А пока нет ничего плохого в том, чтобы прийти сюда и попросить помощи, как вы это делали, когда запутались. :)

Дайте мне знать, если я смогу помочь больше!

person Daniel Lyons    schedule 19.03.2013
comment
Хорошо, спасибо за прекрасное объяснение !!! Мне так плохо, потому что я думаю, что никогда не смогу хорошо выучить такие рассуждения :-( и для этого экзамена я должен реализовать проект на Прологе ... и это мой последний экзамен перед получением степени: - / - person AndreaNobili; 19.03.2013
comment
С вами все будет в порядке, просто расслабьтесь, сделайте глубокий вдох и спросите себя, как код будет выглядеть как чистая логика на бумаге, а затем наложите свое понимание Пролога. Практика очень поможет. - person Daniel Lyons; 19.03.2013

Когда вы пытаетесь выяснить декларативное значение предиката, вы спрашиваете: для каких решений применим этот предикат?

Предложения 1 Пролога независимо вносят свой вклад в набор решений. Поэтому для установления любых связей между пунктами требуется дополнительная проверка. Очень легко сделать некоторые предположения, которые не соответствуют действительности:

myappend1([],L,L). Если факт it не соответствует действительности, это означает, что первый список не пустой и поэтому ...

Считайте цель, myappend1([],[],[a]). Факт неприменим, все равно первый список пуст. Здесь вы пытаетесь операционализировать значение предложения. Это очень соблазнительно, поскольку большую часть языков программирования можно понять, только представив, как что-то происходит, шаг за шагом. Сложность Prolog заключается в попытке игнорировать такие детали, не игнорируя полностью процедурные аспекты.

myappend1([X|L1],L2,[X|L3]) :- myappend1(L1,L2,L3).

Чтобы прочитать правила, в частности рекурсивные правила, полезно взглянуть на :-, который представляет собой рендеринг ← 1970-х годов. Итак, это стрелка, но она идет справа налево. Следовательно, вы можете читать эти правила следующим образом, начиная с правой стороны:

При условии, что myappend(L1,L2,L3) удерживается, теперь переместите :- в левую сторону также myappend([X|L1],L2,[X|L3]).

Иногда даже лучший способ прочитать такое правило - полностью прикрыть голову и спросить

??? :- myappend1(L1,L2,L3).

Предположим, я знаю несколько L1, L2, L3, которые подходят для myappend1(L1,L2,L3)., что я могу сделать из этого? Есть что-нибудь интересное? Есть ли что-нибудь связанное с этим, что я могу легко построить из этих трех значений?

Это то, что вначале несколько отвратительно, потому что вы можете сказать: «Но как я узнаю, что такое существует?» Что ж, вы этого не сделаете. Вы только предполагаете, что он существует. Если его никогда не будет, то вы никогда не сможете сделать такой вывод.

Многие пытаются читать правила слева направо, но хотя Пролог фактически выполняет их слева направо, смысл, который они охватывают, легче понять, двигаясь в направлении вывода. Когда Prolog выполняет правило слева направо, он не знает, сработает это или нет. Таким образом, казнь может носить чисто умозрительный характер. Подумайте о append(L1,[z],[a,b,c,d,e]). Здесь Prolog применит это правило для каждого элемента списка. Но все это напрасно. То есть в конечном итоге выйдет из строя.


Мелкий шрифт

1 Собственно, чистое, монотонное подмножество Prolog.

person false    schedule 19.03.2013
comment
Сложность Prolog заключается в попытке игнорировать такие детали, не игнорируя полностью процедурные аспекты. Это должен быть конец первого абзаца в каждой книге по Прологу. - person Daniel Lyons; 19.03.2013