Материал: ЛиФП. Лабораторная работа 5

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

Федеральное агентство связи ФЕДЕРАЛЬНОЕ ГОСУДАРСТВЕННОЕ БЮДЖЕТНОЕ

ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ ВЫСШЕГО ОБРАЗОВАНИЯ «САНКТ-ПЕТЕРБУРГСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ ТЕЛЕКОММУНИКАЦИЙ ИМ. ПРОФ. М. А. БОНЧ-БРУЕВИЧА» (СПбГУТ)

Факультет инфокоммуникационных сетей и систем Кафедра программной инженерии и вычислительной техники

ЛАБОРАТОРНАЯ РАБОТА №5 по дисциплине «Логическое и функциональное программирование»

Выполнил: студент 3-го курса дневного отделения группы ИКПИ-85

Коваленко Леонид Александрович Преподаватель:

доцент кафедры ПИиВТ Ерофеев Сергей Анатольевич

Санкт-Петербург

2020

Постановка задачи

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

Схема решения

Алгоритм основан на поиске в глубину (англ. Depth-first search, DFS). Алгоритм поиска описывается рекурсивно: перебираем всех соседей

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

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

Код программы Входные данные поступают с клавиатуры либо файла, пример

содержимого которого приведен в табл. 1.

Таблица 1 — Входные данные в файле (слева) и описание ячеек (справа)

#####################

S — стартовая ячейка, с которой начинается путь

#

 

#

#

#

 

 

### #####

# ###

### #

F — финишная ячейка, на которой заканчивается путь

#

 

# S

# # #

 

 

# # # ##

# ###

# # #

* — ячейка-монета, через которую обязательно должен

# # # #

# # #

#

#

проходить путь

### # #

*## #

# ###

 

# # #

#

#

#

#

— пустая ячейка, через которую можно пройти

# #*

# #

### #

# # #

 

#F

*# #

#

 

# #

# — ячейка-стена, через которую нельзя пройти

#####################

 

Код программы приведен в табл. 2.

2

Таблица 2 — Код программы

NOWARNINGS

%Раздел описания доменов

DOMAINS

file = datafile % Файл datafile char_list = char* % Строка из символов matrix = char_list* % Матрица

pair = pair(integer, integer) % Пара значений координат (Left, Top) solution = pair* % Решение является массивом пар значений координат solutions = solution* % Множество решений

%Раздел описания предикатов

PREDICATES

length(matrix, integer) length(solution, integer) input(matrix) input_action(matrix, integer) read_matrix_from_console(matrix) read_matrix_from_file(matrix)

string_to_char_list(string, char_list) find_solution(matrix, solution) calc_stars_in_matrix(matrix, integer) calc_stars_in_list(char_list, integer) find_in_matrix(matrix, char, integer, integer) find_in_matrix(matrix, char, integer, integer, integer) find_in_list(char_list, char, integer, integer, integer)

find_in_list(char_list, char, integer, integer, integer, integer) find_path(matrix, integer, integer, integer, integer, integer, solution, solution) is_free_then_update(char, integer, integer)

contains(solution, pair) get(matrix, integer, char_list) get(char_list, integer, char) merge(solution, solution, solution) min_solution(solutions, solution) output(matrix, solution)

output_action(matrix, solution, integer) write_output_data(matrix, solution) write_output_data(matrix, solution, integer) write_row_with_solution(char_list, solution, integer)

write_row_with_solution(char_list, solution, integer, integer)

%Раздел описания внутренней цели

GOAL

input(InputMatrix), % Ввод исходных данных find_solution(InputMatrix, Solution), % Поиск минимального пути output(InputMatrix, Solution), % Вывод результата

readchar(_). % Завершение программы после нажатия клавиши клавиатуры

%Раздел описания предложений

CLAUSES

%Длина списка length([], 0). length([_|Xs], N) :-

length(Xs, P), N = P + 1.

%Ввод исходных данных input(Matrix) :-

write("-== MENU ==-"), nl, write("1. Read from console;"), nl, write("2. Read from file."), nl, write("Another button to exit"), nl,

readint(C), % Считывание целого числа input_action(Matrix, C), % Ввод length(Matrix, Length), Length >= 3;

