Фрактальные множества Delphi

Вы наверное часто видели довольно хитроумные картины, на которых непонятно что изображено, но все равно необычность их форм завораживает и приковывает внимание. Как правило, это хитроумные формы не поддающиеся казалось бы какому-либо математическому описанию. Вы к примеру видели узоры на стекле после мороза или к примеру хитроумные кляксы, оставленные на листе чернильной ручкой, так вот что-то подобное вполне можно записать в виде некоторого алгоритма, а следовательно доступно объясниться с компьютером. Подобные множества называют фрактальными. Вобще, как мне известно фракталы появились не так уж давно, но сразу завоевали свою важную нишу. Фракталы не похожи на привычные нам фигуры, известные из геометрии, и строятся они по определенным алгоритмам, а эти алгоритмы с помощью компьютера можно изобразить на экране. Вобще, если все слегка упростить, то фракталы - это некое преобразование многократно примененное к исходной фигуре.

Здесь хочу остановиться на фрактальных множествах Мандельброта и Жюлиа. Изображения этих множеств не имеют каких либо четко очерченных границ. Особенностью фракталов является то, что даже маленькая часть изображения в конечном итоге представлет общее целое, особенно хорошо этот эффект можно пронаблюдать на примере множества Жюлиа. Кстати на основе этого свойства фракталов основано фрактальное сжатие данных, но эту тему разберем как-нибудь позже (когда накопиться достаточно нужного материала).

Итак приступим к самому главному, ради чего мы здесь и собрались. Как же строятся эти удивителные множества ?

Все сводится к вычислению одной единственной формулы.


Zi+1=Zi2+C   

Здесь Z и C - комплексные числа. Как видно, формулы по сути представляет собой обычную рекурсию (или что-то вроде многократно примененного преобразования). Зная правила работы с комплексными числами данную формулу можно упростить и привести к следующему виду.


xi+1=xi2-yi2+a  
yi+1=2*xi*yi+b  

Построение множества Мандельброта сводится к следующему. Для каждой точки (a,b) проводится серия вычислений по вышеприведенным формулам, причем x0 и y0 принимаются равными нулю, т.е. точка в формуле выступает в качестве константы. На каждом шаге вычиляется величина r=sqrt(x2+y2 ). Значением r ,как ни трудно заметить, является расстояние точки с координатами (x,y) от начала координат ( r=sqrt[ (x-0)2+(y-0)2] ). Исходная точка (a,b) считается принадлежащей множеству Мандельброта, если она никогда не удаляется от начала координат на какое-то критическое число. Для отображения можно подсчитать скорость удаления от центра, если например точка ушла за критическое расстояние, и в зависимости от нее окрасить исходную точку в соответствующие цвет. Полное изображение множества Мандельброта можно получить на плоскости от -2 до 1 по оси x и от -1.5 до 1.5 по оси y. Также известно, что для получения примелимой точности достаточно 100 итеарций (по теории их должно быть бесконечно много). Ниже представлен листинг функции реализующей выполнение итераций и определение принадлежности точки множеству Мандельброта, точнее на выходе мы получаем цвет для соответствующе точки. В качестве критического числа взято число 2. Чтобы не вычислять корень, мы сравниваем квадрат расстояния (r2) с квадратом критического числа, т.е. сравниваем (x2+y2) и 4.


function MandelBrot(a,b: real): TColor;  
var  
    x,y,xy: real;  
    x2,y2: real;  
    r:real;  
    k: integer;  
begin  
    r:=0;  
    x:=0; y:=0;  
    k:=100;  
    while (k>0)and(r<4) do  
    begin  
        x2:=x*x;  
        y2:=y*y;  
        xy:=x*y;  
        x:=x2-y2+a;  
        y:=2*xy+b;  
        r:=x2+y2;  
        dec(k)  
    end;  
    k:=round((k/100)*255);  
    result:=RGB(k,k,k);  
end;   

Множество Жюлиа получается если зафиксировать в формуле значение комплексной константы (a+ib), которая будет одинакова для всех точек, а начальные значения x0 и y0 принимать равными значениям координатам вычисляемой точки. Листинг для множества Жюлиа приведен ниже.


