一个有效的方式来存储数学表达式以供计算是什么?

huangapple go评论93阅读模式
英文:

What is an efficient way to store mathematical expressions for evaluation?

问题

我的目标是编写一个程序,可以解析包括变量在内的数学表达式,例如"x^2+3y+sin(x)",然后在给定它们的值时进行评估(例如,对于x=0,y=1,输出将为3)。

解析本身没有问题 - 我使用了一种shunting yard算法的变体,它运行良好。然而,我现在正在考虑如何以便于评估的方式存储输出结果。

一种方式是简单地将表达式中的所有变量替换为它们的值,然后进行评估。如果只评估一次表达式,这将是可行的 - 但是,我预计会有一个表达式,会有许多不同的值输入到其中。因此,我希望找到一种存储解析后的表达式的方法,以便替换变量并进行评估既快速又合理地使用内存。有什么想法吗?

我想到的一种方法是将表达式存储为逆波兰表示法的标记数组,这样可以高效地进行评估。Token 可以看起来像这样:

enum TokenType {NUMBER, VARIABLE, OPERATOR, FUNCTION};

struct Token
{
  TokenType type;
  union
  {
    int value; // if type = NUMERIC
    int variable_index;  // if type = VARIABLE
    operator_params op;  // if type = OPERATOR
    function_params fun; // if type = FUNCTION
  };
};

变量值可以存储在一个简单的数组中,并通过 Tokenvariable index 进行访问。当值发生变化时,我们只需更新变量值数组 - 表达式保持不变。

我不确定在这种应用程序中存储运算符和函数的最佳方式是什么。我的思路是使用指向正确操作的函数指针,但我不确定这是否是最佳方式。

英文:

My goal is to write a program that can parse mathematical expressions including variables - like "x^2+3y+sin(x)" - and then evaluate them when given their values (so for x=0, y=1, the output would be 3).

There is no problem with the parsing itself - I used a version of the shunting yard algorithm and it works well. However, I am now thinking how to store the output in a way that can be easily evaluated.

One way would be to simply replace all variables in the expression with their values and then evaluate it. That would be fine if the expression were evaluated once - however, I expect that there will be one expression and many different values will be plugged into it. Thus, I would like to find a way to store the parsed expression so that substituting the variables and evaluating it is fast, as well as reasonably memory-efficient. Any ideas?

One way that occured to me was to store the expression as an array of tokens in reverse Polish notation, which can be evaluated efficiently.
The Token couldlook something like this:

enum TokenType {NUMBER, VARIABLE, OPERATOR, FUNCTION};

struct Token
{
  TokenType type;
  union
  {
    int value; // if type = NUMERIC
    int variable_index;  // if type = VARIABLE
    operator_params op;  // if type = OPERATOR
    function_params fun; // if type = FUNCTION
  }
}

The variable values could be strored in a simple array and accesed by the variable index of the Token. When the values change, we can just update the array of variable values - the expression stays the same.

I am not sure what the best way to store operators and functions for this application is. My thoughts go towards having a function pointer that points to the right operation, but I am not sure if that is the best way.

答案1

得分: 2

按照逆波兰表示法(RPN)对令牌进行排序是一种方法,而且性能相当好。
这些令牌可以是文字、变量或运算符。要评估表达式,您按顺序处理这些令牌,如果是文字或变量,就将其值推送到堆栈中。如果是运算符,您从堆栈中弹出一个或两个项目(取决于它是一元还是二元运算符),对这些项目应用运算,并将结果推送回去。对于格式良好的表达式,最终会在堆栈上留下一个值,这就是评估的最终结果。

另一种有效的方法是创建嵌套的 Lambda 表达式,就像我在这个 CodeReview 帖子中所示。其主要优势在于,解析表达式的结果是一个 Lambda 函数,其工作方式几乎与 C++ 中的常规函数相同。

英文:

Ordering the tokens in RPN order is one way to do it, and quite performant.
The tokens would either be literals, variables or operators. To evaluate the expression, you process the tokens in order, and if it's a literals or variable, you push its value to a stack. If it's an operation, you pop one or two items from the stack (depending on whether it is a unary or binary operator), apply the operation on those items, and push the result back. For well-formed expressions, you will be left with one value on the stack at the end, which is the final result of the evaluation.

Another approach which works is to create nested lambda expressions, as I've shown in this CodeReview post. The main advantage of that is that the result of parsing the expression is a lambda function which works pretty much the same as a regular function in C++.

huangapple
  • 本文由 发表于 2023年7月14日 02:18:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/76682231.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定