Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Л.Р._Ш.И_2-6 / L5_LIST

.doc
Скачиваний:
3
Добавлен:
20.02.2016
Размер:
111.1 Кб
Скачать

AIS_LZ_LIST 7

ЛАБОРАТОРНАЯ РАБОТА

Обработка списков на Прологе

Цель работы:

Разработка и отладка программ обработки списков. Приобретение и закрепление практических навыков составления программ на языке Prolog.

Подготовка к работе:

Изучить правила описания списков и типовые приемы обработки списков.

Порядок выполнения работы:

1. Загрузить компилятор языка логического программирования PROLOG.

2. Отладить программу печати списка.

domains

list = integer*

predicates

print(list)

clauses

print([]). если список пуст, то ничего не делать.

print(H | T):- write(H), напечатай значение головы

nl, перейди на следующую строку

print(T). напечатай хвост списка

Переставить два последних оператора программы. Что получиться?

3. Отладить программу вычисления длины списка двумя способами – общей и хвостовой рекурсией.

Определение длины списка (общая рекурсия)

длина(списка) = Длина(хвоста списка) + 1:

длина([]) = 0.

domains

list = integer*

predicates

len(list, integer)

clauses

len([]), 0). длина пустого списка

len([_|Tail], N):- len(Tail, N1), вычисление длины хвоста

N=N1+1. вычисление общей длины списка

Определение длины списка методом хвостовой рекурсии.

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

domains

list = integer*

predicates

len(list, integer)

lenv(list, integer, integer)

clauses

len(L, N):- lenv(L,N,0),!. третий аргумент – начальная длина списка равная нулю.

lenv([], N, N). когда список станет пустым, накопленное значение NT копируется в переменную N

lenv([_|Tail], N,NT):- NT1=NT+1,

lenv(Tail, N, NT1). рекурсивный вызов.

4. Составить и отладить программу (самостоятельно) вычисления суммы элементов списка методом общей и хвостовой рекурсии.

5. Отладить программу определения принадлежности элемента списку.

Введем предикат member(X, L). Рассмотрим два случая.

Первый случай – элемент X является первым элементом списка. Тогда выделив голову списка и сравнив ее с элементом X, при совпадении даем положительный ответ.

Второй случай - элемент X не является первым элементом списка. Тогда решаем задачу определения принадлежности элемента для хвоста списка. Текст программы приведен ниже.

domains

list = integer*

predicates

member(integer, list)

clauses

member(X, [X | _ ]).

member(X, [ _ | T ]) :- member(X, T).

Каков результат работы программы, если задать цель в виде:

member(X, [1, 2, 5]) и member(2, [1, 3, N]) ?

6. Отладить программу удаления элемента X из списка L.

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

domains

list = integer*

predicates

delete(integer, list, list)

clauses

delete(X, [X | T ], T).

delete(X, [ H | T ],[H | L]) :- delete(X, T ,L).

Какой смысл имеют цели:

delete(Z, [1, 2, 5], [1, 5]) delete(4, U, [1, 5])?

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

7. Отладить программу слияния и объединения списков.

Слияние списков merge(L1, L2, L)

domains

list = integer*

predicates

merge(list, list, list)

clauses

merge([], L2, L2).

merge([H | T], L2, [H | L3]) :- merge(T, L2, L3).

Если первый список пустой, то результат – это второй список. В противном случае необходимо слить хвост первого списка со вторым списком и добавить к полученному списку голову первого списка.

Объединение списков (в теоретико–множественном смысле). Основное отличие заключается в том, что элементы списка не должны повторяться, как это подобает множествам. Поэтому, если голова первого множества содержится во втором множестве, то ее не надо добавлять к полученному списку.

domains

list = integer*

predicates

union(list, list, list)

clauses

union([], L2, L2).

union([H | T], L2, L3) :-

member(H, L2), проверяем, входит ли голова H во второй список L2

union(T, L2, L3),!.

union([H | T], L2, [H | L3]) :-

union(T, L2, L3).

  1. Индивидуальные задания. В таблице приведены 40 вариантов задач, разбитых на 6 групп. Необходимо решить по одной задаче из 5 выбранных групп. Номер варианта в каждой группе определяется по формуле: N= (ZZ mod G) +1, где ZZ – две последние цифры номера студенческой зачетки, G – количество вариантов в группе.

Содержание отчета

Отчет содержит название и цель работы, тексты программ и результаты тестирования.

Контрольные вопросы

1. Как компилятор распознает списки (константы и переменные)?

2. Каким знаком обозначается операция выделения головы списка, хвоста списка?

3. Как обозначается пустой список?

4. Из каких частей состоит любое рекурсивное правило?

5. Когда заканчивается рекурсивный вызов правил?

Варианты заданий

Балл

Выполняемая операция

Предикат

1 группа Теоретико-множественные операции над списками

1

18

Объединение двух упорядоченных по возрастанию списков

union(L1, L2, L3)

2

18

Объединение двух упорядоченных по убыванию списков

union(L1, L2, L3)

3

15

Пересечение двух неупорядоченных списков

cross(L1, L2, L3)

4

18

Пересечение двух упорядоченных по убыванию списков

cross(L1, L2, L3)

5

18

Пересечение двух упорядоченных по возрастанию списков

cross(L1, L2, L3)

6

15