write("Invalid input!"), nl, fail. % Если неверный ввод

%Ввод из диалогового окна

input_action(Matrix, 1) :- read_matrix_from_console(Matrix).

% Ввод из файла input_action(Matrix, 2) :-

write("File name: "), readln(FileName), % Ввод названия файла

existfile(FileName), % Существует ли файл openread(datafile, FileName), % Открытие файла для чтения readdevice(datafile), % Перенаправление ввода на файл read_matrix_from_file(Matrix), % Чтение closefile(datafile), !; % Закрытие файла

3

write("Error reading file!"), nl, fail.

% Чтение с консоли read_matrix_from_console([Head|Tail]) :-

write(">"), readln(T), % Чтение строки

string_to_char_list(T, Head), % Преобразование в список символов read_matrix_from_console(Tail).

read_matrix_from_console([]).

% Чтение с файла read_matrix_from_file([Head|Tail]) :-

readln(T), % Чтение строки

string_to_char_list(T, Head), % Преобразование в список символов read_matrix_from_file(Tail).

read_matrix_from_file([]).

%Конверт строки в список символов string_to_char_list("", []). string_to_char_list(String, [Head|ListTail]) :-

frontchar(String, Head, StringTail), % Head - первый символ, StringTail - остальные string_to_char_list(StringTail, ListTail).

%Поиск пути и генерация результата

find_solution(InputMatrix, Solution) :-

calc_stars_in_matrix(InputMatrix, Stars), % Считаем кол-во звездочек (монет) find_in_matrix(InputMatrix, 83, StartL, StartT), % Находим 'S' = 83 find_in_matrix(InputMatrix, 70, FinishL, FinishT), % Находим 'F' = 70

StartL >= 0, StartT >= 0, FinishL >= 0, FinishT >= 0, !, % Проверка (-1, если было

неуспешно find_in_matrix)

% Находим все решения

findall(List, find_path(InputMatrix, Stars, StartL, StartT, FinishL, FinishT, [], List), Solutions),

min_solution(Solutions, Solution). % Находим минимальное из всех решений

%Подсчет числа звездочек (монет) в матрице calc_stars_in_matrix([Head|Tail], N) :-

calc_stars_in_list(Head, NS), % Ищем в текущей строке calc_stars_in_matrix(Tail, NT), % Рекурсивно по каждой строке

N = NS + NT. % Суммируем calc_stars_in_matrix([], 0).

%Подсчет числа звездочек (монет) в списке

calc_stars_in_list([Head|Tail], N) :-

Head = 42, % Если текущий символ - '*' calc_stars_in_list(Tail, N1), % Рекурсивно смотрим хвост N = 1 + N1, !; % Подсчитываем

calc_stars_in_list(Tail, N). % Иначе просто рекурсивно смотрим хвост calc_stars_in_list([], 0).

% Поиск символа в матрице символов find_in_matrix([], _, -1, -1). find_in_matrix(Matrix, Char, Left, Top) :-

find_in_matrix(Matrix, Char, 0, Left, Top).

find_in_matrix([Head|Tail], Char, T, Left, Top) :- find_in_list(Head, Char, T, Lr, Tr), % Ищем в списке

Lr >= 0, Tr >= 0, Left = Lr, Top = Tr, !; % Если элемент найден

NewT = T + 1, find_in_matrix(Tail, Char, NewT, Left, Top). % Иначе смотрим другие строки find_in_matrix([], _, _, -1, -1).

% Поиска символа в списке символов find_in_list([], _, _, -1, -1). find_in_list(List, Char, T, Left, Top) :-

find_in_list(List, Char, 0, T, Left, Top).

find_in_list([], _, _, _, -1, -1). find_in_list([Head|Tail], Char, L, T, Left, Top) :-

Head = Char, Left = L, Top = T, !; % Если элемент найден

L1 = L + 1, find_in_list(Tail, Char, L1, T, Left, Top). % Иначе смотрим другие строки

% Нахождение пути

find_path(_, 0, CurrentL, CurrentT, CurrentL, CurrentT, SList, List) :-

List = SList, !. % Если все звездочки собраны и мы в нужной точке, то нужный путь найден find_path(_, _, CurrentL, CurrentT, CurrentL, CurrentT, _, _) :-