function Julia(x0,y0: real): TColor;  
var  
    a,b,x,y,x2,y2,xy: real;  
    r:real;  
    speed,k: integer;  
begin  
    r:=1;  
    a:=-0.55; b:=-0.55;  
    x:=x0; y:=y0;  
    k:=100;  
    while (k>0)and(r<4) do  
    begin  
        x2:=x*x;  
        y2:=y*y;  
        xy:=x*y;  
        x:=x2-y2+a;  
        y:=2*xy+b;  
        r:=x2+y2;  
        dec(k)  
    end;  
    k:=round((k/100)*255);  
    result:=RGB(k,k,k);  
end;   

Ниже приведен листинг функции отображающий данные множества.


procedure TForm1.BitBtn2Click(Sender: TObject);  
var  
    x_min,y_min,x_max,y_max,hx,hy,x,y: real;  
    i,j,n: integer;  
    color: TColor;  
begin  
    x_min:=-1.5; x_max:=2;  
    y_min:=-1.5; y_max:=1.5;  
    n:=300;  
    y:=y_min;  
    hx:=(x_max-x_min)/n;  
    hy:=(y_max-y_min)/n;  
    for j:=0 to n do  
    begin  
        x:=x_min;  
        for i:=0 to n do  
        begin  
            if rbM.Checked then color:=MandelBrot(x,y);  
            if rbJ.Checked then color:=Julia(x,y);  
            imPict.Picture.Bitmap.Canvas.Pixels[i,j]:=color;  
            x:=x+hx;  
        end;  
        y:=y+hy;  
    end;  
end;   

При рассмотрении темы большую помощь оказала статья А.Колесникова "Визуализация фрактальных структур" в "Компьтерных вестях".

Добавлено: 07 Августа 2018 21:32:09 Добавил: Андрей Ковальчук

Рисуем график функции в Delphi

В этой статье мы рассмотрим несколько способов нарисовать график какой-нибудь функции. Рисовать график мы будем на канве компонента Image.

Рисование по пикселям

Рисовать на канве можно разными способами. Первый вариант - рисовать по пикселям. Для этого используется свойство канвы Pixels. Это свойство представляет собой двумерный массив, который отвечает за цвета канвы. Например Canvas.Pixels[10,20] - соответствует цвету пикселя с координатами (10,20). С массивом пикселей можно обращаться, как с любым свойством: изменять цвет, задавая пикселю новое значение, или определять его цвет, по хранящемуся в нем значению. На примере ниже мы зададим черный цвет пикселю с координатами (10,20):

Canvas.Pixels[10,20]:=clBlack;


Теперь мы попробуем нарисовать график функции F(x), если известен диапазон ее изменений Ymax и Ymin, и диапазон изменения аргумента Xmax и Xmin. Для этого мы напишем пользовательскую функцию, которая будет вычислять значение функции F в точке x, а также будет возвращать максимум и минимум функции и ее аргумента.


function Tform1.F(x:real; var Xmax,Xmin,Ymax,Ymin:real):real;  
begin  
F:=Sin(x);  
Xmax:=4*pi;  
Xmin:=0;  
Ymax:=1;  
Ymin:=-1;  
end;  

Не забудьте также указать заголовок этой функциии в разделе Public:


public  
{ public declarations }  
function F(x:real; var Xmax,Xmin,Ymax,Ymin:real):real;  

Здесь для ясности мы просто указали диапазон изменения функции Sin(x) и ее аргумента, ниже эта функция будет описана целиком. Параметры Xmax, Xmin, Ymax, Ymin - описаны со словом Var потому что они являются входными-выходными, т.е. через них функция будет возвращать значения вычислений этих данных в основную программу. Поэтому надо объявить Xmax, Xmin, Ymax, Ymin как глобальные переменные в разделе Implementation:


implementation  
var Xmax,Xmin,Ymax,Ymin:real;  

Теперь поставим на форму кнопку и в ее обработчике события OnClick напишем следующий код:


