1

我们有以下格式的半复杂表达式:
“25 + [Variable1] > [Variable2]”

我们需要一个表达式求值器来解析表达式并使用回调来请求变量值并计算出表达式的整体结果。它必须是回调,因为有成千上万的变量。

我们需要常用的数学运算符,但也需要诸如“if”之类的东西。语言越丰富越好。

我们可以使用任何我们想要的语言。有人有什么建议吗?

4

5 回答 5

3

您是否考虑过使用 Mono.CSharp.Evaluator?似乎这与适当设置的 InteractiveBaseClass 结合使用可以很好地完成这个技巧,并且只需最少的努力。

请注意,以下使用 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(); } }
    }
}

真可惜它运行有点慢。例如,在我的 i7-m620 上运行 10,000 次需要将近 8 秒:

[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

如果我们可以将其解析并编译为 IL 以便我们可以以 .NET 速度执行它,那就太好了,但这听起来有点像白日梦……

[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

天啊。

需要像 IF 这样的类似 excel 的表达式构造吗?建立你自己的!

    [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;
        }
    }
于 2012-05-10T10:28:15.440 回答
2

查看NCalc。它是 .NET,应该支持您的要求。

于 2011-02-12T17:44:15.023 回答
1

纯表达式求值器实际上很容易编写。

请参阅此 SO 答案,该答案显示了十几种语言的表达式评估器。您应该能够适应其中之一:

Code Golf:数学表达式评估器(尊重 PEMDAS)

编辑:显然,无论是谁都没有去那里检查解决方案。是的,有一堆是为了满足高尔夫规则(通常是“最小”)而挤得满满当当的,但其中大多数都用明文版本的算法解释得很清楚。

于 2011-02-12T17:43:45.480 回答
0

嗯……你需要一门语言。您有 C#、VB.Net、IronPython、IronRuby 等。

使用正则表达式简单地替换打开的变量(也许您甚至提前知道它们并且只需要一个字符串。替换)然后使用 CodeDOM(对于 C# 或 VB.Net)或使用 DLR(IronPython、IronRuby)编译脚本。您可以简单地将变量作为方法参数添加到用于封装代码(对于 CodeDOM)的方法包装器中,或者只是将变量注入 DLR。我们在业务团队中实施的这两种变体都以更少的努力和可靠的努力。

当您紧急请求回调时,请在上述解决方案中添加一个方法,该方法与编程语言的主机通信,名称如 ValueOf(string)。所以你可以写

价值(“A”)>价值(“B”) - 10

玩得开心。

于 2011-02-12T17:50:08.830 回答
0

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

通过 lambda 或块回调处理变量查找。

理解数字、字符串和布尔常量。

一元运算符 + - !

运算符 || && < <= == != >= > + - * / %

用 ( ) 分组

如果存在语法错误,则引发 Expression::SyntaxError。

于 2011-04-18T09:50:26.627 回答