Прологовая арифметика. Пролог система не считает символы +, -, * и т. д. чем то особым, и не пытается ничего вычислять до тех пор, пока мы не укажем ей, что это - запись арифметических действий. Чтобы лучше понять, что нам непонятно в арифметике Пролога, выполним следующие запросы к системе: GNU Prolog 1.2.16 By Daniel Diaz Copyright (C) 1999-2002 Daniel Diaz | ?- X = 2, Y is X - 1. X = 2 Y = 1 yes | ?- А вот так система поймет Вас неверно: GNU Prolog 1.2.16 By Daniel Diaz Copyright (C) 1999-2002 Daniel Diaz | ?- X = 2, Y = X - 1. X = 2 Y = 2-1 yes | ?- Пролог рассчитан главным образом на обработку символьной информации, при которой потребность в арифметических вычислениях относительно мала. Однако, было бы очень странным, если бы язык программирования компьютера не содержал набора математических функций и арифметических операторов. Для осуществления основных арифметических действий, можно воспользоваться несколькими предопределенными операторами. + Сложение - Вычитание / Деление (вещественное) mod модуль (остаток от целочисленного деления) Самое время сказать, что одно и то же выражение в Прологе может быть записано в инфиксной и постфиксной форме: | ?- X = 15, Y is X mod 2. X = 15 Y = 1 yes | ?- X = 15, Y is mod(X,2). X = 15 Y = 1 yes | ?- Встроенные, математические предикаты в GNU Prolog (без простейших) inc(E) Инкремент (увеличение на единицу) dec(E) Декремент (уменьшение на единицу) E1 /\ E2 Побитовое "И" E1 \/ E2 Побитовое "ИЛИ" E1 ^ E2 Побитовое "XOR" (исключающее "ИЛИ") \E Побитовое "НЕ" E1 << E2 "Целочисленный сдвиг" E1 на E2 разряда влево E1 >> E2 "Целочисленный сдвиг" E1 на E2 разряда вправо abs(E) Абсолютное значение (модуль числа) sign(E) Знаковая функция, возвращает -1, 0 или 1 min(E1,E2) Вернет минимальный аргумент max(E1,E2) Вернет максимальный аргумент E1 ** E2 Возведение в степень: E1E2 sqrt(E) Извлечение квадратного корня из E Тригонометрия в радианах: atan(E), cos(E), acos(E), sin(E), asin(E) exp(X) ex log(X) Натуральный логарифм (по основанию e) float(X) "Превращает" целое число X в вещественное ceiling(X) "Округляет" вещественное число X до большего целого числа floor(X) "Округляет" вещественное число X до меньшего целого числа round(X) "Округляет" вещественное число X до ближайшего целого числа truncate(X) "Целая часть" вещественного числа X Арифметические операции используются также и при сравнении числовых величин. Ниже перечислены операторы сравнения, их основное отличие - вычисление конкретезирующих, числовых значений перед сравнением: Expr1 Expr1 Expr1 Expr1 Expr1 Expr1 X > Y (арифметическое (X больше чем Y)) X < Y (арифметическое (X меньше чем Y)) X >= Y (арифметическое (X больше или равно Y)) X =< Y (арифметическое (X меньше или равно Y)) X=:=Y (арифметическое (X равно Y)) X=\=Y (арифметическое (X не равно Y)) =:= Expr2 succeeds if eval(Expr1) = eval(Expr2). =\= Expr2 succeeds if eval(Expr1) not eq eval(Expr2). < Expr2 succeeds if eval(Expr1) < eval(Expr2). =< Expr2 succeeds if eval(Expr1) <= eval(Expr2). > Expr2 succeeds if eval(Expr1) > eval(Expr2). >= Expr2 succeeds if eval(Expr1) => eval(Expr2). Наибольший общий делитель Давайте рассмотрим использование арифметических операций на простом примере: нахождение наибольшего общего делителя двух чисел. Если заданы два целых числа X и Y, то их наибольший делитель Z можно найти, руководствуясь следующими тремя правилами: 1. Если X и Y равны, то Z равен X. 2. Если X > Y, то Z равен наибольшему общему делителю разности Y - X. 3. Если Y < X, то формулировка аналогична правилу (2), если X и Y поменять местами. Реализуем это рекурсивное определение: nod(X,X,X). nod(X,Y,Z) :- X<Y, Y1 is Y-X, nod(X,Y1,Z). nod(X,Y,Z) :- X>Y, X1 is X-Y, nod(X1,Y,Z). Протестируем в GNU Prolog: GNU Prolog 1.2.16 By Daniel Diaz Copyright (C) 1999-2002 Daniel Diaz | ?- [user]. compiling user for byte code... nod(X,X,X). nod(X,Y,Z) :- X<Y, Y1 is Y-X, nod(X,Y1,Z). nod(X,Y,Z) :- X>Y, X1 is X-Y, nod(X1,Y,Z). user compiled, 4 lines read - 1188 bytes written, 4268 ms (10 ms) yes | ?- nod(20,25,X). X = 5 ? ; no | ?- Вычисление факториала GNU Prolog 1.2.16 By Daniel Diaz Copyright (C) 1999-2002 Daniel Diaz | ?- [user]. compiling user for byte code... fact(0,1). fact(X,F) :- X>0, X1 is X-1, fact(X1,F1), F is X*F1. user compiled, 3 lines read - 800 bytes written, 4199 ms (20 ms) yes | ?- fact(5,X). X = 120 ? ; no | ?- Оформление циклов forall(:Cond, :Action) For all alternative bindings of Cond, Action can be proven. The example verifies that all arithmetic statements in the given list are correct. It does not say which is wrong if one proves wrong. ?- forall(member(Result = Formula, [2 = 1 + 1, 4 = 2 * 2]), Result =:= Formula). The predicate forall/2 is implemented as \+ ( Cond, \+ Action), i.e., There is no instantiation of Cond for which Action is false.. The use of double negation implies that forall/2 does not change any variable bindings. It proves a relation. The forall/2 control structure can be used for its side-effects. E.g., the following asserts relations in a list into the dynamic database: ?- forall(member(Child-Parent, ChildPairs), assertz(child_of(Child, Parent))). Using forall/2 as forall(Generator, SideEffect) is preferred over the classical failure driven loop as shown below because it makes it explicit which part of the construct is the generator and which part creates the side effects. Also, unexpected failure of the side effect causes the construct to fail. Failure makes it evident that there is an issue with the code, while a failure driven loop would succeed with an erroneous result. ..., ( Generator, SideEffect, fail ; true ) If your intent is to create variable bindings, the forall/2 control structure is inadequate. Possibly you are looking for maplist/2, findall/3 or foreach/2.