procedure TForm1.Button1Click(Sender: TObject);  
var x,y:real;  
PX,PY:longInt;  
begin  
for PX:=0 to Image1.Width do  
begin  
x:=Xmin+PX*(Xmax-Xmin)/Image1.Width;  
y:=F(x,Xmax,Xmin,Ymax,Ymin);  
PY:=trunc(Image1.Height-(y-Ymin)*Image1.height/(Ymax-Ymin));  
image1.Canvas.Pixels[PX,PY]:=clBlack;  
end;  
end;  

В этом коде вводятся переменные x и y, являющиеся значениями аргумента и функции, а также переменные PX и PY, являющиеся координатами пикселей, соответствующих x и y. Сама процедура состоит из цикла по всем значениям горизонтальной координаты пикселей PX компонента Image1. Сначала выбранное значение PX пересчитывается в соответствующее значение x. Затем производится вызов функции F(x) и определяется ее значение Y. Это значение пересчитывается в вертикальную координату пикселя PY.

Рисование с помощью пера Pen

У канвы имеется свойство Pen - перо. Это объект, в свою очередь имеющий ряд свойств. Одно из них - свойство Color - цвет, которым наносится рисунок. Второе свойство - Width - ширина линии, задается в пикселах (по умолчанию 1).

Свойство Style определяет вид линии и может принимать следующие значения:

psSolid Сплошная линия
psDash Штриховая линия
psDot Пунктирная линия
psDashDot Штрих-пунктирная линия
psDashDotDot Линия, чередующая штрих и два пунктира
psClear Отсутствие линии
psInsideFrame Сплошная линия, но при Width > 1 допускающая цвета, отличные от палитры Windows
Все стили со штрихами и пунктирами доступны только при толщине линий равной 1. Иначе эти линии рисуются как сплошные.

У канвы имеется свойство PenPos, типа TPoint. Это свойство определяет в координатах канвы текущую позицию пера. Перемещение пера без прорисовки осуществляется методом MoveTo(x,y). После вызова этого метода канвы точка с координатами (x,y) становится исходной, от которой методом LineTo(x,y) можно провести линию в любую точку с координатами (x,y).

Давайте теперь попробуем нарисовать график синуса пером. Для этого добавим перед циклом оператор:

Image1.Canvas.MoveTo(0,Image1.height div 2);


А перед заключительным end цикла добавим следующий оператор:

Image1.Canvas.LineTo(PX,PY);


Таким образом у Вас должен получиться такой код:


procedure TForm1.Button1Click(Sender: TObject);  
var x,y:real;  
PX,PY:longInt;  
begin  
Image1.Canvas.MoveTo(0,Image1.height div 2);  
for PX:=0 to Image1.Width do  
begin  
x:=Xmin+PX*(Xmax-Xmin)/Image1.Width;  
y:=F(x,Xmax,Xmin,Ymax,Ymin);  
PY:=trunc(Image1.Height-(y-Ymin)*Image1.height/(Ymax-Ymin));  
image1.Canvas.Pixels[PX,PY]:=clBlack;  
Image1.Canvas.LineTo(PX,PY);  
end;  
end;  

Как Вы уже успели заметить, если запустили программу, качество рисования графика пером, намного лучше, чем рисования по пикселям.

Как обещал сейчас напишу пример программы которая находит максимум и минимум функции. Я маленько изменил структуру процедур и функций, чтобы было яснее. Вот готовый код программы:


...  
type  
TForm1 = class(TForm)  
Button1: TButton;  
Image1: TImage;  
procedure Button1Click(Sender: TObject);  
private  
{ private declarations }  
public  
function F(x:real):real;  
procedure Extrem1(Xmax,Xmin:real; var Ymin:real);  
procedure Extrem2(Xmax,Xmin:real; var Ymax:real);  
{ public declarations }  
end;  
  
var  
Form1: TForm1;  
  
implementation  
Const e=1e-4;//точность одна тысячная  
var Xmax,Xmin,Ymax,Ymin:real;  
{$R *.DFM}  
function Tform1.F(x:real):real;  
begin  
F:=Sin(x);  
end;  
  
