 
        
        - •Практическое занятие 1. Основы логического программирования
- •Основные парадигмы программирования
- •Понятие предиката
- •Унификация - процесс нахождения решения в Прологе
- •Факты и правила в Прологе
- •Пример программы на Прологе
- •Практикум 1-1
- •Практикум 1-2
- •Практикум 1-3
- •Контрольное задание 1 Исходные данные
- •Задание 1. Генеалогическое дерево
- •Задание 2. Представление правил
- •Практикум 2-1
- •Практикум 2-2
- •Стандартные предикаты
- •Ввод и вывод
- •Практикум 2-3
- •Описание арифметических операций
- •Практикум 2-4
- •Самостоятельные задания
- •Практическое занятие 3. Управление процессом решения задачи. Поиск с возвратом. Рекурсия
- •Использование предиката fail
- •Практикум 3-1
- •Использование предиката cut
- •Практикум 3-2
- •Использование рекурсии
- •Практикум 3-3
- •Практикум 3-4
- •Практикум 3-5
- •Практикум 3-6
- •Хвостовая рекурсия
- •[Head|Tail] [Голова|Хвост]
- •Практикум 4-1
- •Встроенный предикат findall
- •Практикум 4-2
- •Вычисление длины списка
- •Практикум 4-3
- •Проверка принадлежности элемента списку
- •Практикум 4-4
- •Слияние двух списков
- •Практикум 4-5
- •Вычисление суммы списка чисел
- •Практикум 4-6
- •Практикум 4-7
- •Удаление элемента из списка
- •Практикум 4-8
- •Получение элемента списка по его номеру
- •Практикум 4-9
- •Запись элементов списка в обратном порядке
- •Поиск максимального (минимального) элемента в списке
- •Практикум 4-10
- •Самостоятельные задания
- •Контрольное задание 2 Исходные данные
- •Практическое занятие 5. Решение логических задач
- •Пример простой логической задачи (два измерения)
- •Практикум 5-1
- •Практикум 5-2
- •Пример задачи (три измерения)
- •Практикум 5-3
- •Пример задачи (альтернативные высказывания)
- •Практикум 5-4
- •Практикум 5-5
- •Самостоятельные задания
- •Контрольное задание 3 Исходные данные
- •Задача 1
- •Задача 2
- •Задача 3
- •Задача 4
- •Задача 5
- •Задача 6
- •Задача 7
- •Задача 8
- •Задача 9
- •Задача 10
- •Задача 11
- •Задача 12
- •Задача 13
- •Задача 14
- •Задача 15
- •Задача 16
- •Задача 17
- •Задача 18
- •Задача 19
- •Задача 20
- •Задачи повышенной сложности
[Head|Tail] [Голова|Хвост]
С помощью этой операции можно реализовывать рекурсивную обработку списка. Например, если мы хотим напечатать все элементы списка из программы на рис. 4.1 построчно, то это можно выполнить с помощью рекурсивного определения предиката:
print_list([ ]).
print_list([X|Y]) :- write(X), nl, print_list(Y).
Для описания предиката печати можно использовать конструкцию:
show :- dogs(X), print_list(X).
Практикум 4-1
|  | Напишите программу, хранящую увлечения студентов в виде списков. hobby("Маша", [теннис, музыка]). hobby("Коля", [музыка, еда, компьютер]). hobby("Вася", [лыжи, еда, компьютер, футбол]). Результат напечатайте в виде, представленном на рис. 4.3 | 
   
Рис. 4.3. Результаты программы Практикума 4-1
Встроенный предикат findall
При формировании списка из данных, содержащихся в виде фактов в базе данных или непосредственно в программе, полезным оказывается предикат findall. Он собирает значения, получаемые в процессе бектрекинга, в список. Формат предиката:
findall(Переменная, Предикат, Список),
где первый аргумент определяет параметр, который необходимо собрать в список, второй аргумент определяет предикат, из которого формируется список значений, третий аргумент - формируемый список.
На рис. 4.4 представлена программа, выводящая в список мужчин 18 лет.
 
