Программирование на языке пролог
Шрифт:
простые(Предел,Рs):- целые(2,Предел,Is),отсеять(Is,Рs).
целые (Min,Max,[Min|Oct]):-Min=‹Max,!, М is Min+1,целые(М,Мах,Ост).
целые(_,_,[]).
отсеять([],[]).
отсеять([I|Is],[I|Ps]):-удалить(I,Is,Нов),отсеять(Нов,Рs).
удалить(Р,[],[]).
удалить (P,[I|Is],[I|Nis]):-not(0 is I mod Р),!,удалить(Р,Is,Nis).
удалить (P,[I|Is],Nis):-0 is I mod Р,!,удалить(Р,Is,Nis).
Продолжая эту арифметическую тему, рассмотрим Пролог-программу, реализующую рекурсивную формулировку алгоритма Евклида для нахождения наибольшего общего делителя (НОД) и наименьшего общего кратного (НОК) двух чисел. Целевое утверждение нод(I,J,K)доказуемо, если Kявляется наибольшим общим делителем чисел Iи J. Целевое утверждение нок(I,J,K)доказуемо,
нод(I,0,I).
нод(I,J,K):- R is I mod J, нод(J,R,K).
нок(I,J,K):- нод(I,J,R), K is (I*J)/R.
Заметим, что из-за особенностей способа вычисления остатка эти предикаты не являются «обратимыми». Это означает, что для того чтобы они работали, необходимо заблаговременно конкретизировать переменные Iи J.
Упражнение 7.10.Если числа X, Yи Z таковы, что квадрат Z равен сумме квадратов Xи Y(т. е. если Z^2= X^2+ Y^2), то про такие числа говорят, что они образуют Пифагорову тройку.Напишите программу, порождающую Пифагоровы тройки. Определите предикат pythagтакой что, задав вопрос
?- pythag(X,Y,Z).
и запрашивая альтернативные решения, мы получим столько разных Пифагоровых троек, сколько пожелаем. Подсказка: используйте предикаты, подобные целое_числоиз гл. 4.
7.11. Символьное дифференцирование
Символьным дифференцированием в математике называется операция преобразования одного арифметического выражения в другое арифметическое выражение, которое называется производной.Пусть Uобозначает арифметическое выражение, которое может содержать переменную х.Производная от Uпо хзаписывается в виде dU/dxи определяется рекурсивно с помощью некоторых правил преобразования, применяемых к U.Вначале следуют два граничных условия. Стрелка означает «преобразуется в»; Uи Vобозначают выражения, а с– константу:
dc/dx– > 0
dx/dx– > 1
d(-U)/dx– > – (dU/dx)
d(U+V)/dx -> dU/dx+dV/dx
d(U-V)/dx– > dU/dx-dV/dx
d(cU)/dx– > c(dU/dx)
d(UV)/dx– > U(dV/dx) + V(dU/dx)
d(U/V)dx– > d(UV – 1)/dx
d(U c)/dx– > cU c – l(dU/dx)
d(lnU)/dx– > U – 1(dU/dx)
Этот набор правил легко написать на Прологе, поскольку мы можем представить арифметические выражения как структуры и использовать знаки операций как функторы этих структур. Кроме того, сопоставление целевого утверждения с заголовком правила мы можем использовать как сопоставление образцов. Рассмотрим цель d(E,X, F), которая считается согласованной, когда производная выражения Eпо константе [12] Xесть
12
Имеется в виду константа в смысле Пролога.
– Прим. ред.
?- d(x+1,x,X).
X = 1+0
?- d(x*x-2,x,X).
X = х*1+1*х-0
Заметим, что само по себе простое преобразование одного выражения в другое (на основе правил) не всегда дает результат в приведенной (упрощенной) форме. Приведение результата должно быть записано в виде отдельной процедуры (см. разд. 7.12). Программа дифференцирования состоит из определений дополнительных операций и построчной трансляции приведенных выше правил преобразования в утверждения Пролога:
?- op(10,yfx,^).
?- op(9,fx,~).
d(X,X,1):-!.
d(C,X,0):- atomic(C).
d(~U,X,~A):- d(U,X,A).
d(U+V,X,A+B):- d(U,X,A), d(V,X,B).
d(U-V,X,A-В):- d(U,X,A), d(V,X,B).
d(C*U,X,C*A):- atomic(C), C\=X, d(U,X,A),!.
d(U*V,X,B*U+A*V):- d(U,X,A), d(V,X,B).
d(U/V,X,A):- d(U*V^~1),X,A).
d(U^C,X,C*U^(C-1)*W):- atomic(C),C\=X,d(U,X,W).
d(log(U),X,A*U^(~1)):- d(U,X,A).
Обратите внимание на два места, в которых задан предикат отсечения. В первом случае отсечение обеспечивает тот факт, что производная от переменной по ней самой распознается только первым утверждением, исключая возможность применения второго утверждения. Во втором случае предусмотрено два утверждения для умножения. Первое – для специального случая. Если имеет место специальный случай, то утверждение для общего случая должно быть устранено из рассмотрения.
Как уже говорилось, данная программа выдает решения в неприведенной форме (т. е. без упрощений). Например, всякое вхождение х*1может быть приведено к х, а всякое вхождение вида х*1+1*х-0может быть приведено к 2*х. В следующем разделе рассматривается программа алгебраических преобразований, которую можно использовать для упрощения арифметических выражений. Примененный способ очень похож на тот, каким выше выводились производные.
7.12. Отображение структур и преобразование деревьев
Если некоторая структура покомпонентно копируется с целью образования новой структуры, то мы говорим, что одна структура отображаетсяв другую. Обычно при копировании каждая компонента слегка изменяется подобно тому, как в гл. 3 мы изменяли одно предложение, превращая его в другое. В том примере нам иногда нужно было скопировать какое-то слово в точности в том виде, в каком оно встретилось в исходном предложении, а иногда при копировании нам нужно было изменить слово. Для этого мы использовали следующую программу, которая отображаетпервый аргумент предиката преобразоватьво второй его аргумент:
преобразовать([],[]).
преобразовать([А|В],[С|D]):- заменить(А,С),преобразовать(В,D).
Поскольку отображение имеет довольно широкое применение, мы можем определить предикат отобспистакой, что целевое утверждение отобспис(Р, L, M)согласуется с базой данных, применяя предикат Рк каждому элементу списка Lи образуя в результате новый список М. При этом предполагается, что предикат Римеет два аргумента: первый аргумент для передачи «входного» элемента, а второй аргумент – для измененного элемента, подлежащего включению в список М.