Expression evaluator for C#/Python/Ruby

我的未来我决定 提交于 2019-12-24 09:52:34

问题


We have semi-complicated expressions in the format:
"25 + [Variable1] > [Variable2]"

We need an expression evaluator to parse the expression and use a callback to ask for the variable values and work out the overall result of the expression. It has to be a callback as there are thousands of variables.

We need the usual math operators but also things like "if" etc. The richer the language the better.

We can use any language we want. Anyone have any suggestions?


回答1:


Check out NCalc. It's .NET and should support your requirements.




回答2:


Have you considered using Mono.CSharp.Evaluator? It seems like this in conjunction with an appropriatelly set InteractiveBaseClass would do the trick quite nicely, and with minimal effort.

Note that the following uses Mono 2.11.1 alpha.

using System;
using System.Diagnostics;
using Mono.CSharp;
using NUnit.Framework;

public class MonoExpressionEvaluator
{
    [Test]
    public void ProofOfConcept()
    {
        Evaluator evaluator = new Evaluator(new CompilerContext(new CompilerSettings(), new ConsoleReportPrinter()));
        evaluator.InteractiveBaseClass = typeof (Variables);
        Variables.Variable1Callback = () => 5.1;
        Variables.Variable2Callback = () => 30;

        var result = evaluator.Evaluate("25 + Variable1 > Variable2");

        Assert.AreEqual(25 + Variables.Variable1 > Variables.Variable2, result);
        Console.WriteLine(result);
    }

    public class Variables
    {
        internal static Func<double> Variable1Callback;

        public static Double Variable1 { get { return Variable1Callback(); } }

        internal static Func<double> Variable2Callback;

        public static Double Variable2 { get { return Variable2Callback(); } }
    }
}

Real shame it runs a little slow. For instance, on my i7-m620 it takes almost 8 seconds to run this 10,000 times:

[Test]
public void BenchmarkEvaluate()
{
    Evaluator evaluator = new Evaluator(new CompilerContext(new CompilerSettings(), new ConsoleReportPrinter()));
    evaluator.InteractiveBaseClass = typeof(Variables);
    Variables.Variable1Callback = () => 5.1;
    Variables.Variable2Callback = () => 30;

    var sw = Stopwatch.StartNew();
    for (int i = 1; i < 10000; i++)
        evaluator.Evaluate("25 + Variable1 > Variable2");
    sw.Stop();

    Console.WriteLine(sw.Elapsed);
}

00:00:07.6035024

It'd be great if we could parse and compile it to IL so we could execute it at .NET speeds, but that sounds like a bit of a pipe dream...

[Test]
public void BenchmarkCompiledMethod()
{
    Evaluator evaluator = new Evaluator(new CompilerContext(new CompilerSettings(), new ConsoleReportPrinter()));
    evaluator.InteractiveBaseClass = typeof(Variables);
    Variables.Variable1Callback = () => 5.1;
    Variables.Variable2Callback = () => 30;

    var method = evaluator.Compile("25 + Variable1 > Variable2");
    object result = null;
    method(ref result);
    Assert.AreEqual(25 + Variables.Variable1 > Variables.Variable2, result);

    Variables.Variable2Callback = () => 31;
    method(ref result);
    Assert.AreEqual(25 + Variables.Variable1 > Variables.Variable2, result);

    var sw = Stopwatch.StartNew();
    for (int i = 1; i < 10000; i++)
        method(ref result);
    sw.Stop();
    Console.WriteLine(sw.Elapsed);
}

00:00:00.0003799

Oh my.

Need excel-like expression constructs like IF? Build your own!

    [Test]
    public void ProofOfConcept2()
    {
        Evaluator evaluator = new Evaluator(new CompilerContext(new CompilerSettings(), new ConsoleReportPrinter()));
        evaluator.InteractiveBaseClass = typeof(Variables2);
        Variables.Variable1Callback = () => 5.1;
        Variables.Variable2Callback = () => 30;

        var result = evaluator.Evaluate(@"IF(25 + Variable1 > Variable2, ""TRUE"", ""FALSE"")");

        Assert.AreEqual("TRUE", result);
        Console.WriteLine(result);
    }

    public class Variables2 : Variables
    {
        public static T IF<T>(bool expr, T trueValue, T falseValue)
        {
            return expr ? trueValue : falseValue;
        }
    }



回答3:


Pure expression evaluators are actually pretty easy to write.

See this SO answer which shows expression evaluators in a dozen langauges. You should be able to adapt one of these:

Code Golf: Mathematical expression evaluator (that respects PEMDAS)

EDIT: Whoever dinged this obviously didn't go and examine the solutions there. Yes, there are a bunch that are crammed tight to meet the golf-rules (typically "smallest") but most of them are explained pretty clearly with a cleartext version of algorithm.




回答4:


Well ... you need a language. You have C#, VB.Net, IronPython, IronRuby, and others.

Simple replace the open variables using regex (maybe you even know them ahead and just need a string.Replace) and then compile the script using CodeDOM (for C# or VB.Net) or use the DLR (IronPython, IronRuby). You can simply add the variables as method parameters in the method wrapper you use to encapsulate your code (for CodeDOM) or just inject the variables in the DLR. Both variants we implemented in our team in business with less effort and reliable effort.

When you urgently regquire the callback, well the add to the solutions above a method which communicate with the host of the programming language with a name like ValueOf(string). So you can write

ValueOf("A") > ValueOf("B") - 10

Have fun.




回答5:


http://code.google.com/p/bc-expression/

Handles variable lookup via a lambda or block callback.

Understands numeric, string and boolean constants.

Unary operators + - !

Operators || && < <= == != >= > + - * / %

Grouping with ( )

Raises an Expression::SyntaxError if there's a syntax error.



来源:https://stackoverflow.com/questions/4979423/expression-evaluator-for-c-python-ruby

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!