Рис. 4.4. Пример использования предиката findall
В результате получим список
L= [“Петров”, “Сидоров”, “Александров”].
Практикум 4-2
|  | В программу, представленную на рис. 4.4. добавьте факты. С помощью предиката findall получите список всех мужчин призывного возраста (от 18 до 27 лет) | 
Рассмотрим обработку списков. Поскольку списки являются в действительности рекурсивными составными структурами данных, для их обработки необходимы и рекурсивные алгоритмы. Самый естественный способ обработки списков - сквозной просмотр, в ходе которого что-то делается с каждым элементом, до тех пор, пока не достигнут конец.
Как правило, такого рода алгоритмы используют процедуру, состоящую из двух правил. Одно из них говорит о том, как поступать с обыкновенным списком, который может быть разделен на голову и хвост. Другое говорит о том, что делать с пустым списком.
Вычисление длины списка
Создадим предикат, позволяющий вычислить длину списка, т.е. количество элементов в списке.
Для решения этой задачи воспользуемся очевидным фактом, что
- длина пустого списка [] есть 0. 
- длина любого другого списка есть 1 плюс длина его хвоста. 
Запишем эту идею:
   
Рис. 4.5. Вычисление длины списка
Обратите внимание, что при переходе от всего списка к его хвосту нам неважно, чему равен первый элемент списка, поэтому мы используем анонимную переменную.
Разберем на примере, как это будет работать. Пусть нас интересует количество элементов в списке [1,2,3]. Запишем соответствующий вопрос Пролог-системе:
length([1,2,3],X).
Система попытается вначале сопоставить нашу цель с первым предложением length([], 0), однако ей это не удается сделать, потому что первый аргумент цели является непустым списком. Система переходит ко второму предложению процедуры. Сопоставление с заголовком правила проходит успешно, переменная X связывается с переменной L, список [1,2,3] будет сопоставлен со списком [_|T], переменная T будет конкретизирована значением [2,3]. Теперь система переходит к попытке достижения подцели length(T,L_T). Как и в предыдущем случае, первое предложение с подцелью не сопоставляется, так как список T не пустой. При сопоставлении заголовка правила с подцелью хвост T конкретизируется одноэлементным списком [3]. На следующем шаге рекурсии переменная T означена пустым списком (хвост одноэлементного списка). И, значит, наша подцель выглядит следующим образом: length([], L_T). Эта цель сопоставляется с фактом, переменная L_T становится равной нулю. Раскручивается обратный ход рекурсии: переменная L_T увеличивается на единицу, результат попадает в переменную L. Получаем, что длина списка [3] равна единице. На следующем обратном шаге происходит еще одно добавление единицы, после чего длина списка [2,3] конкретизируется двойкой. И, наконец, на последнем возвратном шаге получаем означивание переменной L числом 3 (количеством элементов в списке [1,2,3]).
Пошаговая трассировка программы представлена в таблице 4.1
Таблица 4.1
| 
 | Шаг программы | Проверка | Результат | 
| 1 | Цель сопоставляется с первым предложением | length([1,2,3],X)== length([ ], 0)? | ложь | 
| 2 | Цель сопоставляется со вторым предложением | length([1,2,3],X)==length([ _|T], L)? это точка возврата | Т=[2,3] L – заносим в стек | 
| 3 | Первая подцель во втором предложении | length([2,3], L1)? это точка возврата | L1 – заносим в стек | 
| 4 | сопоставляется с первым предложением | length([2,3],X)== length([], 0)? | ложь | 
| 5 | сопоставляется со вторым предложением | length([2,3],X)==length([_|T], L) | Т=[3] | 
| 6 | Первая подцель во втором предложении | length([3], L1)? это точка возврата | L1 – заносим в стек | 
| 7 | сопоставляется с первым предложением | length([3],X)== length([], 0)? | ложь | 
| 8 | сопоставляется со вторым предложением | length([3],X)==length([_|T], L)? | Т=[] 
 | 
| 9 | Первая подцель во втором предложении | length([ ], L1)? это точка возврата | L1 – заносим в стек | 
| 10 | сопоставляется с первым предложением | length([],X)== length([], 0)? | истина X=0 | 
| 11 | возврат к шагу 9 | Первая подцель во втором предложении length([ ], L1)? Истина | L1=0 | 
| 12 | вторая подцель во втором предложении | L = L1 + 1 | L=1 | 
| 13 | возврат к шагу 6 | Первая подцель во втором предложении length([3], L1)? Истина | L1=1 | 
| 14 | вторая подцель во втором предложении | L = L1 + 1 | L=2 | 
| 15 | возврат к шагу 3 | Первая подцель во втором предложении length([2,3], L1)? Истина | L1=2 | 
| 16 | вторая подцель во втором предложении | L = L1 + 1 | L=3 | 
| 17 | возврат к шагу 2 | length([1,2,3],X)==length([_|2,3], 3)? | X=3 
 | 