!, fail. % Если не все звездочки собраны и мы в нужной точке, то нужный путь не найден find_path(InputMatrix, Stars, CurrentL, CurrentT, FinishL, FinishT, SList, List) :-

% Если слева свободная ячейка (не '#'), то рекурсия туда

C = CurrentL - 1, not(contains(SList, pair(C, CurrentT))),

get(InputMatrix, CurrentT, CurrentList), get(CurrentList, C, X), is_free_then_update(X, Stars, S),

merge([pair(C, CurrentT)], SList, MList),

find_path(InputMatrix, S, C, CurrentT, FinishL, FinishT, MList, List);

4

% Если справа свободная ячейка (не '#'), то рекурсия туда C = CurrentL + 1, not(contains(SList, pair(C, CurrentT))),

get(InputMatrix, CurrentT, CurrentList), get(CurrentList, C, X), is_free_then_update(X,

Stars, S),

merge([pair(C, CurrentT)], SList, MList),

find_path(InputMatrix, S, C, CurrentT, FinishL, FinishT, MList, List);

% Если сверху свободная ячейка (не '#'), то рекурсия туда C = CurrentT - 1, not(contains(SList, pair(CurrentL, C))),

get(InputMatrix, C, CurrentList), get(CurrentList, CurrentL, X), is_free_then_update(X, Stars, S),

merge([pair(CurrentL, C)], SList, MList),

find_path(InputMatrix, S, CurrentL, C, FinishL, FinishT, MList, List);

% Если снизу свободная ячейка (не '#'), то рекурсия туда

C = CurrentT + 1, not(contains(SList, pair(CurrentL, C))),

get(InputMatrix, C, CurrentList), get(CurrentList, CurrentL, X), is_free_then_update(X,

Stars, S),

merge([pair(CurrentL, C)], SList, MList),

find_path(InputMatrix, S, CurrentL, C, FinishL, FinishT, MList, List).

%Проверка на пустоту и изменение Stars (если нужно) is_free_then_update(X, Stars, S) :-

X = 42, !, S = Stars - 1; % Если '*', то обновляем Stars

not(X = 35), S = Stars. % Если не '*' и не '#', то не обновляем Stars (иначе fail)

%Содержит ли список элемент X

contains([X|_], X).

contains([_|Tail], X) :- contains(Tail, X). contains([], _) :- fail.

%Получение элемента списка по индексу get([Head|_], 0, X) :- X = Head.

get([_|Tail], I, X) :- I1 = I - 1, get(Tail, I1, X). get([], _, _) :- fail.

%Объединение двух списков

merge([H|Xs], Zs, [H|Ts]) :- merge(Xs, Zs, Ts). merge([], Zs, Zs).

%Поиск минимального решения min_solution([Min], Min). min_solution([Head, Next|T], M) :-

length(Head, HL), length(Next, NL), HL <= NL,

min_solution([Head|T], M). min_solution([Head, Next|T], M) :-

length(Head, HL), length(Next, NL), HL > NL,

min_solution([Next|T], M).

%Вывод результата

output(InputMatrix, Solution) :- write("1. Write to console;"), nl, write("2. Write to file."), nl, write("Another button to exit"), nl, readint(C), % Считывание числа

output_action(InputMatrix, Solution, C); % Вывод write("The program = complete.").

% Вывод в диалоговое окно output_action(InputMatrix, Solution, 1) :-

write_output_data(InputMatrix, Solution).

% Вывод в файл

output_action(InputMatrix, Solution, 2) :- write("File name: "), readln(FileName), % Ввод названия файла

openwrite(datafile, FileName), % Открытие файла для записи writedevice(datafile), % Перенаправление вывода на файл write_output_data(InputMatrix, Solution), % Вывод closefile(datafile). % Закрытие файла

% Вывод результата в файл в виде write_output_data([], []).

write_output_data(Matrix, Solution) :- write_output_data(Matrix, Solution, 0).

write_output_data([Head|Tail], Solution, N) :- write_row_with_solution(Head, Solution, N), nl, % Запись строки N1 = N + 1, % Переход к следующей

write_output_data(Tail, Solution, N1).

5