Hi All
I would like some ideas on how to get Delphi to evaluate mathematical functions. Suppose I define a function f(x) = x^2. I would like to 'send' f(2) and get 4 back. Now, this is harder then it would seem, because I need to implement _user-defined_ functions at runtime. The user will be able to define a function, say, Sqrt(E^(2*x) + Ln(4*Pi) - log(23*x^3)), in a TEdit, and I have to manipulate the result of the function over a given interval, for many, many values.
Yes, I know of the Poly function, but one of my main problems is that even if I succeeded in translating a function into Delphi using the arithmetic routines, I don't know how I could store the function so that I don't need to keep _re-translating_ it for each iteration. E.G. If the user defined x^2, and I need to calculate x^2 for 2500 values, how can I store the function as an object so that I don't keep translating what is in the TEdit into a function, replacing my x-value, and re-calculating?
Those of you who program in Java may know of the Function method, where you can assign a veritable beast, and evaluate the funtion at given points. I need a Delphi equivalent, or a component (which I have looked for), or a new way of thinking about a solution.

PARSING
-------
I just try to give an idea how these are handled. The Expression Evaluation is a key subject in compiler or interpreter design. In order to understand the expression parsing we should know about the common expression types known as INFIX, PREFIX, POSTFIX. For example :

INFIX : x + y (This is what we can read, it is hard for computers)
PREFIX : +xy (This is more proper for computer)
POSTFIX: xy+ (This is also proper for computer)

In evaluating the expression the INFIX form is the least suitable for computer. If we want to compute fast we should convert it to PREFIX or POSTFIX. Why ? Because the PREFIX and POSTFIX forms requires a single stack to evaluate, and the computer simply push pop the tokens and evaluate in a SINGLE PASS. However in INFIX form we should tokenize the expression each time we evaluate then add paranthesis, evalueate etc.
At this point inorder to correctly evaluate and expression we should have a OPERATOR PRECEDENCE table. You can even search for it from the Delphi help.
WHAT COMPILER DOES
------------------
After converting into a proper form the compiler just places the computation code by making relative reference to the DATA and VARIABLES you specified. This is complex esspecially for optimizing compilers, for example whenever the expression is form of product of sums(ie.e (x+y)*(z+q)*..) it checks the zero condition for each sum then skip if 0.
SO
---
There are two choices we can implement and two factors:
1 - INTERPRETED : Convering expression to POSTFIX and store is in a string quickly parse and evaluate using a TStack class we write.
2 - COMPILED : Declare a modifiable code segment large enough to store the expression (Moderate: can be done in pascal) Parse it and write a cpu code (just like delphi does) which evaluates the result on the fly. And call it in a loop for the arguments. (Hard: can only be done using mixed code-both asm and pascal)

What about you EDO. You've to make the descission.

Regards, ZiF.

0

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Hi,
Again, there is a freeware full source component ( which is said to evaluate only 40% slover than compiled code). As frequent from: http://sunsite.icm.edu.pl/delphi/ftp/d10free/parser10.zip
Take it and install. if you need to modify the source, we(plural-experts-, I do not like to speak like a king) can help you.
Cheers, Igor

The following functions are supported; it doesn't matter if you use lower or upper case:
Arctan, Cos, Sin, Tan, Abs, Exp, Ln, Log, Sqrt, Sqr, Int, Fraq.

it looks decent for trig stuff but if your doing diffEQ and you need it quick get something like Maple.

Hey Inter do you work for HP or something because that is how their caculaters work they call it reverse polish notation.

If you have problems getting the Rx libraries just post something here and I'll send them to you - they are free too.

He, he :-)
No friend (not working for HP) I remember those from '441 - Algorithms and Data Structures with Pascal' or some lecture like that.
Anyway, the theory enlarges back to Automata theory(regular expressions, NFA, DFA, context-free/sensitive languages). Edo, If you really intended to learn a bit theory than seek in internet. There are a lot of lecture notes on the sites.
Regards, Igor

Hello Igor, Zif and Greedy
Thankyou for your comments. Sorry I didn't get back sooner; I have been terribly busy. I am going to check out the components that were suggested, although I don't think they'll be fast enough for my purposes (I'll have to try them to find out).
Igor, converting the expression to POSTFIX and storing it in a string for parsing using a derived TStack class sounds feasible and interesting; could you give me an idea of how to proceed, considering I couldn't find anything referencing POSTFIX in the on-line help? The second method you suggested also sounds interesting, but quite complex. Would you also suggest a list of keywords relating to Automata theory, regarding seeking a bit of theory?