//поиск минимума функции  
procedure TForm1.Extrem1(Xmax,Xmin:real; var Ymin:real);  
var x,h:real; j,n:integer;  
begin  
n:=10;  
repeat  
x:=Xmin;  
n:=n*2;  
h:=(Xmax-Xmin)/n;  
Ymin:=F(Xmin);  
for j:=1 to n do begin  
if f(x)<Ymin then Ymin:=f(x);  
x:=x+h;  
end;  
until abs(f(Ymin)-f(Ymin+h))<e;  
end;  
  
//поиск максимума функции  
procedure TForm1.Extrem2(Xmax,Xmin:real; var Ymax:real);  
var x,h:real; j,n:integer;  
begin  
n:=10;  
repeat  
x:=Xmin;  
n:=n*2;  
h:=(Xmax-Xmin)/n;  
Ymax:=F(Xmin);  
for j:=1 to n do begin  
if f(x)>=Ymax then Ymax:=f(x);  
x:=x+h;  
end;  
until abs(f(Ymax)-f(Ymax+h))<e;  
end;  
  
  
procedure TForm1.Button1Click(Sender: TObject);  
var x,y:real;  
PX,PY:longInt;  
begin  
//здесь необходимо указать диапазон изменения x  
Xmax:=8*pi;  
Xmin:=0;  
  
//вычисляем экстремумы функции  
Extrem1(Xmax,Xmin,Ymin);  
Extrem2(Xmax,Xmin,Ymax);  
  
//рисуем график функции  
Image1.Canvas.MoveTo(0,Image1.height div 2);  
for PX:=0 to Image1.Width do  
begin  
x:=Xmin+PX*(Xmax-Xmin)/Image1.Width;  
y:=F(x);  
PY:=trunc(Image1.Height-(y-Ymin)*Image1.height/(Ymax-Ymin));  
image1.Canvas.Pixels[PX,PY]:=clBlack;  
Image1.Canvas.LineTo(PX,PY);  
end;  
end;  
end.  

Ну вот и всё, программа построения графика функции готова.

Добавлено: 07 Августа 2018 21:11:10 Добавил: Андрей Ковальчук

Создание калькулятора с командной строкой в Delphi

Введение
Если Вы хотели написать калькулятор с командной строкой, но не знали, как это сделать, или просто не собрались духом, то эта статья для Вас.

Для начала предлагаю немного теории.

В рамках данной статьи я рассмотрю только написание функции для расчета значения из строки. Я считаю, что написание формы с кнопками доступно для всех, кто взялся читать эту статью.

Для начала представим себе простейший вариант строки:

1*2+3*4

Если быть совсем честным, это нужно делать через стеки, но поскольку не все знают что это такое, я предлагаю сделать через стандартный класс TStringList. Для расчета такой строки нам нужно создать два экземпляра класса TStringList. В одном мы будем хранить числа, а в другом знаки операций.

Вот что у нас получилось. Пройдемся по строке, как только мы нашли число, добавляем его в первый список. А если мы нашли знак операции, добавляем его во второй список.

Далее Вы спросите: «А зачем, собственно, мы это делали?». Так просто очень легко считать.

Привожу алгоритм подсчета значения строки, разделенной таким способом на два списка.

Ищем во втором списке операции, приоритет которых выше. То есть, знаки умножения или деления.
Если нашли, то вынимаем этот знак. Вынимаем из первого списка число с таким же номером, как и у знака, и следующее. Это и будут наши операнды. Выполняем с ними соответствующее действие, и записываем результат в первый список на то место, с которого выдернули первый операнд.
Повторяем пункты 1 и 2 до тех пор, пока во втором списке не останется ни одного такого знака.
Повторяем пункты 1-3 со знаками сложения и вычитания.
А теперь давайте прогоним через этот алгоритм наш пример.

Ищем в правом столбце знак умножения или деления. Нашли – он стоит на первой позиции. Далее нам нужны числа, которые надо умножить. Они стоят во втором списке под номерами 1, и 1+1, то есть 2. В нашем примере это цифры 1 и 2. Вынимаем их из списка и умножаем. Получилась двойка. Записываем её в первый список на то место, откуда выдернули первый операнд, в нашем случае на первое место. И удаляем из второго списка знак умножения. Вот что должно получиться:

