Usage of EvaluateOptions.IterateParameters with Functions

Sep 10, 2009 at 4:07 PM

Ahhhh ..... great!

I just see two problems right now.
1.) Eaven if an Expression "is iterated", the EvaluateParameter Event should be rissen -> but only once, at first time Call.

2.) And this is a real puzzle ... We must define if a Function-Result is used as array and iterated, or if the function parameters
have to be iterated. The best would be if you could do this per Function instead of per Expression. There is one simple reason:
you cannot calculate aggregation-functions (i.e. moving averages) with single values, you must use a function taking arrays and
returning arrays. On the other hand you have functions like Math (sin, tan, ...) where you need single values. Not easy hu?
Maybe NCalc must use two different kind of brackets for beeing array threated or not?

i.e.

public Double[] Foo(Double[] inA, Double[] inB) {return (Double[]) inA.Clone(); }
public Double Bar(Double inA, Double inB) {return (inA + inB); }

new Expression("Foo([a], [b]) * 2", Options.Iterate)  // -> iterate function result array
new Expression("Bar({c}, {d}) * 2 ", Options.Iterate) // -> iterate function paramters

But what to do if Bar is used as parameter for Foo? Or vice versa?

new Expression("Foo( [a], Bar({c}, {d}) ) * 2", Options.Iterate)
so you have to iterate each c and d pass to function bar and do all the bar results as array-parameter for function Foo.
Now this is where i got stuck im my current "workaround" solution. And today I have not solved the subfunction issue:
new Expression("Foo(Foo([a])^3)");


One solution could be that every function is threated as a new single expression, and passed back the same way a parameter does.
You have to do this recoursvly.

i.e.:
Expression("[Foo( [Foo( [1] ) ] + 3 )] * 2", Options.Iterate);
-> parameter = Foo([Foo([1])] + 3])
   -> new expression, iterating
      -> parameter = Foo([1])
         -> new expression, no iterating because its only one function
      <- = double[]   
   <- double[] + 3 = double[]
<- double[] * 2 = double[]

so i stared to play with unix shell like signs to mark weather its a normal parameter or a function
"[`Foo( [`Foo( [1] ) ´] + 3 )´] * 2",

 param 1 `Foo( [`Foo( [1] ) ´] + 3 )´ // -> Containes more `´-expressions -> iterate = ture
 param 2 `Foo( [1] ) ´                // -> no more `´, do not iterate

I hope I could show you my Problems :-)

Chris

Sep 10, 2009 at 4:10 PM
Edited Sep 11, 2009 at 3:43 PM

Ok, here is a prototype how you can work with this issue:

 

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Evaluant.Calculator;

namespace NCalcTest2
{
    class Expr
    {
        private Dictionary<String,Object> pList = new Dictionary<String,Object>();
        private String exprStr;

        private static Regex regex = new Regex("`[^`´]*´");

        public Expr(String exprStr, params Object[] parameters) 
        {
            this.exprStr = exprStr;

            //1.) Convert Array to Liste
            for(int i=0;i<parameters.Length;i++)
                this.pList[i.ToString()] = parameters[i]; 
        }

        private Expression NewExpression(String exprStr, Boolean iterate)
        {
            Expression e;

            if (iterate)
                e = new Expression(exprStr,EvaluateOptions.IterateParameters);
            else
                e = new Expression(exprStr);

            e.Parameters = pList;
            AddFunctionHandler(e);
            return e; 
        }

