这不是学校作业或任何事情,但我意识到这是一个主要的学术问题。但是,我一直在努力做的是解析“数学”文本并提出答案。
例如 - 我可以弄清楚如何解析'5 + 5'或'3 * 5' - 但是当我尝试正确地将操作链接在一起时我失败了。
(5 + 5)* 3
我大多只是在烦扰我,我无法弄明白。如果有人能指出我的方向,我真的很感激。
修改 感谢所有快速回复。对不起,我没有更好地解释。
首先 - 我没有使用正则表达式。我也知道已有的库可以作为字符串使用数学表达式并返回正确的值。所以,我主要是看着这个,因为,遗憾的是,我并没有“明白”。
第二 - 我尝试做过的事情(可能是误入歧途)但是我正在计算'('和')'并首先评估最深的项目。在简单的例子中,这有效;但我的代码不漂亮,更复杂的东西崩溃。当我'计算'最低级别时,我正在修改字符串。
所以... (5 + 5)* 3
会变成 10 * 3
然后评估为 30
但它感觉'错了'。
我希望这有助于澄清事情。我肯定会查看提供的链接。
答案 0 :(得分:9)
在使用简单的图形应用程序时,我使用this algorithm(相当容易理解并且非常适合这些简单的数学表达式)首先将表达式转换为RPN然后计算结果。 RPN对于不同的变量值执行起来非常好而且快速。
当然,语言解析是一个非常广泛的主题,还有许多其他的方法(以及它的预制工具)
答案 1 :(得分:4)
@Rising Star [我希望将此添加为评论,但格式化失败]
这似乎违反直觉,但二叉树更简单,更灵活。在这种情况下,节点可以是常量(数字)或运算符。当您决定使用控制流和函数等元素扩展语言时,二叉树会使生活变得更容易。
示例:
((3 + 4 - 1) * 5 + 6 * -7) / 2
'/'
/ \
+ 2
/ \
* *
/ \ / \
- 5 6 -7
/ \
+ 1
/ \
3 4
在上述情况下,扫描仪已编程为读取“ - ”后跟一系列数字作为单个数字,因此“-7”将作为“数字”标记的值组件返回。 ' - '后跟空格被撤回为“减号”令牌。这使得解析器更容易编写。它在你想要“ - (x * y)”的情况下失败,但你可以很容易地将表达式改为“0 - exp”
答案 2 :(得分:3)
这是一个简单的(天真的运算符优先级)语法,可以满足您的需求。
expression =
term
| expression "+" term
| expression "-" term .
term =
factor
| term "*" factor
| term "/" factor .
factor =
number
| "(" expression ")" .
当您处理“因子”时,您只需检查下一个标记是否为数字,或“(”,如果它是“(”那么您再解析“表达式”,当表达式返回时检查如果下一个标记是“)”。您可以通过使用 out 或 ref 参数将[calculate | read]值冒泡到父级,或构建一个表达树。
以下是EBNF中的相同内容:
expression =
term
{ "+" term | "-" term } .
term =
factor
{ "*" factor | "/" factor }.
factor =
number
| "(" expression ")" .
答案 3 :(得分:2)
你有没有在学校上过正规语言课?实际上你需要一个语法来解析。
编辑:哦,废话,维基百科说我错了,但现在我忘了正确的名字:( http://en.wikipedia.org/wiki/Formal_grammar答案 4 :(得分:2)
去年,我写了一个基本的数学评估员,原因是我不记得了。在任何一段时间内,它都不是一个“适当的”解析器,并且......就像所有旧代码一样,我现在并不为它感到骄傲。
但是you can take a look,看看它是否对你有帮助。
通过启动此standalone Java app
运行一些输入测试答案 5 :(得分:2)
当我想解析某些内容时,我决定使用GOLD Parser:
解析器包含sample grammars,包括例如一个用于操作员优先。
除了GOLD之外,还有其他更着名的解析器,例如ANTLR,我还没有用过。
答案 6 :(得分:2)
答案 7 :(得分:2)
正如许多答案已经说明的那样,问题是您需要recursive parser
associativity rules
,因为您最终可能会遇到以下表达式:
val = (2-(2+4+(3-2)))/(2+1)*(2-1)
并且您的解析器需要知道:
你可以想象,写一个(好的)解析器是一门艺术。好消息是有几个工具,称为parser generators
,可以让您轻松定义语言的语法,以及解析规则。您可能需要检查维基百科中 BNF 的条目,以便了解如何定义语法。
最后,如果你这样做是为了学习经验,请继续。如果这是用于生产代码,请不要重新发明轮子,并找到现有的库,否则您可能会花费1000行代码来添加2 + 2。
答案 8 :(得分:1)
基本上,你问我们如何写一个“解析器”。这是关于解析器的另一个Stack Overflow问题:hand coding a parser
答案 9 :(得分:1)
我做了类似你描述的事情。我使用递归来解析所有括号。然后我使用三元树来表示不同的段。左侧分支是操作员的左侧。中心分支是运营商。右分支是运营商的右侧。
简短回答递归和三元树。
答案 10 :(得分:1)
总有一个选项可以使用数学解析器库,例如mXparser。你可以:
1 - 检查表达式语法
import org.mariuszgromada.math.mxparser.*;
...
...
Expression e = new Expression("2+3-");
e.checkSyntax();
mXparser.consolePrintln(e.getErrorMessage());
结果:
[mXparser-v.4.0.0] [2+3-] checking ...
[2+3-] lexical error
Encountered "<EOF>" at line 1, column 4.
Was expecting one of:
"(" ...
"+" ...
"-" ...
<UNIT> ...
"~" ...
"@~" ...
<NUMBER_CONSTANT> ...
<IDENTIFIER> ...
<FUNCTION> ...
"[" ...
[2+3-] errors were found.
[mXparser-v.4.0.0]
2 - 评估表达
import org.mariuszgromada.math.mxparser.*;
...
...
Expression e = new Expression("2+3-(10+2)");
mXparser.consolePrintln(e.getExpressionString() + " = " + e.calculate());
结果:
[mXparser-v.4.0.0] 2+3-(10+2) = -7.0
3 - 使用内置函数常量,运算符等。
import org.mariuszgromada.math.mxparser.*;
...
...
Expression e = new Expression("sin(pi)+e");
mXparser.consolePrintln(e.getExpressionString() + " = " + e.calculate());
结果:
[mXparser-v.4.0.0] sin(pi)+e = 2.718281828459045
4 - 定义自己的函数,参数和常量
import org.mariuszgromada.math.mxparser.*;
...
...
Argument z = new Argument("z = 10");
Constant a = new Constant("b = 2");
Function p = new Function("p(a,h) = a*h/2");
Expression e = new Expression("p(10, 2)-z*b/2", p, z, a);
mXparser.consolePrintln(e.getExpressionString() + " = " + e.calculate());
结果:
[mXparser-v.4.0.0] p(10, 2)-z*b/2 = 0.0
5 - Tokenize表达式字符串并使用表达式标记
import org.mariuszgromada.math.mxparser.*;
...
...
Argument x = new Argument("x");
Argument y = new Argument("y");
Expression e = new Expression("2*sin(x)+(3/cos(y)-e^(sin(x)+y))+10", x, y);
mXparser.consolePrintTokens( e.getCopyOfInitialTokens() );
结果:
[mXparser-v.4.0.0] --------------------
[mXparser-v.4.0.0] | Expression tokens: |
[mXparser-v.4.0.0] ---------------------------------------------------------------------------------------------------------------
[mXparser-v.4.0.0] | TokenIdx | Token | KeyW | TokenId | TokenTypeId | TokenLevel | TokenValue | LooksLike |
[mXparser-v.4.0.0] ---------------------------------------------------------------------------------------------------------------
[mXparser-v.4.0.0] | 0 | 2 | _num_ | 1 | 0 | 0 | 2.0 | |
[mXparser-v.4.0.0] | 1 | * | * | 3 | 1 | 0 | NaN | |
[mXparser-v.4.0.0] | 2 | sin | sin | 1 | 4 | 1 | NaN | |
[mXparser-v.4.0.0] | 3 | ( | ( | 1 | 20 | 2 | NaN | |
[mXparser-v.4.0.0] | 4 | x | x | 0 | 101 | 2 | NaN | |
[mXparser-v.4.0.0] | 5 | ) | ) | 2 | 20 | 2 | NaN | |
[mXparser-v.4.0.0] | 6 | + | + | 1 | 1 | 0 | NaN | |
[mXparser-v.4.0.0] | 7 | ( | ( | 1 | 20 | 1 | NaN | |
[mXparser-v.4.0.0] | 8 | 3 | _num_ | 1 | 0 | 1 | 3.0 | |
[mXparser-v.4.0.0] | 9 | / | / | 4 | 1 | 1 | NaN | |
[mXparser-v.4.0.0] | 10 | cos | cos | 2 | 4 | 2 | NaN | |
[mXparser-v.4.0.0] | 11 | ( | ( | 1 | 20 | 3 | NaN | |
[mXparser-v.4.0.0] | 12 | y | y | 1 | 101 | 3 | NaN | |
[mXparser-v.4.0.0] | 13 | ) | ) | 2 | 20 | 3 | NaN | |
[mXparser-v.4.0.0] | 14 | - | - | 2 | 1 | 1 | NaN | |
[mXparser-v.4.0.0] | 15 | e | e | 2 | 9 | 1 | NaN | |
[mXparser-v.4.0.0] | 16 | ^ | ^ | 5 | 1 | 1 | NaN | |
[mXparser-v.4.0.0] | 17 | ( | ( | 1 | 20 | 2 | NaN | |
[mXparser-v.4.0.0] | 18 | sin | sin | 1 | 4 | 3 | NaN | |
[mXparser-v.4.0.0] | 19 | ( | ( | 1 | 20 | 4 | NaN | |
[mXparser-v.4.0.0] | 20 | x | x | 0 | 101 | 4 | NaN | |
[mXparser-v.4.0.0] | 21 | ) | ) | 2 | 20 | 4 | NaN | |
[mXparser-v.4.0.0] | 22 | + | + | 1 | 1 | 2 | NaN | |
[mXparser-v.4.0.0] | 23 | y | y | 1 | 101 | 2 | NaN | |
[mXparser-v.4.0.0] | 24 | ) | ) | 2 | 20 | 2 | NaN | |
[mXparser-v.4.0.0] | 25 | ) | ) | 2 | 20 | 1 | NaN | |
[mXparser-v.4.0.0] | 26 | + | + | 1 | 1 | 0 | NaN | |
[mXparser-v.4.0.0] | 27 | 10 | _num_ | 1 | 0 | 0 | 10.0 | |
[mXparser-v.4.0.0] ---------------------------------------------------------------------------------------------------------------
6 - 您可以在 mXparser tutorial,mXparser math collection和mXparser API definition中找到更多内容。
7 - mXparser支持:
祝你好运
答案 11 :(得分:1)
对于任何在发表这篇文章的九年后才看到这个问题的人:如果您不想重新发明轮子,那么那里就有许多奇特的数学解析器。
我几年前用Java写过一篇文章,它支持算术运算,方程求解,微积分,积分微积分,基本统计量,函数/公式定义,图形等。
它叫ParserNG,它是免费的。
计算表达式非常简单:
MathExpression expr = new MathExpression("(34+32)-44/(8+9(3+2))-22");
System.out.println("result: " + expr.solve());
result: 43.16981132075472
或者使用变量并计算简单表达式:
MathExpression expr = new MathExpression("r=3;P=2*pi*r;");
System.out.println("result: " + expr.getValue("P"));
或使用功能:
MathExpression expr = new MathExpression("f(x)=39*sin(x^2)+x^3*cos(x);f(3)");
System.out.println("result: " + expr.solve());
result: -10.65717648378352
或者在给定点上评估导数(请注意,它在幕后进行了符号微分(不是数字),因此精度不受数值近似误差的限制):
MathExpression expr = new MathExpression("f(x)=x^3*ln(x); diff(f,3,1)");
System.out.println("result: " + expr.solve());
result: 38.66253179403897
在x = 3时一次区分 x^3 * ln(x)
。
您现在可以区分的次数是1。
或用于数值积分:
MathExpression expr = new MathExpression("f(x)=2*x; intg(f,1,3)");
System.out.println("result: " + expr.solve());
result: 7.999999999998261... approx: 8
此解析器非常快,并且具有许多其他功能。
免责声明:ParserNG是我创作的。
答案 12 :(得分:0)
取自 here [但是,我已经向其中添加了 Division(/) 功能]
<html>
<body>
<h1>how to write a parser - part2 </h1>
Expression<input id='expression'>
Result<input id='result'>
<button onclick="parse()">PARSE</button>
</body>
<script>
// split expression by operator considering parentheses
const split = (expression, operator) => {
const result = [];
let braces = 0;
let currentChunk = "";
for (let i = 0; i < expression.length; ++i) {
const curCh = expression[i];
if (curCh == '(') {
braces++;
} else if (curCh == ')') {
braces--;
}
if (braces == 0 && operator == curCh) {
result.push(currentChunk);
currentChunk = "";
} else currentChunk += curCh;
}
if (currentChunk != "") {
result.push(currentChunk);
}
return result;
};
// Division
const parseDivisionSeparatedExpression = (expression) => {
const numbersString = split(expression, '/');
const numbers = numbersString.map(noStr => {
if (noStr[0] == '(') {
const expr = noStr.substr(1, noStr.length - 2);
// recursive call to the main function
return parsePlusSeparatedExpression(expr);
}
return noStr;
});
const initialValue = 1.0;
const result = numbers.reduce((acc, no) => {
return acc / no
});
return result;
};
var res = (12 - 5-(5/2 + (32 + 4)) + (3*20))/2//12-5-38.5+60
// this will only take strings containing * operator [ no + ]
const parseMultiplicationSeparatedExpression = (expression) => {
const numbersString = split(expression, '*');
const numbers = numbersString.map(noStr => parseDivisionSeparatedExpression(noStr));
const initialValue = 1.0;
console.log("parseMultiplicationSeparatedExpression - numbers: ", numbers)
const result = numbers.reduce((acc, no) => acc * no, initialValue);
return result;
};
// both * -
const parseMinusSeparatedExpression = (expression) => {
const numbersString = split(expression, '-');
const numbers = numbersString.map(noStr => parseMultiplicationSeparatedExpression(noStr));
const initialValue = numbers[0];
const result = numbers.slice(1).reduce((acc, no) => acc - no, initialValue);
return result;
};
// * - +
const parsePlusSeparatedExpression = (expression) => {
const numbersString = split(expression, '+');
const numbers = numbersString.map(noStr => parseMinusSeparatedExpression(noStr));
const initialValue = 0.0;
const result = numbers.reduce((acc, no) => acc + no, initialValue);
return result;
};
const parse = () => {
const expressionNode = document.getElementById('expression');
const resultNode = document.getElementById('result');
var expression = expressionNode.value;
expression = expression.replace(/ +/g, '')
console.log("parse - expression: ", expression)
const result = parsePlusSeparatedExpression(expression, '+');
resultNode.value = String(result);
};
</script>
示例: 12 * 5+(5 * (32 - 4)) + 3 = 203