Арифметика и строки
В настоящей главе рассматриваются примеры реализации некоторых операций над целыми числами, предикаты класса 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)).