我打算编写一个程序来计算给定函数的零点。我决定编写一个解析器来解析该函数(我之前从未写过)。它是像"sin(1/x)+exp(x)"
这样的实变量的实值函数。我想使用像Bisection和Newton这样的根查找方法。由于这些方法是迭代的,我想避免每次在每个点x
的循环中评估函数。所以在我努力编写自己的解析器之前,我想知道可以解析一次并在点f
评估函数x0, x2, ..., xn
而无需为每个解析f
x
?
答案 0 :(得分:2)
这个问题有两种标准方法:
将公式解析为所谓的"抽象语法树" (AST)。这是一个表示公式结构的编译器数据结构,可以通过代码快速检查。也可以相对快速地将AST评估为公式。看到我的答案 构建支持上述任务的递归下降解析器: Is there an alternative for flex/bison that is usable on 8-bit embedded systems?
以某种方式将公式编译成您的编程语言。这通常涉及首先构建AST,然后将AST转换为编程语言术语,在该结果上运行编译器,然后加载编译结果。使用C等编译语言可能会很尴尬,因为缺少动态链接;使用Java或C#等语言更容易,因为它们鼓励动态加载。 您可以猜测,这种方法需要付出更多的努力,但现在可以通过编程语言尽快评估公式。您可以通过临时(例如,递归下降)解析和翻译来完成此操作,或者您可以使用重写"工具以更加规律的方式解决此问题。一种语法到另一种:https://softwarerecs.stackexchange.com/a/31379/101
答案 1 :(得分:1)
正如Ira已经指出的那样,您将表达式解析为抽象语法树。适合您的抽象语法树看起来类似于:
interface AstNode {
double eval(double x);
}
class ConstantNode implements AstNode {
double value;
double eval(double x) {
return value;
}
}
class VariableNode implements AstNode {
double eval(double x) {
return x;
}
}
class OperatorNode implements AstNode {
char op;
AstNode left;
AstNode right;
double eval(double x) {
switch (op) {
case '+': return left.eval(x) + right.eval(x);
case '-': return left.eval(x) - right.eval(x);
case '/': return left.eval(x) / right.eval(x);
case '*': return left.eval(x) * right.eval(x);
case '^': return Math.pow(left.eval(x), right.eval(x));
default:
throw new RuntimeException("Invalid op " + op);
}
}
}
class Function implements AstNode {
...
将表达式解析为树后,只需使用您感兴趣的值调用eval()
即可。
答案 2 :(得分:0)
也许你可以用Java Scripting Support做到这一点。例如,应该可以在Javascript(Nashorn)中评估函数。
亲:你不需要自己解析这个功能。只需提供脚本API。