2.Далее продолжаем искать знаки умножения или деления. Нашли. Знак умножения стоит на второй позиции. Нам опять же нужны операнды. Они находятся на втором и третьем месте в первом списке. Это цифры 3 и 4. Умножаем их и удаляем из первого списка. Результат, число 12, заносим в первый список под номером 2.

На этом знаки умножения и деления у нас закончились, займемся поисками знаков сложения и вычитания. В нашем случае есть знак умножения, стоящий на первом месте. Выбираем из первого списка то, что будем складывать (это элементы под номерами 1 и два), удаляем их из списка, а результат сложения заносим обратно в список. У нас получилось следующее:



Так как список операций пуст, то мы выполнили все действия, а результатом является число, оставшееся в списке чисел.
Как Вы видите, на данном этапе все довольно легко. Давайте теперь напишем функцию, которая вычисляет значение уже разделенной строки. В качестве параметра у неё будут два списка: список чисел и список операций. Вот полный код этой функции. Я думаю, в ней нет ничего сложного. А если Вы дочитали до этого момента, то алгоритм Вам уже ясен.


function CalculateLists (s1, s2: TStringList): real;  
var  
    i: integer;  
    a,b,r1: real;  
    c: char;  
begin  
 r1 := 0;  
// Ищем знаки  умножения или деления  
i := 0;  
if s2.Count>0 then  
while (s2.Find('*', i)or(s2.Find('/', i))) do  
begin  
  c := s2[i][1];  
  a := strtofloat(s1[i]);  
  b := strtofloat(s1[i+1]);  
  case c of  
   '*': r1 := a*b;  
   '/': r1 := a/b;  
  end;  
  s1.Delete(i);  
  s1.Delete(i);  
  s1.Insert(i, floattostr(r1));  
  s2.Delete(i);  
end;  
// Сложение и вычитание ///  
if s2.Count>0 then  
repeat  
    c := s2[0][1];  
    a := strtofloat(s1[0]);  
    b := strtofloat(s1[1]);  
    case c of  
     '+': r1 := a+b;  
     '-': r1 := a-b;  
    end;  
    s1.Delete(0);  
    s1.Delete(0);  
    s1.Insert(0, floattostr(r1));  
    s2.Delete(0);  
    if s1.Count = 1 then break;  
    if s2.Count = 0 then break;  
until false;  
 result := strtofloat(s1[0]);  
end;  

Сразу хочу заметить, что элементы TStringList имеют строковый тип. Поэтому приходится преобразовывать типы туда сюда.

Ну а теперь давайте займемся самой сложной частью всего задания: разделение строки на два списка. Давайте, прежде чем включать Delphi и начинать лупить клавиатуру, немного разберемся, чего мы от этой функции хотим. Главная её задача состоит в том, чтобы корректно разделить строку на два списка и передать эти списки на вычисление функции CalculateLists, которую мы только что написали. А что, если мы наткнемся на неверный символ? Для того, чтобы в Вашей основной программе Вы смогли верно определить какая произошла ошибка и на каком символе, я предлагаю создать свой класс-исключение. И возбуждать это исключение при каждой ошибке обработки строки. Этот класс самый простой, просто чтобы не загромождать проект. Вы можете изменить его по Вашему желанию.


type ECalcError = class (Exception)  
end;  

Пойдем дальше. Давайте добавим еще возможность написания функций. Для определения функций давайте создадим два массива. В одном мы будем хранить строковые представления функций, а в другом ссылки на реальные функции. Так же для облегчения процесса редактирования предлагаю создать константу, которая будет отвечать за количество поддерживаемых функций. Также, для описания массива реальных функций, нам понадобиться тип-функция. А для облегчения поиска цифр и знаков операций создадим два множества.


const Sign: set of char = ['+', '-', '*', '/'];  
var Digits: set of char = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];  
type TFunc = function (x: real): real;  
const MaxFunctionID = 2; // - Количество обрабатываемых функций  

Так как использовать ссылки на стандартные процедуры нельзя, или я просто не знаю как. Поэтому нам придется переопределить парочку функций.