Вычитание двух неупорядоченных списков

diflist(L1, L2, L3)

7

18

Вычитание двух упорядоченных по убыванию списков

diflist(L1, L2, L3)

8

18

Вычитание двух упорядоченных по возрастанию списков

diflist(L1, L2, L3)

2 группа Вставка элемента в список

1

10

Вставка элемента X в упорядоченный по возрастанию список

insert(X, L1, L2)

2

10

Вставка элемента X в упорядоченный по убыванию список

insert(X, L1, L2)

3

10

Вставка элемента X на N-тое место в неупорядоченном списке

insert(X, N, L1, L2)

4

12

Вставка элемента X в список перед элементом Y списка

insert(X, Y, L1, L2)

5

12

Вставка элемента X в список после элемента Y списка

insert(X, Y, L1, L2)

6

10

Вставка элемента X в список вместо элемента Y списка

insert(X, Y, L1, L2)

7

5

Выделение N-того элемента из списка

member(N, L, X)

3 группа Удаление элементов из списка

1

5

Удаление N-того элемента из списка

del(N, L1, L2)

2

12

Удаление последнего элемента списка

del(L1, L2)

3

15

Удалить из списка элементы с четными номерами

del(L1, L2)

4

15

Удалить из списка отрицательные элементы

del(L1, L2)

5

15

Удалить из списка четные (N mod 2 = 0) элементы

del(L1, L2)

4 группа Слияние и разделение списка

1

10

Слияние двух упорядоченных по возрастанию списков

append(L1, L2, L3)

2

10

Слияние двух упорядоченных по убыванию списков

append(L1, L2, L3)

3

10

Разделить список на два списка, отличающихся по длине не более чем одним элементом

Split(L1, L2, L3)

4

12

Разделение списка L1 на два списка по компаратору К: L2 содержит числа меньше чем К, L3 - больше чем К

Split(K, L1, L2, L3)

5

15

Разделение списка L1 на два списка: L2 содержит четные числа, L3 - нечетные

Split(L1, L2, L3)

6

14

Разделение списка L1 на два списка. L2 содержит положительные числа, L3 - отрицательные

Split(L1, L2, L3)

7

18

Разделение списка L1 на два списка: L2 содержит числа меньше чем первый элемент списка, L3 - большие чем первый элемент списка

Split(L1, L2, L3)

5 группа Вычисление характеристик списка

1

8

Вычисление количества четных N1 и нечетных чисел - N2 в списке L

count(L, N1, N2)

2

6

Вычисление количества положительных - N1 и отрицательных чисел - N2 в списке L

count(L, N1, N2)

3

8

Вычисление суммы четных – S1 и нечетных чисел - S2 в списке L

sum(L, N1, N2)

4

6

Вычисление суммы положительных – S1 и отрицательных чисел - S2 в списке L

sum(L, N1, N2)

5

5

Вычисление порядкового номера N элемента X в списке L

number(X, L, N)

6

12

Является ли список L1 подсписком списка L2

sublist(L1, L2)

7

14

Нахождение максимального элемента списка методом деления списка пополам. В каждой половинке найти максимум и сравнить их.

max(L, X_max)

8

10

Нахождение максимального элемента списка путем нахождения его в хвосте и сравнения с головой списка

max(L, X_max)

6 группа (дополнительная) Сортировка списка

1

15

Сортировка методом простого включения (отсортировать хвост списка и вставить голову в нужное место)

sort(L1, L2)

2

20

Сортировка методом простого выбора (найти максимальный элемент списка и формировать отсортированный список восходящей рекурсией)

sort(L1, L2)

3

20

Сортировка методом простого обмена (пузырька)

sort(L1, L2)

4

25

Быстрая сортировка списка (разделить список на два подсписка: элементы первого подсписка меньше, в второго – больше первого элемента исходного списка; подсписки отсортировать и соединить)

sort(L1, L2)

5

12

Инверсия списка (расположение элементов списка в обратном порядке)

invers(L1, L2)

Справочный материал

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

[1,3,5], [a,b,e,f], [jem,apple,jin]. Список может содержать произвольное количество элементов, и их количество может изменяться в процессе работы программы. Поэтому списки относятся к динамическим структурам. Частными случаями списка являются список, состоящий из одного элемента - [х] и пустой список - [].

Для списков базовыми операциями являются:

- выделение головы списка car([1,3,5])=1,

- выделение хвоста списка cdr([1,3,5])=[3,5],

- добавление элемента в голову списка cons(1, [3,5])=[1,3,5].

Эти три операции подчиняются следующей аксиоме:

cons(car(L), cdr(L))=L

В языке Prolog описание списка осуществляется в секции domains. Признаком списка является наличие символа * после описания типа элемента.

Примеры

domains

list=integer* список целых чисел [3,5]

l_char=char* список символов [a, f, h, d]

l_col=color* список красок [red, green, yellow]

color=symbol

Все три базовые операции со списками в языке Prolog обозначаются одним символом – вертикальная черта ( | ): L=[Head | Tail].

В зависимости от ситуации, программы сопоставления и унификации самостоятельно выполняют операции разделения списка на голову Head и хвост Tail или добавления элемента Head к списку Tail.

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

- что делать с пустым списком?

- что делать со списком, у которого есть голова и хвост?

Соседние файлы в папке Л.Р._Ш.И_2-6