1

I am writing a open source engineering calculator application in C#.

There is a class called CalcVar, which represents a single calculator variable. They are added to a Calculator class. On construction of this CalcVar variable, it is passed an "equation" lambda function (of type Func<double>, as shown below) whose body may contain any number of CalcVar objects (which also belong to the same Calculator class), which are multiplied/added/divided/whatever together and return the value for this CalcVar object.

e.g.

class OhmsLaw : Calculator
{
    CalcVar voltage;
    CalcVar current;
    CalcVar resistance;

    public OhmsLaw() : base("Ohm's Law", "Ohm's law calculator.")
    {           
        this.voltage = new CalcVar(() => current.RawVal*resistance.RawVal);

        this.current = new CalcVar(() => voltage.RawVal / resistance.RawVal);

        this.resistance = new CalcVar(() => voltage.RawVal / current.RawVal);
    }
}

I want to be able to somehow work out what other CalcVar variables are used inside a given CalcVar's equation function, so I can work out the variables dependencies.

How would I go about doing this? I feel like I need to emit an event or similar when a CalcVar's RawVal is accessed, and somehow get the CalcVar in question to subscribe to these events and log which ones fire when it calls equation.Invoke().

gbmhunter
  • 1,747
  • 3
  • 23
  • 24
  • You could do that, but if the equation includes conditional code, not every execution will access all CalcVars. I think your best bet is to explicitly declare what CalcVars are used. – Cecilio Pardo Jan 05 '16 at 22:12
  • That's a good point that I hadn't thought about, and will be true if I use things like checkboxes in the calculators. I was really hoping for an automatic way of calculating dependencies to make life easier for the person writing a calculator. – gbmhunter Jan 05 '16 at 22:16
  • Other option could be not using C# for the formulas, but some embedded language (there are may options). Then you can examine the script and collect all references. – Cecilio Pardo Jan 05 '16 at 22:19
  • Yes I have had a look at "calculator engines" such as http://ncalc.codeplex.com/ and https://github.com/pieterderycke/Jace. One worry I have with these is that I want to include conditional statements as part of the equation, and I don't know how this would work with one of these calculator engines. Also, I don't know if these calculation engines allow you to work out the dependencies. I'm sure they calculate it somewhere internally, but after a quick browse I couldn't find any documentation on it. – gbmhunter Jan 05 '16 at 22:23
  • I'd go with explicit declaration, and maybe add automatic declaration at some point if necessary. The expression trees look like a good option for that. – Cecilio Pardo Jan 05 '16 at 22:31

1 Answers1

4

You may want CalcVar to accept an expression tree rather than a lambda.
e.g.

Expression<Func<double>>

instead of

Func<double>

That way you can inspect the body of the expression tree within CalcVar and see what the dependencies are. You can look here for an introduction to expression trees:

What are Expression Trees and how do you use them and why would you use them?

Community
  • 1
  • 1
RJM
  • 1,164
  • 9
  • 21
  • I believe this is the right thing to do, however converting it to an expression didn't report any parameters, as it treated the `current.rawVal*res.rawVal` as constants (which was another issue in itself!). I got around this by changing the function so it got passed a list of all the calculator variables (`Func, double>`) and used these instead in the lambda (e.g. `(calcVars) => calcVars[1].RawVal * calcVars[2].RawVal`). The problem is that the only parameter that the expression tree reports is List calcVars, and not the individual elements accessed from the list. – gbmhunter Jan 05 '16 at 23:29
  • Oh, hit a major roadblock here, when trying to expand the lambda equation function to multiple lines (which they will have), I get the error: `a lambda expression with statement body cannot be converted to an expression tree` Doesn't look like this method will work for anything but the simplest of equations :-(. – gbmhunter Jan 06 '16 at 01:17
  • The expression trees are intended for use when the content of the body is an expression. If, instead, the body consists of one or more statements, that body cannot be represented as an expression tree. For a calculator, each calculation **should** be able to represented by a single expression...although writing it as multiple statements might be more readable and would certainly make reusing intermediate results easier. – RJM Jan 06 '16 at 02:53