Hi,
I try to writedown something for you tonight for converting from infix to postfix. There are several reference in internet esspecially lecture notes. Automata is quickly scanned, for example at: http://cs-www.uchicago.edu/~odonnell/OData/Courses/CS221/Lecture-Notes/syntax.html
Or use the keywords
+"parse tree" +automata
OR
+"parse tree" +"lecture notes"
from www.infoseek.com
To succesfully understand the expression notation you'd better know about expression trees, or parse trees and tree traversal. Sorry, can not give more info, I have to leave
c.u
Igor

Hi,
Excuse me, since my IP changes, there is a problem that I can have notification via email. Please those who want the code please give email addresses. It is not proper for this thread to post 700 lines (yes it took about 700 lines) of code with all the conversion and evaluation staff in it. So, let me email you the source, then we discuss and finalize it.
Regards,
Igor

Since we require(or this is because I can not design it better) fast execution, there are some
restrictions imposed by the parser (although normal variable assignment is easy to add). Here I
should act tricky. You will see what I mean. Here is the general usage:

Say we have : f(A,B,C,D,E) = cos(A) + sqr(B^sin(A)/C+ln(D)*E)

The following is the real delphi code that executes the function above 10000 times with the
random values with only parsing it ONCE:
//*************START*****************
var
T: TEvaluator;
A, B, C, D, E: double;
i : integer;
begin
Randomize;
T := TEvaluator.Create;
try
// 1 - Assign the string expression to expression.
// At this step all the convertions are done
// The evaluator only needs Variable references
T.Expression := 'cos(A) + sqr(B^sin(A)/C+ln(D)*E)';
// 2 - You MAY get the postfix representation (OPTIONAL) afterwards
Edit2.Text := T.PostFix;
// 3 - This is the trick. Here we say parser that for subsequent evaluations
// It will use the actual address of the variable 'A' when using computation
// with A. THE RESTRICTION I MEANT IS THIS!
T.AddVar('A', A); T.AddVar('B', B); T.AddVar('C', C);
T.AddVar('D', D); T.AddVar('E', E);
// 4 - Begin eval just checks if all the variables in the expression are assigned to values
// if that is the case it is ready for evaluation
if T.BeginEval then
for i := 1 to 10000 do
begin
A := Random; B := Random; C := Random;
D := Random; E := Random;
T.Evaluate;
end;
finally
T.Free;
end;
end;
//*************END*****************

If the variable passing above appears some of you weird, consider the following static
allocation:

1 - Tell your users that they can only use 'A' to 'Z'.
2 - Construct an array of Double
V : array['A'..'Z'] of Double;
3 - After T.Evaluate add all the vars with
for i := Ord('A') to Ord('Z') do
T.AddVar(Char(i), V[i]);
4 - The redundant variables are not important. That means if only A,B,C is used in expression
it is not an error to Add D also.

! YOU ARE WELCOME TO OFFER A MORE CLEVER SCEME AND I DO IMPLEMENT IT !

BencMark :
-------------
133Mhz Pentium, Nt4.0 : f(.) = cos(A) + sqr(B^sin(A)/C+ln(D)*E)
performs about 36K function evaluations/second

Any comment well come
Regards, (You remind me my university days, I work hard for it, may it please any one who use)
Igor
inter@kosgeb.tekmer.gov.tr (not reliable nowadays)

Hi Inter
The benchmark is within the limits of speed I require. Post an answer whenever, you certainly deserve points for this most edifying ( ;-) to Greedy ) work. I look forward to working with the code.

Igor,
You made such a killer effort, post 'HELLO' as an answer, I will award the points and we can kill this thread.
Thankyou one and all for sending me useful stuff and perpetuating stimulating discourse.

Thanks friend,
I have been away for my exams, but return now. What do you think about publising the source in Delphi sites(I am always at standby for bug fixes and improvments). It is nice to work for friends,
Regards, Igor

0

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

A Formula Parser?

AND

Something that ones the formula is parsed --> calculations are made...

Correct?

Regards, Zif.