Quick start

The API is very simple to use. Let's begin from importing ILCalc's namespace:

using ILCalc;

Context creation

Now we can create new expression context:

var calc = new CalcContext("x");
We may specify the expression arguments to the constructor, or do it later, by the context's Arguments property.
For example, you may add another argument named "y":

calc.Arguments.Add("y");
Context is empty after creation, so let's import some built-it and user constants:

// built-in Pi, E, Inf and NaN constants:
calc.Constants.ImportBuiltIn( );

// user constants:
calc.Constants.Add("lambda0", 1.234);
calc.Constants.Add("epsilon", double.Epsilon);
Now let's import some suitable methods. There are different ways to specify importing methods:

// standard System.Math methods:
calc.Functions.ImportBuiltIn( );

// user methods as delegates:
calc.Functions.Add("Simple", Program.SimpleMethod);

// lambda expressions:
double val = 1.23;
calc.Functions.Add("Magic", () => val); // even with closure!
calc.Functions.Add("Triple", x => 3 * x);

// all suitable methods from whole class:
calc.Functions.Import(typeof(MyFunctionsClass));

// public method reflections:
calc.Functions.AddStatic("Some1",
    typeof(SomeClass).GetMethod("SomeStaticMethod"));

// even instance methods with targets:
var c = new SomeClass();
calc.Functions.AddInstance("Some2",
    typeof(SomeClass).GetMethod("SomeInstanceMethod"), c);
We can find a lot of Add( ) and Import( ) method overloads in FunctionCollection / ConstantDictionary classes.
Look for documentation for more detailed information about this methods.

Now it's time to set expression parse culture.
It's affects on parsing the decimal separator and arguments list separator symbols and number literals parsing.
Culture property can be set to null if you don't want to use any culture-sensitive features (defaults is '.' for decimal separator and ',' for arguments list separator).

calc.Culture = CultureInfo.InvariantCulture;
Expression context contains some properties for other options.
We can disable the ignore-case mode for the identifiers names, that is used by default:

calc.IgnoreCase = false;
Or can ask for performing overflow checks:

calc.OverflowCheck = false;
For the next article examples we will use the default values for this two properties.

Evaluation

Let's define simple arithmetical expression and evaluates it in different ways:

string expression = "4x + 2sin(pi/6) - lambda0";
As we may see, expression contains a couple of number literals (remember that all of them are threated as literals of Double type that is only supported), arithmetical operators (including implicit multiplication), an argument name "x", imported Math.Sin() method with name "sin" and constant "lambda0".

Quick interpreter

This is the best way to use ILCalc when you need to evaluate the specified expression only once:

double result = calc.Evaluate(expression, 1.0);  // = 1.0
Every Evaluate() call executes expression parsing, so this way is not efficient if you need to evaluate the same expression more than once with different argument value.

We should specify expression string and another one parameter, that would be threated as argument "х" value.
Specified parameters count (except expression) should be exactly the same, as the context's Arguments.Count, otherwise we will get an exception here.
Value of arguments should be provided to method in the same order as their names presented in ArgumentCollection class. For example:

calc.Arguments.AddRange("X", "Z", "Lambda");
calc.Evaluate(expression, 1, 2, 3);

// expression will get: X = 1,  Z = 2,  Lambda = 3

Interpret object

This is the best way when you need to evaluate expression more than once with giving some different argument values:

// create object:
Interpret interpret = calc.CreateInterpret(expression);

// and separately invokes the evaluation:
double result = interpret.Evaluate(4.0);  // = 13.0
For this and following evaluation ways, we can use expression optimizer.
Optimizer options may be specified by context's Optimization property:

calc.Optimization =
    OptimizeModes.ConstantFolding |
    OptimizeModes.PowOptimize;

Evaluator object

This is the best way when you need for best performance during much many times expression evaluation.
Creation and esuage looks exactly the same way, as Interpret object, but backstage processes are different:

Evaluator eval = calc.CreateEvaluator(expression);

res = eval.Evaluate(3.0);  // = 9.0
Creation of Evaluator is much slower than creation of Interpret object cause it uses runtime MSIL code generation, but evaluation performance may be more than 10x better. So we should use this way when the best evaluation performance is needed and the startup time is not important.

Tabulator object

This is the best way when you need for extra performance in during expression evaluation with argument(s) in the specified range(s).
Argument range may be represented with ValueRange class. Tabulator creation is the same, as Evaluator:

Tabulator tab = calc.CreateTabulator(expression);

// use it by specify begin, end, step values:
double[] table = tab.Tabulate(1.0, 5.0, 0.25);  // = { 1.0, 2.0, 3.0 ... }

// or via TabRange class:
var range = new TabRange(1.0, 5.0, 1.0);
range.Count = 100; // set points count (recalculate step)

table = tab.Tabulate(range);  // = { 1.0, 1.16, 1.32, 1.48 ... }
Note, that you should specify at least one argument for Tabulator usage. As we can see, usage of Tabulator is nearly the same as the usage of Evaluator, except that argument ranges should be provided instead of argument values.

Validator

If needed to check the expression validity in this context, we can use the context's Validate() method like this:

try
{
    calc.Validate("2 / xyz");  // unresolved identifier "xyz" here
}
catch (SyntaxException err)
{
    // TODO: Show error message

    // we can get some error information here:
    //  err.Position   = 4
    //  err.Length     = 3
    //  err.Substring  = "xyz"
    //  err.Expression = "2 / xyz"
}
Expression validation doesn't causes any compilation or evaluation, preforming only validity check.

Other .NET languages

Example of ILCalc usage in F#:

#light
open ILCalc

let c = new CalcContext()

c.Arguments.Add("x")
c.Constants.ImportBuiltIn()
c.Functions.ImportBuiltIn()
c.Functions.Add("dbl", fun x -> 2.0 * x)

c.Optimization <-
    OptimizeModes.ConstantFolding |||
    OptimizeModes.PowOptimize

let eval = c.CreateEvaluator "2sin(x) + dbl(x)"

c.Functions.Add("func", eval.Evaluate1)

let eval2 = c.CreateEvaluator "func(x) - 2sin(x)"

let result = eval2.Evaluate 3.14

printfn "result = %f" result

Last edited Jul 28, 2009 at 12:10 PM by Pelmen64, version 1

Comments

No comments yet.