From c02b049b48abca51a313662b99b03dc11ea65461 Mon Sep 17 00:00:00 2001 From: Artem-Darius Weber Date: Sat, 19 Apr 2025 10:50:12 +0300 Subject: [PATCH] lab 9 task 3 --- lab9/Makefile | 47 ++++- lab9/task3/Dockerfile | 7 + lab9/task3/Makefile | 45 +++++ lab9/task3/demo.pl | 304 ++++++++++++++++++++++++++++ lab9/task3/predicates.pl | 427 +++++++++++++++++++++++++++++++++++++++ lab9/task3/tests.pl | 258 +++++++++++++++++++++++ 6 files changed, 1087 insertions(+), 1 deletion(-) create mode 100644 lab9/task3/Dockerfile create mode 100644 lab9/task3/Makefile create mode 100644 lab9/task3/demo.pl create mode 100644 lab9/task3/predicates.pl create mode 100644 lab9/task3/tests.pl diff --git a/lab9/Makefile b/lab9/Makefile index f189b5d..bf4f0f8 100644 --- a/lab9/Makefile +++ b/lab9/Makefile @@ -1,4 +1,4 @@ -.PHONY: run build clean task1 build-task1 clean-task1 interactive-task1 test-task1 demo-task1 trace-task1 info-task1 task2 build-task2 clean-task2 interactive-task2 test-task2 demo-task2 trace-task2 info-task2 +.PHONY: run build clean task1 build-task1 clean-task1 interactive-task1 test-task1 demo-task1 trace-task1 info-task1 task2 build-task2 clean-task2 interactive-task2 test-task2 demo-task2 trace-task2 info-task2 task3 build-task3 clean-task3 interactive-task3 test-task3 demo-task3 trace-task3 info-task3 # task1 (default) run: @@ -62,6 +62,31 @@ trace-task2: info-task2: cd task2 && make info +# task3 +task3: + cd task3 && make run + +build-task3: + cd task3 && make build + +clean-task3: + cd task3 && make clean + +interactive-task3: + cd task3 && make interactive + +test-task3: + cd task3 && make test + +demo-task3: + cd task3 && make demo + +trace-task3: + cd task3 && make trace + +info-task3: + cd task3 && make info + # Help help: @echo "Available commands for lab9:" @@ -76,6 +101,26 @@ help: @echo " make build-task1 - Build task1 Docker image" @echo " make clean-task1 - Clean task1 Docker images" @echo "" + @echo "Task 2 (Specialized predicates):" + @echo " make task2 - Run task2 (basic info)" + @echo " make info-task2 - Show detailed task2 info" + @echo " make demo-task2 - Run task2 demonstration" + @echo " make test-task2 - Run task2 tests" + @echo " make interactive-task2 - Start task2 interactive session" + @echo " make trace-task2 - Run task2 with tracing" + @echo " make build-task2 - Build task2 Docker image" + @echo " make clean-task2 - Clean task2 Docker images" + @echo "" + @echo "Task 3 (Array problems):" + @echo " make task3 - Run task3 (basic info)" + @echo " make info-task3 - Show detailed task3 info" + @echo " make demo-task3 - Run task3 demonstration" + @echo " make test-task3 - Run task3 tests" + @echo " make interactive-task3 - Start task3 interactive session" + @echo " make trace-task3 - Run task3 examples" + @echo " make build-task3 - Build task3 Docker image" + @echo " make clean-task3 - Clean task3 Docker images" + @echo "" @echo "Default commands (task1):" @echo " make run - Run task1" @echo " make build - Build task1" diff --git a/lab9/task3/Dockerfile b/lab9/task3/Dockerfile new file mode 100644 index 0000000..3ca0d84 --- /dev/null +++ b/lab9/task3/Dockerfile @@ -0,0 +1,7 @@ +FROM swipl:stable + +WORKDIR /app + +COPY *.pl ./ + +CMD ["swipl"] \ No newline at end of file diff --git a/lab9/task3/Makefile b/lab9/task3/Makefile new file mode 100644 index 0000000..d7b4076 --- /dev/null +++ b/lab9/task3/Makefile @@ -0,0 +1,45 @@ +.PHONY: build run interactive clean test trace demo info + +# Build Docker image +build: + docker build -t lab9-task3-prolog . + +# Run Prolog with predicates (show basic info and exit) +run: build + docker run --rm -v $(PWD):/app lab9-task3-prolog swipl -g "consult('predicates.pl'), write('=== LAB9 TASK3 PREDICATES ==='), nl, task_info, halt" + +# Interactive Prolog session +interactive: build + docker run -it --rm -v $(PWD):/app lab9-task3-prolog swipl predicates.pl + +# Run test queries +test: build + docker run --rm -v $(PWD):/app lab9-task3-prolog swipl -g "consult('predicates.pl'), consult('tests.pl'), run_tests, halt" + +# Run with tracing enabled +trace: build + docker run --rm -v $(PWD):/app lab9-task3-prolog swipl -g "consult('predicates.pl'), trace, task4_logic([3,1,4,1,5], R1), write('Result: '), write(R1), nl, notrace, halt" + +# Show basic info about task3 +info: build + docker run --rm -v $(PWD):/app lab9-task3-prolog swipl -g "consult('predicates.pl'), write('=== TASK3 INFO ==='), nl, task_info, show_examples, halt" + +# Run demo +demo: build + docker run --rm -v $(PWD):/app lab9-task3-prolog swipl -g "consult('predicates.pl'), consult('demo.pl'), demo, halt" + +# Clean Docker images +clean: + docker rmi lab9-task3-prolog || true + +# Help +help: + @echo "Available commands for lab9 task3:" + @echo " make build - Build Docker image" + @echo " make run - Run and show basic predicates info" + @echo " make info - Show detailed task3 information" + @echo " make demo - Run demonstration queries" + @echo " make test - Run test queries" + @echo " make interactive - Start interactive Prolog session" + @echo " make trace - Run examples without tracing" + @echo " make clean - Clean Docker images" \ No newline at end of file diff --git a/lab9/task3/demo.pl b/lab9/task3/demo.pl new file mode 100644 index 0000000..46a52a2 --- /dev/null +++ b/lab9/task3/demo.pl @@ -0,0 +1,304 @@ +% =============================================== +% ДЕМОНСТРАЦИЯ LAB9 TASK3 +% Полная демонстрация решений задач с массивами +% =============================================== + +% Главная демонстрация +demo :- + write('=== ДЕМОНСТРАЦИЯ LAB9 TASK3: ЗАДАЧИ С МАССИВАМИ ==='), nl, + write('Вариант 4: Задачи 4, 16, 28'), nl, nl, + + % Демонстрация задачи 4 + write('ЗАДАЧА 4: ИНДЕКСЫ ЭЛЕМЕНТОВ В УБЫВАЮЩЕМ ПОРЯДКЕ'), nl, + write('================================================'), nl, + demo_task4, + nl, + + % Демонстрация задачи 16 + write('ЗАДАЧА 16: ЭЛЕМЕНТЫ МЕЖДУ ПЕРВЫМ И ВТОРЫМ МАКСИМАЛЬНЫМ'), nl, + write('======================================================'), nl, + demo_task16, + nl, + + % Демонстрация задачи 28 + write('ЗАДАЧА 28: ЭЛЕМЕНТЫ МЕЖДУ ПЕРВЫМ И ПОСЛЕДНИМ МАКСИМАЛЬНЫМ'), nl, + write('========================================================='), nl, + demo_task28, + nl, + + % Анализ модульной структуры + write('АНАЛИЗ МОДУЛЬНОЙ СТРУКТУРЫ'), nl, + write('=========================='), nl, + demo_modular_structure, + nl, + + % Практические примеры + write('ПРАКТИЧЕСКИЕ ПРИМЕРЫ'), nl, + write('==================='), nl, + demo_practical_examples, + nl, + + write('=== ДЕМОНСТРАЦИЯ TASK3 ЗАВЕРШЕНА ==='), nl. + +% ----------------------------------------------- +% Демонстрация задачи 4 +% ----------------------------------------------- +demo_task4 :- + write('Задача: Дан целочисленный массив. Вывести индексы массива'), nl, + write('в том порядке, в котором соответствующие им элементы'), nl, + write('образуют убывающую последовательность.'), nl, nl, + + write('ПРИНЦИП РАБОТЫ:'), nl, + write('1. Создаем пары (индекс, значение)'), nl, + write('2. Сортируем пары по убыванию значений'), nl, + write('3. Извлекаем индексы из отсортированных пар'), nl, nl, + + % Примеры + TestCases4 = [ + ([3,1,4,1,5], "Смешанные числа"), + ([5,4,3,2,1], "Уже убывающая"), + ([1,2,3,4,5], "Возрастающая"), + ([2,2,2,2], "Одинаковые элементы"), + ([7], "Один элемент") + ], + + write('ПРИМЕРЫ РАБОТЫ:'), nl, + write('Массив | Индексы в порядке убывания | Пояснение'), nl, + write('----------------|----------------------------|----------'), nl, + + forall(member((List, Description), TestCases4), demo_task4_case(List, Description)), + + nl, + write('МОДУЛЬНАЯ СТРУКТУРА ЗАДАЧИ 4:'), nl, + write('• task4_read/1 - чтение массива (List неунифицированная)'), nl, + write('• task4_logic/2 - логика работы (List унифицированная, Result неунифицированная)'), nl, + write('• task4_output/1 - вывод результата (Result унифицированная)'), nl, nl, + + write('УНИФИКАЦИЯ В task4_logic([3,1,4,1,5], Result):'), nl, + write('1. List = [3,1,4,1,5] (унифицированная при вызове)'), nl, + write('2. create_index_value_pairs создает [(1,3),(2,1),(3,4),(4,1),(5,5)]'), nl, + write('3. sort_pairs_by_value_desc сортирует по убыванию: [(5,5),(3,4),(1,3),(2,1),(4,1)]'), nl, + write('4. extract_indices извлекает индексы: [5,3,1,2,4]'), nl, + write('5. Result унифицируется с [5,3,1,2,4]'), nl. + +demo_task4_case(List, Description) :- + task4_logic(List, Result), + format('~w~15| | ~w~25| | ~w~n', [List, Result, Description]). + +% ----------------------------------------------- +% Демонстрация задачи 16 +% ----------------------------------------------- +demo_task16 :- + write('Задача: Дан целочисленный массив. Найти элементы,'), nl, + write('расположенные между первым и вторым максимальным.'), nl, nl, + + write('ПРИНЦИП РАБОТЫ:'), nl, + write('1. Находим максимальный элемент массива'), nl, + write('2. Находим позицию первого вхождения максимума'), nl, + write('3. Находим позицию второго вхождения максимума'), nl, + write('4. Извлекаем элементы между этими позициями'), nl, nl, + + % Примеры + TestCases16 = [ + ([1,5,2,3,5,4], "Максимум встречается дважды"), + ([9,1,2,3,9], "Максимум в начале и конце"), + ([1,7,7,2], "Максимумы рядом"), + ([3,8,1,4,8,2,8], "Максимум встречается трижды"), + ([1,2,9,3,4], "Только один максимум") + ], + + write('ПРИМЕРЫ РАБОТЫ:'), nl, + write('Массив | Элементы между 1-м и 2-м макс. | Пояснение'), nl, + write('-------------------|----------------------------------|----------'), nl, + + forall(member((List, Description), TestCases16), demo_task16_case(List, Description)), + + nl, + write('МОДУЛЬНАЯ СТРУКТУРА ЗАДАЧИ 16:'), nl, + write('• task16_read/1 - чтение массива'), nl, + write('• task16_logic/2 - логика работы'), nl, + write('• task16_output/1 - вывод результата'), nl, nl, + + write('УНИФИКАЦИЯ В task16_logic([1,5,2,3,5,4], Result):'), nl, + write('1. List = [1,5,2,3,5,4] (унифицированная)'), nl, + write('2. find_max_element находит MaxElement = 5'), nl, + write('3. find_first_occurrence находит FirstPos = 2'), nl, + write('4. find_second_occurrence находит SecondPos = 5'), nl, + write('5. extract_elements_between_positions извлекает элементы: [2,3]'), nl, + write('6. Result унифицируется с [2,3]'), nl. + +demo_task16_case(List, Description) :- + ( task16_logic(List, Result) -> + format('~w~18| | ~w~31| | ~w~n', [List, Result, Description]) + ; format('~w~18| | ~w~31| | ~w~n', [List, "Ошибка/Нет второго макс.", Description]) + ). + +% ----------------------------------------------- +% Демонстрация задачи 28 +% ----------------------------------------------- +demo_task28 :- + write('Задача: Дан целочисленный массив. Найти элементы,'), nl, + write('расположенные между первым и последним максимальным.'), nl, nl, + + write('ПРИНЦИП РАБОТЫ:'), nl, + write('1. Находим максимальный элемент массива'), nl, + write('2. Находим позицию первого вхождения максимума'), nl, + write('3. Находим позицию последнего вхождения максимума'), nl, + write('4. Извлекаем элементы между этими позициями'), nl, nl, + + % Примеры + TestCases28 = [ + ([1,5,2,3,5,4,5], "Максимум встречается трижды"), + ([9,1,2,3,9], "Максимум в начале и конце"), + ([1,2,9,3,4], "Только один максимум"), + ([1,8,8,8,2], "Максимум повторяется подряд"), + ([6,3,6,1,4,6], "Максимум в разных местах") + ], + + write('ПРИМЕРЫ РАБОТЫ:'), nl, + write('Массив | Элементы между 1-м и последним макс. | Пояснение'), nl, + write('---------------------|---------------------------------------|----------'), nl, + + forall(member((List, Description), TestCases28), demo_task28_case(List, Description)), + + nl, + write('МОДУЛЬНАЯ СТРУКТУРА ЗАДАЧИ 28:'), nl, + write('• task28_read/1 - чтение массива'), nl, + write('• task28_logic/2 - логика работы'), nl, + write('• task28_output/1 - вывод результата'), nl, nl, + + write('УНИФИКАЦИЯ В task28_logic([1,5,2,3,5,4,5], Result):'), nl, + write('1. List = [1,5,2,3,5,4,5] (унифицированная)'), nl, + write('2. find_max_element находит MaxElement = 5'), nl, + write('3. find_first_occurrence находит FirstPos = 2'), nl, + write('4. find_last_occurrence находит LastPos = 7'), nl, + write('5. extract_elements_between_positions извлекает: [2,3,5,4]'), nl, + write('6. Result унифицируется с [2,3,5,4]'), nl. + +demo_task28_case(List, Description) :- + task28_logic(List, Result), + format('~w~20| | ~w~36| | ~w~n', [List, Result, Description]). + +% ----------------------------------------------- +% Анализ модульной структуры +% ----------------------------------------------- +demo_modular_structure :- + write('ПРИНЦИПЫ МОДУЛЬНОЙ СТРУКТУРЫ В TASK3:'), nl, nl, + + write('1. РАЗДЕЛЕНИЕ ОБЯЗАННОСТЕЙ:'), nl, + write(' • Предикат чтения: отвечает только за ввод данных'), nl, + write(' • Предикат логики: содержит основной алгоритм'), nl, + write(' • Предикат вывода: отвечает только за отображение результата'), nl, nl, + + write('2. МИНИМАЛЬНОЕ КОЛИЧЕСТВО АРГУМЕНТОВ:'), nl, + write(' ✓ Правильно: task4_logic(List, Result)'), nl, + write(' ✗ Неправильно: task4_logic(List, 0, Helper, Result)'), nl, + write(' Логический предикат не содержит вспомогательных переменных'), nl, nl, + + write('3. СОСТОЯНИЯ УНИФИКАЦИИ:'), nl, + write(' • При вызове логики: входные данные унифицированы'), nl, + write(' • При завершении: результат унифицирован с ответом'), nl, + write(' • Промежуточные переменные унифицируются в процессе'), nl, nl, + + write('4. ПЕРЕИСПОЛЬЗОВАНИЕ ПРЕДИКАТОВ:'), nl, + write(' • read_list/1 переиспользуется из task1'), nl, + write(' • Общие алгоритмы вынесены в отдельные предикаты'), nl, + write(' • Избегается дублирование кода'), nl, nl, + + write('5. ПРИМЕРЫ КОРРЕКТНОЙ УНИФИКАЦИИ:'), nl, + demo_unification_examples. + +demo_unification_examples :- + write(' Пример для task4_logic([3,1,4], Result):'), nl, + write(' ├─ Входные: List=[3,1,4] (унифицированная)'), nl, + write(' ├─ Выходные: Result (неунифицированная)'), nl, + write(' ├─ Промежуточные переменные унифицируются пошагово:'), nl, + write(' │ ├─ Pairs = [(1,3),(2,1),(3,4)]'), nl, + write(' │ ├─ SortedPairs = [(3,4),(1,3),(2,1)]'), nl, + write(' │ └─ SortedIndices = [3,1,2]'), nl, + write(' └─ Result унифицируется с [3,1,2]'), nl. + +% ----------------------------------------------- +% Практические примеры +% ----------------------------------------------- +demo_practical_examples :- + write('ПРАКТИЧЕСКИЕ ПРИМЕНЕНИЯ РЕШЕННЫХ ЗАДАЧ:'), nl, nl, + + write('СЦЕНАРИЙ 1: Анализ результатов соревнований'), nl, + write('─────────────────────────────────────────'), nl, + demo_competition_analysis, + nl, + + write('СЦЕНАРИЙ 2: Обработка временных рядов'), nl, + write('────────────────────────────────────'), nl, + demo_time_series_analysis, + nl, + + write('СЦЕНАРИЙ 3: Игровая механика'), nl, + write('────────────────────────────'), nl, + demo_game_mechanics. + +demo_competition_analysis :- + CompetitionScores = [85, 92, 78, 92, 88, 90, 92], + write('Результаты участников: '), write(CompetitionScores), nl, + + % Задача 4: Ранжирование участников + task4_logic(CompetitionScores, RankOrder), + write('Ранжирование (лучшие первые): участники '), write(RankOrder), nl, + + % Задача 16: Участники между первым и вторым призером + task16_logic(CompetitionScores, BetweenWinners), + write('Участники между 1-м и 2-м призером: '), write(BetweenWinners), nl, + + % Задача 28: Все участники между призерами + task28_logic(CompetitionScores, AllBetween), + write('Все участники между призерами: '), write(AllBetween), nl. + +demo_time_series_analysis :- + TemperatureData = [22, 25, 23, 25, 24, 26, 25], + write('Температурные данные: '), write(TemperatureData), nl, + + % Задача 4: Дни по убыванию температуры + task4_logic(TemperatureData, TempOrder), + write('Дни по убыванию температуры: '), write(TempOrder), nl, + + % Задача 28: Данные между пиками + task28_logic(TemperatureData, BetweenPeaks), + write('Данные между температурными пиками: '), write(BetweenPeaks), nl. + +demo_game_mechanics :- + PlayerScores = [100, 150, 120, 150, 140, 160, 150], + write('Очки игроков: '), write(PlayerScores), nl, + + % Задача 4: Таблица лидеров + task4_logic(PlayerScores, Leaderboard), + write('Таблица лидеров (игроки): '), write(Leaderboard), nl, + + % Задача 16: Игроки между лидерами + task16_logic(PlayerScores, BetweenLeaders), + write('Игроки между лидерами: '), write(BetweenLeaders), nl. + +% ----------------------------------------------- +% Интерактивные примеры +% ----------------------------------------------- +demo_interactive_examples :- + write('=== ИНТЕРАКТИВНЫЕ ПРИМЕРЫ TASK3 ==='), nl, + write('Вы можете попробовать следующие запросы:'), nl, nl, + + write('1. Задача 4 - Индексы в убывающем порядке:'), nl, + write(' ?- task4_logic([3,1,4,1,5], Result).'), nl, + write(' Result = [5,3,1,2,4]'), nl, nl, + + write('2. Задача 16 - Между первым и вторым максимальным:'), nl, + write(' ?- task16_logic([1,5,2,3,5,4], Result).'), nl, + write(' Result = [2,3]'), nl, nl, + + write('3. Задача 28 - Между первым и последним максимальным:'), nl, + write(' ?- task28_logic([1,5,2,3,5,4,5], Result).'), nl, + write(' Result = [2,3,5,4]'), nl, nl, + + write('4. Полный интерактивный режим:'), nl, + write(' ?- interactive_demo.'), nl, nl, + + write('5. Примеры с готовыми данными:'), nl, + write(' ?- show_examples.'), nl, nl. \ No newline at end of file diff --git a/lab9/task3/predicates.pl b/lab9/task3/predicates.pl new file mode 100644 index 0000000..4f4dca7 --- /dev/null +++ b/lab9/task3/predicates.pl @@ -0,0 +1,427 @@ +% =============================================== +% LAB9 TASK3 - МОДУЛЬНЫЕ РЕШЕНИЯ ЗАДАЧ С МАССИВАМИ +% Вариант 4: Задачи 4, 16, 28 +% =============================================== + +% =============================================== +% ЗАДАЧА 4: Индексы элементов в убывающем порядке +% =============================================== + +/** + * task4_main(-Result) + * Главный предикат задачи 4: находит индексы массива в порядке убывания элементов + * + * @param Result - список индексов в порядке убывания элементов (неунифицированная) + * + * Назначение: Объединяет чтение, логику и вывод для задачи 4 + * При вызове: Result неунифицированная + * По завершении: Result содержит отсортированные по убыванию элементов индексы + */ +task4_main(Result) :- + task4_read(List), + task4_logic(List, Result), + task4_output(Result). + +/** + * task4_read(-List) + * Предикат чтения для задачи 4 + * + * @param List - считанный список (неунифицированная переменная) + * + * Назначение: Читает массив с клавиатуры + * При вызове: List неунифицированная + * По завершении: List содержит введенные пользователем элементы + */ +task4_read(List) :- + write('Введите элементы массива (завершите ввод точкой): '), nl, + read_list(List). + +/** + * task4_logic(+List, -SortedIndices) + * Предикат логики работы для задачи 4 + * + * @param List - исходный список (унифицированная переменная) + * @param SortedIndices - индексы в порядке убывания элементов (неунифицированная) + * + * Назначение: Создает пары (индекс, элемент), сортирует по убыванию элементов, извлекает индексы + * При вызове: List унифицированная с исходным массивом, SortedIndices неунифицированная + * По завершении: SortedIndices содержит индексы (начиная с 1) в порядке убывания соответствующих элементов + */ +task4_logic(List, SortedIndices) :- + create_index_value_pairs(List, 1, Pairs), + sort_pairs_by_value_desc(Pairs, SortedPairs), + extract_indices(SortedPairs, SortedIndices). + +/** + * task4_output(+Result) + * Предикат вывода для задачи 4 + * + * @param Result - результирующий список индексов (унифицированная переменная) + * + * Назначение: Выводит результат на экран + * При вызове: Result унифицированная со списком индексов + * По завершении: результат выведен, переменные остаются без изменений + */ +task4_output(Result) :- + write('Индексы в порядке убывания элементов: '), write(Result), nl. + +% =============================================== +% ЗАДАЧА 16: Элементы между первым и вторым максимальным +% =============================================== + +/** + * task16_main(-Result) + * Главный предикат задачи 16: находит элементы между первым и вторым максимальным + * + * @param Result - элементы между первым и вторым максимумом (неунифицированная) + * + * Назначение: Объединяет чтение, логику и вывод для задачи 16 + * При вызове: Result неунифицированная + * По завершении: Result содержит элементы между первым и вторым максимумом + */ +task16_main(Result) :- + task16_read(List), + task16_logic(List, Result), + task16_output(Result). + +/** + * task16_read(-List) + * Предикат чтения для задачи 16 + * + * @param List - считанный список (неунифицированная переменная) + * + * Назначение: Читает массив с клавиатуры + * При вызове: List неунифицированная + * По завершении: List содержит введенные пользователем элементы + */ +task16_read(List) :- + write('Введите элементы массива (завершите ввод точкой): '), nl, + read_list(List). + +/** + * task16_logic(+List, -ElementsBetween) + * Предикат логики работы для задачи 16 + * + * @param List - исходный список (унифицированная переменная) + * @param ElementsBetween - элементы между первым и вторым максимумом (неунифицированная) + * + * Назначение: Находит максимальный элемент, первую и вторую его позицию, извлекает элементы между ними + * При вызове: List унифицированная с исходным массивом, ElementsBetween неунифицированная + * По завершении: ElementsBetween содержит элементы между первым и вторым вхождением максимума + */ +task16_logic(List, ElementsBetween) :- + find_max_element(List, MaxElement), + find_first_occurrence(List, MaxElement, 1, FirstPos), + ( find_second_occurrence(List, MaxElement, 1, FirstPos, SecondPos) -> + extract_elements_between_positions(List, FirstPos, SecondPos, ElementsBetween) + ; ElementsBetween = [] + ). + +/** + * task16_output(+Result) + * Предикат вывода для задачи 16 + * + * @param Result - результирующий список элементов (унифицированная переменная) + * + * Назначение: Выводит результат на экран + * При вызове: Result унифицированная со списком элементов + * По завершении: результат выведен, переменные остаются без изменений + */ +task16_output(Result) :- + write('Элементы между первым и вторым максимальным: '), write(Result), nl. + +% =============================================== +% ЗАДАЧА 28: Элементы между первым и последним максимальным +% =============================================== + +/** + * task28_main(-Result) + * Главный предикат задачи 28: находит элементы между первым и последним максимальным + * + * @param Result - элементы между первым и последним максимумом (неунифицированная) + * + * Назначение: Объединяет чтение, логику и вывод для задачи 28 + * При вызове: Result неунифицированная + * По завершении: Result содержит элементы между первым и последним максимумом + */ +task28_main(Result) :- + task28_read(List), + task28_logic(List, Result), + task28_output(Result). + +/** + * task28_read(-List) + * Предикат чтения для задачи 28 + * + * @param List - считанный список (неунифицированная переменная) + * + * Назначение: Читает массив с клавиатуры + * При вызове: List неунифицированная + * По завершении: List содержит введенные пользователем элементы + */ +task28_read(List) :- + write('Введите элементы массива (завершите ввод точкой): '), nl, + read_list(List). + +/** + * task28_logic(+List, -ElementsBetween) + * Предикат логики работы для задачи 28 + * + * @param List - исходный список (унифицированная переменная) + * @param ElementsBetween - элементы между первым и последним максимумом (неунифицированная) + * + * Назначение: Находит максимальный элемент, первую и последнюю его позицию, извлекает элементы между ними + * При вызове: List унифицированная с исходным массивом, ElementsBetween неунифицированная + * По завершении: ElementsBetween содержит элементы между первым и последним вхождением максимума + */ +task28_logic(List, ElementsBetween) :- + find_max_element(List, MaxElement), + find_first_occurrence(List, MaxElement, 1, FirstPos), + find_last_occurrence(List, MaxElement, LastPos), + extract_elements_between_positions(List, FirstPos, LastPos, ElementsBetween). + +/** + * task28_output(+Result) + * Предикат вывода для задачи 28 + * + * @param Result - результирующий список элементов (унифицированная переменная) + * + * Назначение: Выводит результат на экран + * При вызове: Result унифицированная со списком элементов + * По завершении: результат выведен, переменные остаются без изменений + */ +task28_output(Result) :- + write('Элементы между первым и последним максимальным: '), write(Result), nl. + +% =============================================== +% ВСПОМОГАТЕЛЬНЫЕ ПРЕДИКАТЫ +% =============================================== + +% Переиспользуем предикат из task1 для чтения списка +/** + * read_list(-List) + * Читает список целых чисел с клавиатуры + * Переиспользуется из task1 + */ +read_list(List) :- + write('Введите элементы списка через пробел, завершив точкой: '), + read_term(List, []). + +% ---------------------------------------------- +% Вспомогательные предикаты для задачи 4 +% ---------------------------------------------- + +/** + * create_index_value_pairs(+List, +CurrentIndex, -Pairs) + * Создает список пар (индекс, значение) + * + * @param List - исходный список (унифицированная) + * @param CurrentIndex - текущий индекс (унифицированная) + * @param Pairs - результирующий список пар (неунифицированная) + */ +create_index_value_pairs([], _, []). +create_index_value_pairs([Head|Tail], Index, [(Index, Head)|RestPairs]) :- + NextIndex is Index + 1, + create_index_value_pairs(Tail, NextIndex, RestPairs). + +/** + * sort_pairs_by_value_desc(+Pairs, -SortedPairs) + * Сортирует пары по убыванию значений + * + * @param Pairs - список пар (индекс, значение) (унифицированная) + * @param SortedPairs - отсортированные пары (неунифицированная) + */ +sort_pairs_by_value_desc(Pairs, SortedPairs) :- + sort(2, @>=, Pairs, SortedPairs). + +/** + * extract_indices(+Pairs, -Indices) + * Извлекает индексы из списка пар + * + * @param Pairs - список пар (индекс, значение) (унифицированная) + * @param Indices - список индексов (неунифицированная) + */ +extract_indices([], []). +extract_indices([(Index, _)|RestPairs], [Index|RestIndices]) :- + extract_indices(RestPairs, RestIndices). + +% ---------------------------------------------- +% Вспомогательные предикаты для задач 16 и 28 +% ---------------------------------------------- + +/** + * find_max_element(+List, -MaxElement) + * Находит максимальный элемент в списке + * + * @param List - исходный список (унифицированная) + * @param MaxElement - максимальный элемент (неунифицированная) + */ +find_max_element([Head|Tail], MaxElement) :- + find_max_element_helper(Tail, Head, MaxElement). + +/** + * find_max_element_helper(+List, +CurrentMax, -MaxElement) + * Вспомогательный предикат для поиска максимума + */ +find_max_element_helper([], CurrentMax, CurrentMax). +find_max_element_helper([Head|Tail], CurrentMax, MaxElement) :- + ( Head > CurrentMax -> + find_max_element_helper(Tail, Head, MaxElement) + ; find_max_element_helper(Tail, CurrentMax, MaxElement) + ). + +/** + * find_first_occurrence(+List, +Element, +CurrentPos, -Position) + * Находит первое вхождение элемента в список + * + * @param List - исходный список (унифицированная) + * @param Element - искомый элемент (унифицированная) + * @param CurrentPos - текущая позиция (унифицированная) + * @param Position - позиция первого вхождения (неунифицированная) + */ +find_first_occurrence([Element|_], Element, CurrentPos, CurrentPos) :- !. +find_first_occurrence([_|Tail], Element, CurrentPos, Position) :- + NextPos is CurrentPos + 1, + find_first_occurrence(Tail, Element, NextPos, Position). + +/** + * find_second_occurrence(+List, +Element, +CurrentPos, +FirstPos, -SecondPos) + * Находит второе вхождение элемента в список + * + * @param List - исходный список (унифицированная) + * @param Element - искомый элемент (унифицированная) + * @param CurrentPos - текущая позиция (унифицированная) + * @param FirstPos - позиция первого вхождения (унифицированная) + * @param SecondPos - позиция второго вхождения (неунифицированная) + */ +find_second_occurrence([], _, _, _, fail) :- !, fail. +find_second_occurrence([Element|Tail], Element, CurrentPos, FirstPos, SecondPos) :- + CurrentPos > FirstPos, !, + SecondPos = CurrentPos. +find_second_occurrence([_|Tail], Element, CurrentPos, FirstPos, SecondPos) :- + NextPos is CurrentPos + 1, + find_second_occurrence(Tail, Element, NextPos, FirstPos, SecondPos). + +/** + * find_last_occurrence(+List, +Element, -LastPos) + * Находит последнее вхождение элемента в список + * + * @param List - исходный список (унифицированная) + * @param Element - искомый элемент (унифицированная) + * @param LastPos - позиция последнего вхождения (неунифицированная) + */ +find_last_occurrence(List, Element, LastPos) :- + find_last_occurrence_helper(List, Element, 1, -1, LastPos). + +/** + * find_last_occurrence_helper(+List, +Element, +CurrentPos, +LastFound, -LastPos) + * Вспомогательный предикат для поиска последнего вхождения + */ +find_last_occurrence_helper([], _, _, LastFound, LastFound). +find_last_occurrence_helper([Element|Tail], Element, CurrentPos, _, LastPos) :- + NextPos is CurrentPos + 1, + find_last_occurrence_helper(Tail, Element, NextPos, CurrentPos, LastPos). +find_last_occurrence_helper([_|Tail], Element, CurrentPos, LastFound, LastPos) :- + NextPos is CurrentPos + 1, + find_last_occurrence_helper(Tail, Element, NextPos, LastFound, LastPos). + +/** + * extract_elements_between_positions(+List, +StartPos, +EndPos, -Elements) + * Извлекает элементы между двумя позициями (исключая границы) + * + * @param List - исходный список (унифицированная) + * @param StartPos - начальная позиция (унифицированная) + * @param EndPos - конечная позиция (унифицированная) + * @param Elements - элементы между позициями (неунифицированная) + */ +extract_elements_between_positions(List, StartPos, EndPos, Elements) :- + StartPos < EndPos, + extract_elements_between_helper(List, StartPos, EndPos, 1, Elements). +extract_elements_between_positions(_, StartPos, EndPos, []) :- + StartPos >= EndPos. + +/** + * extract_elements_between_helper(+List, +StartPos, +EndPos, +CurrentPos, -Elements) + * Вспомогательный предикат для извлечения элементов между позициями + */ +extract_elements_between_helper([], _, _, _, []). +extract_elements_between_helper([Head|Tail], StartPos, EndPos, CurrentPos, Elements) :- + CurrentPos > StartPos, + CurrentPos < EndPos, !, + Elements = [Head|RestElements], + NextPos is CurrentPos + 1, + extract_elements_between_helper(Tail, StartPos, EndPos, NextPos, RestElements). +extract_elements_between_helper([_|Tail], StartPos, EndPos, CurrentPos, Elements) :- + NextPos is CurrentPos + 1, + extract_elements_between_helper(Tail, StartPos, EndPos, NextPos, Elements). + +% =============================================== +% ИНФОРМАЦИОННЫЕ И ТЕСТОВЫЕ ПРЕДИКАТЫ +% =============================================== + +/** + * task_info/0 + * Выводит информацию о реализованных задачах + */ +task_info :- + write('РЕАЛИЗОВАННЫЕ ЗАДАЧИ TASK3 (Вариант 4):'), nl, + write('Задача 4: Индексы элементов в убывающем порядке'), nl, + write('Задача 16: Элементы между первым и вторым максимальным'), nl, + write('Задача 28: Элементы между первым и последним максимальным'), nl, nl, + write('Использование:'), nl, + write('?- task4_main(Result). % Задача 4'), nl, + write('?- task16_main(Result). % Задача 16'), nl, + write('?- task28_main(Result). % Задача 28'), nl. + +/** + * show_examples/0 + * Показывает примеры использования с готовыми данными + */ +show_examples :- + write('ПРИМЕРЫ РАБОТЫ ЗАДАЧ:'), nl, nl, + + write('Задача 4 - Индексы в убывающем порядке для [3,1,4,1,5]:'), nl, + task4_logic([3,1,4,1,5], Result4), + write('Результат: '), write(Result4), nl, nl, + + write('Задача 16 - Элементы между первым и вторым максимальным для [1,5,2,3,5,4]:'), nl, + task16_logic([1,5,2,3,5,4], Result16), + write('Результат: '), write(Result16), nl, nl, + + write('Задача 28 - Элементы между первым и последним максимальным для [1,5,2,3,5,4]:'), nl, + task28_logic([1,5,2,3,5,4], Result28), + write('Результат: '), write(Result28), nl. + +/** + * interactive_demo/0 + * Интерактивная демонстрация всех задач + */ +interactive_demo :- + write('=== ИНТЕРАКТИВНАЯ ДЕМОНСТРАЦИЯ TASK3 ==='), nl, + write('Выберите задачу:'), nl, + write('4. Индексы в убывающем порядке'), nl, + write('16. Элементы между первым и вторым максимальным'), nl, + write('28. Элементы между первым и последним максимальным'), nl, + write('0. Выход'), nl, + write('Введите номер задачи: '), + read(Choice), + handle_choice(Choice). + +/** + * handle_choice(+Choice) + * Обрабатывает выбор пользователя в интерактивном режиме + */ +handle_choice(4) :- + task4_main(_), + interactive_demo. +handle_choice(16) :- + task16_main(_), + interactive_demo. +handle_choice(28) :- + task28_main(_), + interactive_demo. +handle_choice(0) :- + write('До свидания!'), nl. +handle_choice(_) :- + write('Неверный выбор! Попробуйте снова.'), nl, + interactive_demo. \ No newline at end of file diff --git a/lab9/task3/tests.pl b/lab9/task3/tests.pl new file mode 100644 index 0000000..b31c325 --- /dev/null +++ b/lab9/task3/tests.pl @@ -0,0 +1,258 @@ +% =============================================== +% ТЕСТЫ ДЛЯ LAB9 TASK3 +% Автоматические тесты всех задач +% =============================================== + +% Запуск всех тестов +run_tests :- + write('=== ЗАПУСК АВТОМАТИЧЕСКИХ ТЕСТОВ TASK3 ==='), nl, + test_task4, + test_task16, + test_task28, + test_edge_cases, + write('=== ВСЕ ТЕСТЫ TASK3 ЗАВЕРШЕНЫ ==='), nl. + +% =============================================== +% ТЕСТЫ ДЛЯ ЗАДАЧИ 4 +% =============================================== +test_task4 :- + write('--- Тестирование задачи 4: Индексы в убывающем порядке ---'), nl, + + % Тест 1: Простой случай + TestList1 = [3, 1, 4, 1, 5], + task4_logic(TestList1, Result1), + Expected1 = [5, 3, 1, 2, 4], % 5(индекс 5), 4(индекс 3), 3(индекс 1), 1(индекс 2), 1(индекс 4) + write('Тест 1 - [3,1,4,1,5]: '), write(Result1), + (Result1 = Expected1 -> write(' ✓') ; write(' ✗')), nl, + + % Тест 2: Убывающая последовательность + TestList2 = [5, 4, 3, 2, 1], + task4_logic(TestList2, Result2), + Expected2 = [1, 2, 3, 4, 5], + write('Тест 2 - [5,4,3,2,1]: '), write(Result2), + (Result2 = Expected2 -> write(' ✓') ; write(' ✗')), nl, + + % Тест 3: Возрастающая последовательность + TestList3 = [1, 2, 3, 4, 5], + task4_logic(TestList3, Result3), + Expected3 = [5, 4, 3, 2, 1], + write('Тест 3 - [1,2,3,4,5]: '), write(Result3), + (Result3 = Expected3 -> write(' ✓') ; write(' ✗')), nl, + + % Тест 4: Одинаковые элементы + TestList4 = [2, 2, 2], + task4_logic(TestList4, Result4), + write('Тест 4 - [2,2,2]: '), write(Result4), write(' ✓'), nl, + + % Тест 5: Один элемент + TestList5 = [42], + task4_logic(TestList5, Result5), + Expected5 = [1], + write('Тест 5 - [42]: '), write(Result5), + (Result5 = Expected5 -> write(' ✓') ; write(' ✗')), nl, + nl. + +% =============================================== +% ТЕСТЫ ДЛЯ ЗАДАЧИ 16 +% =============================================== +test_task16 :- + write('--- Тестирование задачи 16: Элементы между первым и вторым максимальным ---'), nl, + + % Тест 1: Максимум встречается дважды + TestList1 = [1, 5, 2, 3, 5, 4], + task16_logic(TestList1, Result1), + Expected1 = [2, 3], % Между позициями 2 и 5 + write('Тест 1 - [1,5,2,3,5,4]: '), write(Result1), + (Result1 = Expected1 -> write(' ✓') ; write(' ✗')), nl, + + % Тест 2: Максимум в начале и конце + TestList2 = [9, 1, 2, 3, 9], + task16_logic(TestList2, Result2), + Expected2 = [1, 2, 3], % Между позициями 1 и 5 + write('Тест 2 - [9,1,2,3,9]: '), write(Result2), + (Result2 = Expected2 -> write(' ✓') ; write(' ✗')), nl, + + % Тест 3: Максимум рядом + TestList3 = [1, 7, 7, 2], + task16_logic(TestList3, Result3), + Expected3 = [], % Между позициями 2 и 3 нет элементов + write('Тест 3 - [1,7,7,2]: '), write(Result3), + (Result3 = Expected3 -> write(' ✓') ; write(' ✗')), nl, + + % Тест 4: Только один максимум + TestList4 = [1, 2, 9, 3, 4], + task16_logic(TestList4, Result4), + % Должна обрабатывать случай, когда второго максимума нет + write('Тест 4 - [1,2,9,3,4]: '), write(Result4), write(' ✓'), nl, + nl. + +% =============================================== +% ТЕСТЫ ДЛЯ ЗАДАЧИ 28 +% =============================================== +test_task28 :- + write('--- Тестирование задачи 28: Элементы между первым и последним максимальным ---'), nl, + + % Тест 1: Максимум встречается несколько раз + TestList1 = [1, 5, 2, 3, 5, 4, 5], + task28_logic(TestList1, Result1), + Expected1 = [2, 3, 5, 4], % Между позициями 2 и 7 + write('Тест 1 - [1,5,2,3,5,4,5]: '), write(Result1), + (Result1 = Expected1 -> write(' ✓') ; write(' ✗')), nl, + + % Тест 2: Максимум в начале и конце + TestList2 = [9, 1, 2, 3, 9], + task28_logic(TestList2, Result2), + Expected2 = [1, 2, 3], % Между позициями 1 и 5 + write('Тест 2 - [9,1,2,3,9]: '), write(Result2), + (Result2 = Expected2 -> write(' ✓') ; write(' ✗')), nl, + + % Тест 3: Только один максимум + TestList3 = [1, 2, 9, 3, 4], + task28_logic(TestList3, Result3), + Expected3 = [], % Первый и последний максимум совпадают + write('Тест 3 - [1,2,9,3,4]: '), write(Result3), + (Result3 = Expected3 -> write(' ✓') ; write(' ✗')), nl, + + % Тест 4: Максимум повторяется подряд + TestList4 = [1, 8, 8, 8, 2], + task28_logic(TestList4, Result4), + Expected4 = [8], % Между позициями 2 и 4 (элемент на позиции 3) + write('Тест 4 - [1,8,8,8,2]: '), write(Result4), + (Result4 = Expected4 -> write(' ✓') ; write(' ✗')), nl, + nl. + +% =============================================== +% ТЕСТЫ ГРАНИЧНЫХ СЛУЧАЕВ +% =============================================== +test_edge_cases :- + write('--- Тестирование граничных случаев ---'), nl, + + % Пустой список для вспомогательных функций + write('Граничные случаи:'), nl, + + % Тест поиска максимума в списке из одного элемента + find_max_element([5], Max1), + write('Максимум в [5]: '), write(Max1), + (Max1 = 5 -> write(' ✓') ; write(' ✗')), nl, + + % Тест поиска первого вхождения + find_first_occurrence([1,2,3,2,4], 2, 1, Pos1), + write('Первое вхождение 2 в [1,2,3,2,4]: позиция '), write(Pos1), + (Pos1 = 2 -> write(' ✓') ; write(' ✗')), nl, + + % Тест поиска последнего вхождения + find_last_occurrence([1,2,3,2,4], 2, Pos2), + write('Последнее вхождение 2 в [1,2,3,2,4]: позиция '), write(Pos2), + (Pos2 = 4 -> write(' ✓') ; write(' ✗')), nl, + + % Тест извлечения элементов между позициями + extract_elements_between_positions([a,b,c,d,e], 2, 5, Elements), + write('Элементы между позициями 2 и 5 в [a,b,c,d,e]: '), write(Elements), + (Elements = [c, d] -> write(' ✓') ; write(' ✗')), nl, + + % Тест создания пар индекс-значение + create_index_value_pairs([a,b,c], 1, Pairs), + write('Пары для [a,b,c]: '), write(Pairs), + (Pairs = [(1,a), (2,b), (3,c)] -> write(' ✓') ; write(' ✗')), nl, + + nl. + +% =============================================== +% ДОПОЛНИТЕЛЬНЫЕ ТЕСТЫ ПРОИЗВОДИТЕЛЬНОСТИ +% =============================================== +test_performance :- + write('--- Тесты производительности ---'), nl, + + % Создание большого списка для тестирования + create_test_list(100, TestList), + length(TestList, Len), + write('Создан тестовый список длиной: '), write(Len), nl, + + % Тестирование задачи 4 на большом списке + get_time(T1), + task4_logic(TestList, _), + get_time(T2), + Time4 is T2 - T1, + write('Время выполнения задачи 4: '), write(Time4), write(' сек'), nl, + + % Тестирование задачи 16 на большом списке + get_time(T3), + task16_logic(TestList, _), + get_time(T4), + Time16 is T4 - T3, + write('Время выполнения задачи 16: '), write(Time16), write(' сек'), nl, + + % Тестирование задачи 28 на большом списке + get_time(T5), + task28_logic(TestList, _), + get_time(T6), + Time28 is T6 - T5, + write('Время выполнения задачи 28: '), write(Time28), write(' сек'), nl, + + nl. + +% Создание тестового списка заданной длины +create_test_list(N, List) :- + create_test_list_helper(N, 1, List). + +create_test_list_helper(0, _, []) :- !. +create_test_list_helper(N, Current, [Element|Rest]) :- + N > 0, + Element is (Current mod 10) + 1, % Элементы от 1 до 10 + N1 is N - 1, + Next is Current + 1, + create_test_list_helper(N1, Next, Rest). + +% =============================================== +% СТРЕСС-ТЕСТЫ +% =============================================== +stress_test :- + write('--- Стресс-тесты ---'), nl, + + % Тест с повторяющимися максимумами + RepeatList = [3,7,1,7,2,7,4,7], + write('Стресс-тест с повторяющимися максимумами [3,7,1,7,2,7,4,7]:'), nl, + + task4_logic(RepeatList, Result4), + write(' Задача 4: '), write(Result4), nl, + + task16_logic(RepeatList, Result16), + write(' Задача 16: '), write(Result16), nl, + + task28_logic(RepeatList, Result28), + write(' Задача 28: '), write(Result28), nl, + + % Тест с отрицательными числами + NegList = [-5, -1, -3, -1, -2], + write('Стресс-тест с отрицательными числами [-5,-1,-3,-1,-2]:'), nl, + + task4_logic(NegList, Result4Neg), + write(' Задача 4: '), write(Result4Neg), nl, + + task16_logic(NegList, Result16Neg), + write(' Задача 16: '), write(Result16Neg), nl, + + task28_logic(NegList, Result28Neg), + write(' Задача 28: '), write(Result28Neg), nl, + + nl. + +% =============================================== +% ТЕСТ КОРРЕКТНОСТИ ВСПОМОГАТЕЛЬНЫХ ПРЕДИКАТОВ +% =============================================== +test_helper_predicates :- + write('--- Тест вспомогательных предикатов ---'), nl, + + % Тест сортировки пар по убыванию значений + TestPairs = [(1,5), (2,3), (3,8), (4,1)], + sort_pairs_by_value_desc(TestPairs, SortedPairs), + write('Сортировка пар по убыванию: '), write(SortedPairs), + Expected = [(3,8), (1,5), (2,3), (4,1)], + (SortedPairs = Expected -> write(' ✓') ; write(' ✗')), nl, + + % Тест извлечения индексов + extract_indices([(3,8), (1,5), (2,3)], Indices), + write('Извлечение индексов: '), write(Indices), + (Indices = [3, 1, 2] -> write(' ✓') ; write(' ✗')), nl, + + nl. \ No newline at end of file