Федеральное агентство связи ФЕДЕРАЛЬНОЕ ГОСУДАРСТВЕННОЕ БЮДЖЕТНОЕ
ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ ВЫСШЕГО ОБРАЗОВАНИЯ «САНКТ-ПЕТЕРБУРГСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ ТЕЛЕКОММУНИКАЦИЙ ИМ. ПРОФ. М. А. БОНЧ-БРУЕВИЧА» (СПбГУТ)
Факультет инфокоммуникационных сетей и систем Кафедра программной инженерии и вычислительной техники
ЛАБОРАТОРНАЯ РАБОТА №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