Логическое программирование
Примеры программ
Рассмотрим некоторые программы, демонстрирующие обработку числовых данных. Отметим важную особенность процедур, создаваемых на языке Пролог: они, в отличии от встроенных функций, не могут появляться в арифметических выражениях. Если требуется, например, переменной R присвоить значение, равное умноженному на три большему из двух выражений X и Y, то, используя определенную ниже процедуру максимум, это можно записать так:
максимум(X,Y,Z), R is 3*Z.
(см. примеры 1.1)
максимум(X,X,X). максимум(X,Y,X):- X>Y. максимум(X,Y,Y):- X<Y. гипотенуза(X,Y,Z):- number(X), number(Y), Z is sqrt(X**2 + Y**2). мин_гип(A1,B1,A2,B2,Min):- гипотенуза(A1,B1,C1), гипотенуза(A2,B2,C2), Min is min(C1,C2). сумма(X,Y):- integer(X), X<10, Y is X. сумма(X,Y):- integer(X), X1 is X//10, сумма(X1,Y1), Z is X mod 10, Y is Y1+Z. печать_суммы:- write('Введите число (не забудьте точку в конце): '), read(X), nl, write('Сумма цифр введенного числа равна '), сумма(X,Y), write(Y), nl. факт(1,1). факт(N,R):- integer(N), N>1, N1 is N-1, факт(N1,R1), R is N*R1. сумма_списка([],0). сумма_списка([H|T],S):- сумма_списка(T,S1), number(H), S is S1+H.Пример 1.1.
Пример
Написать процедуру, вычисляющую максимум из двух чисел.
максимум(X,X,X). максимум(X,Y,X):- X>Y. максимум(X,Y,Y):- X<Y.
В предикате максимум/3 третий аргумент является максимумом из двух чисел - первого и второго его аргументов. Смысл каждого из правил данной процедуры вполне очевиден. Посмотрим на реакцию интерпретатора Пролога на запросы, содержащие данный предикат.
?- максимум(20,50,X). X = 50 Yes ?- максимум(100,50,X). X = 100 Yes ?- максимум(X,50,100). X = 100 Yes
Последний ответ показывает, что наш предикат позволяет находить ответ на вопросы типа: "Каково должно быть число, чтобы максимум из искомого числа и числа 50 равнялся бы 100?".
Как вы думаете, почему был получен ответ "No" на следующий запрос?
?- максимум(X,50,40). No
Пример
Составьте процедуру гипотенуза/3, которая по двум катетам прямоугольного треугольника вычисляет его гипотенузу.
Воспользуемся теоремой Пифагора и встроенной функцией sqrt для вычисления квадратного корня:
гипотенуза(X,Y,Z):- Z is sqrt(X**2 + Y**2).
Программа корректно вычисляет гипотенузу, но если мы попробуем при ее помощи найти один из катетов, то убедимся, что процедура работает не вполне правильно. Чтобы избежать этого добавим проверку того, что первые два аргумента предиката - положительные числа, для чего используем встроенный предикат number/1 и сравнение с нулем:
гипотенуза(X,Y,Z):- number(X), X>0, number(Y), Y>0, Z is sqrt(X**2 + Y**2).
?- гипотенуза(3,4,X). X = 5 Yes ?- гипотенуза(3,'a',X). No ?- гипотенуза(3,X,5). No
Пример
Напишите предикат, который по двум парам чисел - длинам катетов прямоугольных треугольников - определяет величину меньшей из гипотенуз.
Воспользуемся процедурой гипотенуза/3, разобранной выше, и встроенной функцией min/2:
мин_гип(A1,B1,A2,B2,Min):- гипотенуза(A1,B1,C1), гипотенуза(A2,B2,C2), Min is min(C1,C2).
Запросы к интерпретатору Пролога могут выглядеть так:
?- мин_гип(3,4,8,6,X). X = 5 Yes ?- мин_гип(3,4,Y,6,X). No
Пример
Факториалом натурального числа n называют произведение всех целых чисел от 1 до n включительно. Для записи факториала числа n используют обозначение n!.
Следующая процедура вычисляет факториал числа. Обратите внимание на использование рекурсии в данной процедуре:
факториал(1,1). факториал(N,R):- integer(N), N>1, N1 is N-1, факториал(N1,R1), R is N*R1.
Первое правило (так называемый терминальный случай, то есть тот момент выполнения процедуры, когда она перестает вызывать сама себя) гласит, что факториал единицы равен единице. Второе правило есть просто запись определения факториала: результат R получается умножением числа N на факториал числа, на единицу меньшего. Оно будет срабатывать при всех n>1 потому, что интерпретатор Пролога просматривает базу данных сверху вниз и переходит к следующему правилу или факту только в том случае, когда он не может выполнить текущее правило.
Пример
Напишите программу на языке Пролог, печатающую сумму всех цифр введенного с клавиатуры числа.
Для решения данной задачи воспользуемся двумя предикатами. Предикат сумма/2 имеет своим первым аргументом число, сумма цифр которого является его вторым аргументом. Второй предикат - печать_суммы/0 - запрашивает число, вызывает предикат сумма/2 и печатает полученный результат.
сумма(X,Y):- integer(X), X<10, Y is X. сумма(X,Y):- integer(X), X1 is X//10, сумма(X1,Y1), Z is X mod 10, Y is Y1+Z. печать_суммы:- write('Введите число (в конце точка): '), read(X), nl, сумма(X,Y), write('Сумма цифр числа '), write(X), write(' равна '), write(Y), nl.
Правило печать_суммы не имеет аргументов, данные вводятся с клавиатуры и затем, при помощи механизма унификации, передаются другим подцелям данного правила.
Пример
Напишите программу на языке Пролог, вводящую с клавиатуры два числа - координаты точки на плоскости и определяющую, попадает ли данная точка в круг единичного радиуса с центром в начале координат.
inside(X,Y,попадает):- number(X), number(Y), X**2+Y**2=<1. inside(X,Y,не_попадает):-number(X), number(Y), X**2+Y**2>1. /* Ввести два числа и вызвать предикат inside/3 */ input:-write('Введите x-координату: '), read(X), nl, write('Введите y-координату: '), read(Y), nl, inside(X,Y,R), write(R).
Задание
Напишите процедуры на языке Пролог для решения следующих задач и приведите примеры использования этих процедур.
- Измените последнюю из рассмотренных программ так, чтобы пользователь мог ввести координаты центра круга.
- Найдите количество цифр во введенном числе.
- Определите максимальную цифру введенного числа.
- Одноклеточная амеба каждые 3 часа делится на 2 клетки. Определите, сколько клеток будет через N часов (N=3, 6, ..., 24, т. е. кратно 3), если первоначально была одна амеба.