Арифметика и строки
В настоящей главе рассматриваются примеры реализации некоторых операций над целыми числами, предикаты класса math и предикаты класса string. Строится изображение троичного острова Коха.
10.1. Математические операции
Рассмотрим встроенные операции над целыми числами, а также предикаты класса math.
В следующей программе вычисляются наибольший общий делитель и наименьшее общее кратное целых чисел. Для вычисления используется алгоритм Евклида. Наименьшее общее кратное двух чисел находится как частное от деления произведения чисел на их наибольший общий делитель.
class predicates
gcd: (unsigned64, unsigned64) -> unsigned64.
clauses
gcd(X, 0) = X:- !.
gcd(X, Y) = gcd(Y, X mod Y).
run():-
X = 194546742, Y = 387623625,
Z = gcd(X, Y),
writef("Наибольший общий делитель % и % = %\n",
X, Y, Z),
writef("Наименьшее общее кратное % и % = %\n",
X, Y, (Y div Z) * X),
_ = readLine().
Пример
10.1.
Наибольший общий делитель. Наименьшее общее кратное
Следующая программа посвящена сокращению дробей. Если знаменатель исходной дроби является отрицательным числом, а числитель положительным, то после сокращения отрицательным становится числитель. Если знаменатель исходной дроби равен нулю, то и после сокращения он равен нулю.
Конструкция if-then-else позволяет определить операцию вычисления наибольшего делителя с помощью всего одного правила (см. программу).
class predicates
simplify: (tuple{integer, integer}) -> tuple{integer, integer}.
gcd: (integer, integer) -> integer.
toStr: (integer) -> string.
clauses
simplify(tuple(X, Y)) = tuple(X div Z, Y div Z):-
Z = gcd(X, Y).
gcd(X, Y) = if Y = 0 then X else gcd(Y, X mod Y) end if.
toStr(X) = toString(X):-
X >= 0,
!.
toStr(X) = string::format("(%)", X).
run():-
X = 386, Y = -514,
tuple(A, B) = simplify(tuple(X, Y)),
writef("% / % = % / %", X, toStr(Y), A, toStr(B)),
_ = readLine().
Пример
10.2.
Сокращение дробей
Предикат toString переводит элемент любого домена в элемент домена string. Предикат format форматирует строку.
В приведенной ниже программе строится снежинка Коха (рис. 10.1). Отрезок в консоли изображается в виде последовательности точек. Перед печатанием каждой точки делается небольшая пауза. Цвет точек генерируется случайным образом.
open core, console, console_native, math
class facts
w : integer := getConsoleWidth() - 1.
class predicates
fractal: (positive, real, real, real, real, real [out], real [out]).
line: (integer, integer, integer, integer).
printPoint: (integer, integer).
f: (real) -> integer.
clauses
f(X) = restrict(round(X), 0, w).
printPoint(X, Y):-
programControl::sleep(10),
setLocation(coord(X + 0, Y + 0)),
setTextAttribute(math::random(7) + 9),
write(".").
line(X1, Y, X2, Y):- !,
foreach X = std::between(X1, X2) do printPoint(X, Y)
end foreach.
line(X1, Y1, X2, Y2):-
foreach Y = std::between(Y1, Y2) do
printPoint(round(X1 + (Y - Y1) * (X2 - X1)/(Y2 - Y1)), Y)
end foreach.
fractal(0, X, Y, L, A, X1, Y1):- !,
X1 = X + L * cos(A),
Y1 = Y + L * sin(A),
line(f(2 * X), f(Y), f(2 * X1), f(Y1)).
fractal(I, X, Y, L, A, X4, Y4):-
I1 = I - 1,
L1 = L / 3,
fractal(I1, X, Y, L1, A, X1, Y1),
fractal(I1, X1, Y1, L1, A - pi/3, X2, Y2),
fractal(I1, X2, Y2, L1, A + pi/3, X3, Y3),
fractal(I1, X3, Y3, L1, A, X4, Y4).
run():-
N = 3,
L = 36,
fractal(N, 2, 11, L, 0, X1, Y1),
fractal(N, X1, Y1, L, 2 * pi/3, X2, Y2),
fractal(N, X2, Y2, L, 4 * pi/3, _, _),
_ = readLine().
Пример
10.3.
Снежинка Коха
Предикат round округляет число до ближайшего целого. Предикат restrict возвращает число из указанного промежутка, ближайшее к заданному числу. Предикат between недетерминированно возвращает целые числа в указанных пределах. Предикаты cos и sin вычисляют соответственно косинус и синус заданного действительного аргумента.
Программа выполняет три итерации. На нулевой итерации рисуется равносторонний треугольник (см. рис. 10.1 (a)). Количество итераций указывается в определении предиката run (N = 3). На последующих итерациях каждый отрезок преобразуется по правилу
(см. рис. 10.1 (b)).
