Сибирский университет потребительской кооперации
Опубликован: 04.05.2005 | Доступ: свободный | Студентов: 4322 / 1347 | Оценка: 4.45 / 4.22 | Длительность: 12:28:00
ISBN: 978-5-9556-0034-5
Лекция 7:

Списки

< Лекция 6 || Лекция 7: 12345 || Лекция 8 >

Первый способ. Добавим в правило проверку на несовпадение первого элемента списка с искомым элементом, чтобы поиск элемента в хвосте списка производился только тогда, когда первый элемент списка не является искомым. Модифицированный предикат будет выглядеть следующим образом:

member2(X,[X|_]).
member2(X,[Y|T]):–
                  X<>Y, member2(X,T).

Заметим, что эту модификацию предиката member нельзя использовать для получения всех элементов списка. Если подставить в качестве первого аргумента несвязанную переменную, то при попытке согласования подцели правила неозначенная переменная X будет сравниваться с неозначенной переменной Y. Получим сообщение об ошибке "Free variable in expression".

Второй способ. Добавим в факт отсечение, чтобы в ситуации, когда искомый элемент оказался первым элементом списка, не производился лишний поиск в хвосте исходного списка. Получим:

member3(X,[X|_]):–!.
member3(X,[_|T]):–
                  member3(X,T).

Заметим, что хотя эта модификация предиката member более эффективна, чем исходная, за счет того, что она не выполняет поиск в хвосте после того, как искомый элемент найден, ее можно использовать только для того, чтобы проверить, имеется ли в списке конкретное значение. Если мы попытаемся применить ее для получения всех элементов списка, подставив в качестве первого аргумента несвязанную переменную, то результатом будет только первый элемент списка. Отсечение не позволит нам получить оставшиеся элементы.

Пример. Создадим предикат, позволяющий соединить два списка в один. Первые два аргумента предиката будут представлять соединяемые списки, а третий — результат соединения.

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

conc([ ], L, L). /* при присоединении пустого списка
                    к списку L получим список L */
conc([H|T], L, [H|T1]) :–
conc(T,L,T1). /* соединяем хвост и список L, получаем
                 хвост результата */

Заметим, что этот предикат также можно применять для решения нескольких задач.

Во-первых, для соединения списков. Например, если задать вопрос

conc([1, 2, 3], [4, 5], X)

то получим в результате

X= [1, 2, 3, 4, 5]

Во-вторых, для того, чтобы проверить, получится ли при объединении двух списков третий. Например, на вопрос:

conc([1, 2, 3], [4, 5], [1, 2, 5]).

ответом будет, конечно, No.

В-третьих, можно использовать этот предикат для разбиения списка на подсписки. Например, если задать следующий вопрос:

conc([1, 2], Y, [1, 2, 3]).

то ответом будет Y=[3].

Аналогично, на вопрос

conc(X, [3], [1, 2, 3]).

получим ответ X=[1, 2].

И, наконец, можно спросить

conc(X, Y, [1, 2, 3]).

Получим четыре решения:

X=[], Y=[1, 2, 3]
X=[1], Y=[2, 3]
X=[1, 2], Y=[3]
X=[1, 2, 3], Y=[]

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

conc(L, [2|R], [1, 2, 3, 2, 4]).

Получим два решения:

L=[1], R=[3, 2, 4].
L=[1, 2, 3], R=[4]

В-пятых, на основе нашего предиката conc можно создать предикат, находящий последний элемент списка:

last(L,X):–
          conc(_,[X],L).

Справедливости ради стоит заметить, что этот предикат можно реализовать и "напрямую", без использования предиката conc:

last2([X],X). /* последний элемент одноэлементного
                 списка — этот элемент */
last2([_|L],X):–
        last2(L,X). /* последний элемент списка совпадает
                       с последним элементом хвоста */

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

member4(X,L):–
             conc(_,[X|_],L).

В-седьмых, используя предикат, позволяющий объединить списки, можно создать предикат, проверяющий по двум значениям и списку, являются ли эти значения соседними элементами списка. Предикат будет иметь три параметра: первые два — значения, третий — список.

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

neighbors(X,Y,L):–
     conc(_,[X,Y|_],L). /* список L получается путем
                           объединения некоторого списка
                           со списком, голову которого
                           составляют элементы X и Y */

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

neighbors2(X,Y,L):–
          conc(_,[X,Y|_],L);
          conc(_,[Y,X|_],L). /* список L получается
                       путем объединения некоторого
                       списка со списком, голову
                       которого составляют элементы X
                       и Y или элементы Y и X */
< Лекция 6 || Лекция 7: 12345 || Лекция 8 >
Виктор Бондарь
Виктор Бондарь

После приведения формулы вида ПНФ к виду ССФ вы получаете формулу, в безквантовой матрице которой дизъюнкт содержит оба контранрных атома:. Как тогда проводить его унификацию, если в случае замены x на f(x) весь дизъюнкт обратится в единицу?

Ольга Потапенко
Ольга Потапенко

никак не могу увидеть тексты самих лекций.