function _sin(x: real):real;  
begin  
 result := sin(x);  
end;  
function _cos(x: real):real;  
begin  
 result := cos(x);  
end;  

А теперь можно и определять два массива функций:


const sfunc: array [1..MaxFunctionID] of string[7]= ('sin', 'cos');  
const ffunc: array [1..MaxFunctionID] of TFunc = (_sin, _cos);  

А теперь, для правильной работы с такими массивами я предлагаю написать парочку функций:

Для нахождения функции
Для подсчета значения функции.
Напишем функцию, для проверки, есть ли в строке поддерживаемая функция. Я считаю, что она довольно простая, поэтому сразу приведу её код:


function GetFunction (Line: string; index: integer): integer;  
var i: integer;  
begin  
 for i:= 1 to MaxFunctionID do  
  if sfunc[i] = copy(Line, index, length(sfunc[i])) then  
   begin  
    result := i;  
    exit;  
   end;  
 result := 0;  
end;  

Как Вы видите, функция получает строку, и индекс символа, в котором возможно появление функции. Если наша функция определила, что в строке иметься поддерживаемая математическая функция, то она возвращает её номер, а если таковой нет, то возвращает ноль.

Функция для подсчета значения выглядит еще проще:


function CalculateFunction (Fid: integer; x: real): real;  
begin  
 result := ffunc[Fid](x);  
end;  

Я предлагаю написать еще одну простенькую функция, которая после облегчит нам жизнь. Она будет считать длину строкового написания математической функции.


function GetFunctionNameLength (Fid: integer): integer;  
begin  
 result := length (sfunc[Fid]);  
end;  

Вот мы и закончили писать подготовительные функции. Давайте разберем примерный алгоритм функции разбора строки. Мы будем просматривать строку по символам. Если очередной символ есть цифра, то заносим эту цифру в строку-число. Если символ – знак операции, то записываем в список операций эту операцию, и записываем строку-число в список чисел. Если нам попалась открывающая скобка, то мы должны найти её закрывающую, независимо есть ли в этих скобках вложенные скобки и передать строку, которая находиться в этих скобках функции разделения строк, и записать в массив чисел то, что она вернет. Этот пример называется рекурсией. Идем дальше, если мы нашли символ, не являющийся скобкой, цифрой или знаком операции. Это должно быть функция. Вот здесь нам и пригодится функция для проверки, начинается ли с этой позиции поддерживаемая функция. Если да, то ищем после записи этой функции открывающую скобку, так как параметр любой функции должен идти в скобках. Если скобки нет, то можно вызвать ошибку. Если скобка есть, то действуем по намеченному алгоритму обработки скобки, только в список чисел надо вносить не результат выполнения функции разбиения, а значение функции с этим результатом.

Если Вы досюда дочитали, то я готов Вас поздравить, потому что дальше я предлагаю полный код функции разделения строки на два списка и подсчета значений этого списка.


function Calculate (Line1: string): real;  
var z, d: TStringList;            // - z –список знаков; d – список чисел  
    i, j, c: integer;                //  счетчики  
    w, l, Line: string;                     // begw – переменная, отвечающая за начало числа  
    begw, ok: boolean;  
    res: real;                       // - результат  
    e: ECalcError;                // - ошибка  
    id : integer;                    // - номер функции  
begin  
 w := '';  
 Line := Line1;  
 begw := FALSE;  
 ok := false;  
 z := TStringList.Create;  
 d := TStringList.Create;  