        public Object[] Evaluate()
        {
            Expression e;

            while (regex.IsMatch(exprStr))
            {
                String arrFunc = regex.Match(exprStr).Value.Trim('`','´');
                Console.WriteLine(arrFunc);

                e = NewExpression(arrFunc, false);
                
                String parIndex = pList.Count().ToString();
                pList.Add(parIndex, e.Evaluate());
                exprStr = regex.Replace(exprStr, String.Format("[{0}]", parIndex));
            }

            //Just debugging Stuff
            Console.WriteLine(exprStr);
            foreach (KeyValuePair<String, Object> pair in pList)
            {
                String val = "";
                if (pair.Value.GetType().IsArray)
                    foreach (Object vArr in (Array)pair.Value)
                        val += vArr.ToString() + ",";
                else
                    val = pair.Value.ToString();

                Console.WriteLine("{0} : {1}", pair.Key, val);
            }

            e = NewExpression(exprStr, true);
            Object result = e.Evaluate();
            List<Object> oList = (List<Object>)result;
            Console.WriteLine("{0} : {1}", result.ToString(), oList.Count);

            foreach (var o in oList)
                Console.WriteLine(o.ToString());
            //End of debugging stuff

            return oList.ToArray() ;
        }

        private void AddFunctionHandler(Expression e)
        {
            e.EvaluateFunction += delegate(String name, FunctionArgs args)
            {
                Console.WriteLine("Function = {0}", name);
                if (name == "foo" || name == "Foo")
                {
                    Object[] parameters = new Object[args.Parameters.Length];
                    for (int j = 0; j < parameters.Length; j++)
                    {
                        String exp = args.Parameters[j].ParsedExpression.ToString();
                        if(ExpressionHasFunction(exp))
                            parameters[j] = IListToArr(((IList)NewExpression(exp, true).Evaluate()));
                        else 
                            parameters[j] = args.Parameters[0].Evaluate();
                    }
                    
                    { }
                    args.Result = FooFunction((Double[]) parameters[0]);
                }
                else if (name == "bar" || name == "Bar")
                {
                    Object[] parameters = new Object[args.Parameters.Length];
                    for (int j = 0; j < parameters.Length; j++)
                    {
                        String exp = args.Parameters[j].ParsedExpression.ToString();
                        parameters[j] = NewExpression(exp ,false).Evaluate();
                        Console.WriteLine("Bar = {0}", parameters[j]);
                    }
                    
                    args.Result = BarFunction(
                        Convert.ToDouble(parameters[0]),
                        Convert.ToDouble(parameters[1]));
                }
            };
        }

        private Double[] FooFunction(Double[] inArr)
        {
            Double[] result = (Double[])inArr.Clone();
            for (int i = 0; i < result.Length; i++)
            {
                result[i] += 2;
            }

            return result;
        }

        private Double BarFunction(Double a, Double b)
        {
            return a * b;
        }

        private Boolean ExpressionHasFunction(String expStr)
        {
            if (regex.IsMatch(expStr)) { return false; } // just to be on the safe side

            Expression e = new Expression(expStr);
            Boolean hasFunction = false;

            e.EvaluateFunction += delegate(String name, FunctionArgs args)
            {
                hasFunction = true;
            };

            try
            {
                e.Evaluate();
            }
            catch { }

            return hasFunction;
        }

        private Object IListToArr(IList iList)
        {
            Array result = Array.CreateInstance(typeof(Double), iList.Count);
            iList.CopyTo(result,0);
            return result;
        }
    }
}


And call from Main

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Evaluant.Calculator;

namespace NCalcTest2
{
    class Program
    {
        static void Main(string[] args)
        {
            Double[] a = new Double[]{1,2,3,4,5};
            Double[] b = new Double[]{6,7,8,9,10};
            Double c = 3.0;
            //String ex = "`Foo(`Foo([0])´)´ * [1]^[2]";
            //String ex = "`Foo(`Foo([0])´)´ * [1]^Bar(2,3)";
            //String ex = "`Foo(`Foo([0])´)´ * [1]^Bar([0],[1])";
            String ex = "`Foo(Bar(1,1))´ * [1]^Bar([0],[1])";

            Expr e = new Expr(ex, a, b, c);
            e.Evaluate();

            Console.ReadKey();
        }

        
    }
}

 

Output:

15625
78364164096
1.91581231380566E+20
3.24518553658427E+32
5.15377520732011E+47

So there are still some tasks open like Correct Typecasting for functions parameters but I think you can get the clue. Just quote array functions and do not for Math functions.

Chris