C#/Python/Ruby的表达式求值器
我们有一些比较复杂的表达式,格式是:
"25 + [Variable1] > [Variable2]"
我们需要一个表达式计算器来解析这个表达式,并使用一个回调函数来请求变量的值,然后计算出整个表达式的结果。之所以要用回调函数,是因为变量的数量可能有成千上万。
我们需要常见的数学运算符,还有像“如果”这样的功能。语言越丰富越好。
我们可以使用任何我们想要的编程语言。有没有人有什么建议?
5 个回答
1
纯表达式求值器其实写起来挺简单的。
你可以看看这个StackOverflow的回答,里面展示了很多种语言的表达式求值器。你应该能根据其中一种进行调整:
编辑:谁要是给这个点了差评,显然没有去看看那里的解决方案。没错,有一些是为了符合高尔夫规则(通常是“最小”)而写得很紧凑,但大多数都有很清晰的解释和算法的明文版本。
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
哎呀。
需要像 Excel 那样的表达式构造,比如 IF 吗?那就自己动手做吧!
[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;
}
}
2
可以看看 NCalc。这是一个.NET的工具,应该能满足你的需求。