//// Разбиение строки на два списка  ////  
 i := 1;  
 repeat  
 ////  Если знак операции ////  
  if Line[i] in Sign then  
   begin  
    z.Add(Line[i]);  
    if begw then d.Add(w);  
    w := '';  
    begw := TRUE;  
   end  
 ////  Если цифра ////  
  else if Line[i] in digits then  
   begin  
    begw := true;  
    w := w + Line[i];  
   end  
 //// Если скобка ////  
  else if Line[i]='(' then  
   begin  
    c := 1;  
    for j := i+1 to length (Line) do  
     begin  
      if Line[j]='(' then c := c + 1;  
     if Line[j]=')' then c := c - 1;  
      if c=0 then  
       begin  
        ok := true;  
        break;  
       end;  
     end;  
    if not ok then  
     begin  
      e := ECalcError.Create('Не найдена закрывающая скобка к символу ' + inttostr(i));  
      raise e;  
      e.Free;  
     end;  
    l := copy (Line, i+1, j-i-1);  
    d.Add(floattostr(Calculate(l)));  
    delete (Line, i, j-i+1);  
    i := i - 1;  
   end  
 /// Проверка на функцию  
  else if (GetFunction (Line, i)<>0) then  
   begin  
    id := GetFunction (Line, i);  
    if Line[i+GetFunctionNameLength(id)]<>'(' then  {Если после функции нет скобки}  
     begin  
      e := ECalcError.Create('Не найдена скобка после функции в символе  '+ inttostr(i));  
      raise e;  
      e.Free;  
     end;  
{----Если есть скобка----------}  
    c := 1;  
    for j := i+GetFunctionNameLength(id)+1 to length (Line) do  
     begin  
      if Line[j]='(' then c := c + 1;  
      if Line[j]=')' then c := c - 1;  
      if c=0 then  
       begin  
        ok := true;  
        break;  
       end;  
     end;  
    if not ok then  
     begin  
      e := ECalcError.Create('Не найдена закрывающая скобка к символу' + inttostr(i));  
      raise e;  
      e.Free;  
     end;  
    l := copy (Line, i+GetFunctionNameLength(id)+1, j-i-GetFunctionNameLength(id)-1);  
    d.Add(floattostr(CalculateFunction(id, Calculate(l))));  
    delete (Line, i, j-i+1);  
    i := i - 1;  
   end  
 ////  Если неизвестный символ ////  
  else  
   begin  
    e := ECalcError.Create('Неизвестный символ : '+inttostr(i));  
    raise e;  
    e.Free;  
   end;  
  i := i + 1;  
  j := Length (Line);  
  if i>J then break;  
 until false;  
 if w<>'' then d.Add(w);  
 res := (CalculateLists(d, z));  
 z.Free;  
 d.Free;  
 result := res;  
end;  

Вот и все. Надеюсь, Вы быстро разобрались в этой функции.

Добавлено: 07 Августа 2018 07:22:34 Добавил: Андрей Ковальчук

Math - математика, статистика, финансы. Краткий справочник по функциям в Delphi

Тригонгометрические функции:

function ArcCos(X: Extended): Extended;  
function ArcSin(X: Extended): Extended;  
function ArcTan2(Y, X: Extended): Extended; // Арктангенс X/Y возвращает угол в квадранте  
procedure SinCos(Theta: Extended; var Sin, Cos: Extended) register; // возвращает сразу и синус и косинус, вычисления в 2 раза быстрее чем Sin, Cos по отдельности  
function Tan(X: Extended): Extended;  
function Cotan(X: Extended): Extended;  

function Hypot(X, Y: Extended): Extended; // Возвращает значение гипотенузы по катетам
Конвертация углов

function DegToRad(Degrees: Extended): Extended;  
function RadToDeg(Radians: Extended): Extended;  
function GradToRad(Grads: Extended): Extended;  
function RadToGrad(Radians: Extended): Extended;  
function CycleToRad(Cycles: Extended): Extended;  
function RadToCycle(Radians: Extended): Extended;   

Гиперболические функции

function Cosh(X: Extended): Extended;  
function Sinh(X: Extended): Extended;  
function Tanh(X: Extended): Extended;  
function ArcCosh(X: Extended): Extended;  
function ArcSinh(X: Extended): Extended;  
function ArcTanh(X: Extended): Extended;   

Логарифмы, экспоненты и возведение в степень

function LnXP1(X: Extended): Extended; // натуральный логариф x+1 (для более высокой точности при x близких к нулю)  
function Log10(X: Extended): Extended; // десятичный логарифм  
function Log2(X: Extended): Extended; // логарифм по основанию 2  
function LogN(Base, X: Extended): Extended; // логарифм по произвольному основанию  
function IntPower(Base: Extended; Exponent: Integer): Extended register;  
function Power(Base, Exponent: Extended): Extended;   

