Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции по ФиЛП для ИВТ.docx
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
147.27 Кб
Скачать

5.11. Списки

Список – это объект, который содержит конечное число других объектов. Список в ПРОЛОГе можно приблизительно сравнить с массивами в других языках, но для списков нет необходимости заранее объявлять размерность.

Список в ПРОЛОГе заключается в квадратные скобки и элементы списка разделяются запятыми. Список, который не содержит ни одного элемента, называется пустым списком.

Примеры списков:

список, элементами которого являются целые числа: [1, 2, 3]

список, элементами которого являются строки: [“One”, “Two”, “Three”]

пустой список: []

Элементами списка могут быть списки: [[-1,3,5],[6,4,2,8]]

Чтобы использовать в ПРОЛОГ-программе списки, необходимо в разделе DOMAINS описать тип домена в формате.

<имя домена> = <тип элементов>*

Например,

DOMAINS

list = <integer>*

Список является рекурсивным объектом. Он состоит из головы (первого элемента списка) и хвоста (все последующие элементы). Хвост также является списком.

Например, [A, B, C] - список , у которого А – голова, [B,C] - хвост

В ПРОЛОГе имеется операция “|”, которая позволяет делить список на голову и хвост.

[A, B, C] = [A | [B, C] ] = [A | [B | [C] ] ] = [A | [B | [C | [ ] ] ] ]

Пустой список нельзя разделить на голову и хвост. Такая структура позволяет использовать рекурсию для обработки списка.

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

DOMAINS

list = integer *

PREDICATES

writelist(list)

CLAUSES

writelist ([]).

writelist ([A | Z]) :- write (A), nl, writelist (Z).

GOAL

writelist([10, 20, 30, 40]).

Результат выполнения программы:

10

20

30

40

В данной программе A – голова списка, Z – его хвост

5.12. Стандартные задачи обработки списка

К наиболее типичным задачам обработки списков относятся:

1) генерирование списка;

2) объединение списков;

3) поиск заданного элемента в списке;

4) удаление указанного элемента из списка;

5) вставка указанного элемента в список.

1. Генерирование списка из (n2-n1) последовательных целых чисел, начиная с n1.

Для формирования списка создадим отношение genlist(N1, N2, L), где

N1 – начальный элемент списка

N2 – его верхняя граница

L – список

Возможны случаи:

1) если N1 = N2, тогда N1 – не добавляется в список и L – пустой список.

genlist (N2, N2, [ ]) – первая часть правила (условие останова).

2) если N1 < N2, то N1 помещаем в голову списка, затем увеличиваем N1 на 1 и для нового значения снова вызывается genlist. Получаем вторую часть правила:

genlist (N1, N2, [N1 | L]): - N1 < N2, N = N1+1, genlist (N, N2, L).

Таким образом, правило формирования списка имеет вид:

genlist (N2, N2, [ ]). % нерекурсивная часть правила

genlist (N1, N2, [N1 | L]): - % рекурсивная часть правила

N1 < N2, N = N1+1, GenList (N, N2, L).

Например, для формирования списка [2, 3, 4, 5] следует указать запрос:

GOAL

GenList (2, 6, L), Write (L), nl.

2. Объединение списков.

Создадим отношение append(L1, L2, L3), где L1, L2 – исходные списки, L3 – список, полученный в результате присоединения L2 к L1.

Возможны случаи:

1) если L1 пустой список, то результирующий список L3 равен списку L2. Получаем первую (нерекурсивную) часть правила:

append ([ ], L, L).

2) если L1 не пустой список, то делим его на голову и хвост ( [X | L1]). Вторая (рекурсивная) часть правила:

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

Таким образом, правило объединения списков имеет вид:

append ([ ], L, L).

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

Например, для объединения списков [1, 2, 3] и [4, 5] следует написать запрос:

GOAL

append ([1, 2, 3], [4, 5], L) write [L].

Результат:

[1, 2, 3, 4, 5]

При выполнении программы из L1 выделяется и заносится в стек по одному элементу (голова списка) до тех пор, пока L1 не станет пустым. После этого выполняется первая часть правила и L3 получает значение L2, то есть L3 = [4, 5] начинается выгрузка элементов из стека и добавление их в голову L3.

Правило append обладает большое гибкостью и позволяет решать множество задач по обработке списка. Его можно использовать для решения обратной задачи: разбиение исходного списка на две части.

Например, чтобы определить все возможные способы разбиения списка [1,2,3], следует составить запрос:

GOAL

append (L1, L2, [1,2,3]), write (“L1=”, L1, “L2=”, L2), fail, nl.

Результат:

L1 = [ ] L2 = [1, 2, 3]

L1 = [1] L2 = [2, 3]

L1 = [1, 2] L2 = [3]

L1 = [1, 2, 3] L2 = [ ]