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

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

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

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