Разные функции

procedure Frexp(X: Extended; var Mantissa: Extended; var Exponent: Integer) register; // возвращает мантису и экспоненту  
function Ldexp(X: Extended; P: Integer): Extended register; // возвращает X*2**P  
function Ceil(X: Extended):Integer; // округляет до ближайшего большего целого  
function Floor(X: Extended): Integer; // округляет до ближайшего меньшего целого  
function Poly(X: Extended; const Coefficients: array of Double): Extended; // вычисление полинома 

Статистические функции

function Mean(const Data: array of Double): Extended; // среднее арифметическое  
function Sum(const Data: array of Double): Extended register; // сумма ряда  
function SumInt(const Data: array of Integer): Integer register; // сумма ряда целых чисел  
function SumOfSquares(const Data: array of Double): Extended; // сумма квадратов  
procedure SumsAndSquares(const Data: array of Double; var Sum, SumOfSquares: Extended) register; // сумма и сумма квадратов одной функцией  
function MinValue(const Data: array of Double): Double; // минимальное значение в ряду  
function MinIntValue(const Data: array of Integer): Integer; // минимальное значение в ряду целых  
function Min(A,B) минимальное значение из 2х чисел (overload функции для Integer, Int64, Single, Double, Extended)  
function MaxValue(const Data: array of Double): Double;  
function MaxIntValue(const Data: array of Integer): Integer;  
function Max(A,B);  
function StdDev(const Data: array of Double): Extended; // стандартное отклонение  
procedure MeanAndStdDev(const Data: array of Double; var Mean, StdDev: Extended); // среднее арифметическое и стандартное отклонение  
function PopnStdDev(const Data: array of Double): Extended; // распределение стандартного отклонения (Population Standard Deviation)  
function Variance(const Data: array of Double): Extended;  
function PopnVariance(const Data: array of Double): Extended; (Population Variance)  
function TotalVariance(const Data: array of Double): Extended;  
function Norm(const Data: array of Double): Extended; // среднее квадратичное (Sqrt(SumOfSquares))  
procedure MomentSkewKurtosis(const Data: array of Double;  
var M1, M2, M3, M4, Skew, Kurtosis: Extended); // основные статистические моменты  
function RandG(Mean, StdDev: Extended): Extended; // случайные числа с Гауссовским распределением

Финансовые функции

function DoubleDecliningBalance(Cost, Salvage: Extended; Life, Period: Integer): Extended;  
function FutureValue(Rate: Extended; NPeriods: Integer; Payment, PresentValue: Extended; PaymentTime: TPaymentTime): Extended;  
function InterestPayment(Rate: Extended; Period, NPeriods: Integer; PresentValue,  
FutureValue: Extended; PaymentTime: TPaymentTime): Extended;  
function InterestRate(NPeriods: Integer;  
Payment, PresentValue, FutureValue: Extended; PaymentTime: TPaymentTime): Extended;  
function InternalRateOfReturn(Guess: Extended;  
const CashFlows: array of Double): Extended;  
function NumberOfPeriods(Rate, Payment, PresentValue, FutureValue: Extended;  
PaymentTime: TPaymentTime): Extended;  
function NetPresentValue(Rate: Extended; const CashFlows: array of Double;  
PaymentTime: TPaymentTime): Extended;  
function Payment(Rate: Extended; NPeriods: Integer;  
PresentValue, FutureValue: Extended; PaymentTime: TPaymentTime): Extended;  
function PeriodPayment(Rate: Extended; Period, NPeriods: Integer;  
PresentValue, FutureValue: Extended; PaymentTime: TPaymentTime): Extended;   
function PresentValue(Rate: Extended; NPeriods: Integer;  
Payment, FutureValue: Extended; PaymentTime: TPaymentTime): Extended;  
function SLNDepreciation(Cost, Salvage: Extended; Life: Integer): Extended;  
function SYDDepreciation(Cost, Salvage: Extended; Life, Period: Integer): Extended;  

Добавлено: 31 Июля 2018 08:34:10 Добавил: Андрей Ковальчук