我一直在努力在JavaScript中实现Shunting-Yard算法。
到目前为止,这是我的工作:
var userInput = prompt("Enter in a mathematical expression:");
var postFix = InfixToPostfix(userInput);
var result = EvaluateExpression(postFix);
document.write("Infix: " + userInput + "<br/>");
document.write("Postfix (RPN): " + postFix + "<br/>");
document.write("Result: " + result + "<br/>");
function EvaluateExpression(expression)
{
var tokens = expression.split(/([0-9]+|[*+-\/()])/);
var evalStack = [];
while (tokens.length != 0)
{
var currentToken = tokens.shift();
if (isNumber(currentToken))
{
evalStack.push(currentToken);
}
else if (isOperator(currentToken))
{
var operand1 = evalStack.pop();
var operand2 = evalStack.pop();
var result = PerformOperation(parseInt(operand1), parseInt(operand2), currentToken);
evalStack.push(result);
}
}
return evalStack.pop();
}
function PerformOperation(operand1, operand2, operator)
{
switch(operator)
{
case '+':
return operand1 + operand2;
case '-':
return operand1 - operand2;
case '*':
return operand1 * operand2;
case '/':
return operand1 / operand2;
default:
return;
}
}
function InfixToPostfix(expression)
{
var tokens = expression.split(/([0-9]+|[*+-\/()])/);
var outputQueue = [];
var operatorStack = [];
while (tokens.length != 0)
{
var currentToken = tokens.shift();
if (isNumber(currentToken))
{
outputQueue.push(currentToken);
}
else if (isOperator(currentToken))
{
while ((getAssociativity(currentToken) == 'left' &&
getPrecedence(currentToken) <= getPrecedence(operatorStack[operatorStack.length-1])) ||
(getAssociativity(currentToken) == 'right' &&
getPrecedence(currentToken) < getPrecedence(operatorStack[operatorStack.length-1])))
{
outputQueue.push(operatorStack.pop())
}
operatorStack.push(currentToken);
}
else if (currentToken == '(')
{
operatorStack.push(currentToken);
}
else if (currentToken == ')')
{
while (operatorStack[operatorStack.length-1] != '(')
{
if (operatorStack.length == 0)
throw("Parenthesis balancing error! Shame on you!");
outputQueue.push(operatorStack.pop());
}
operatorStack.pop();
}
}
while (operatorStack.length != 0)
{
if (!operatorStack[operatorStack.length-1].match(/([()])/))
outputQueue.push(operatorStack.pop());
else
throw("Parenthesis balancing error! Shame on you!");
}
return outputQueue.join(" ");
}
function isOperator(token)
{
if (!token.match(/([*+-\/])/))
return false;
else
return true;
}
function isNumber(token)
{
if (!token.match(/([0-9]+)/))
return false;
else
return true;
}
function getPrecedence(token)
{
switch (token)
{
case '^':
return 9;
case '*':
case '/':
case '%':
return 8;
case '+':
case '-':
return 6;
default:
return -1;
}
}
function getAssociativity(token)
{
switch(token)
{
case '+':
case '-':
case '*':
case '/':
return 'left';
case '^':
return 'right';
}
}
到目前为止一切正常。如果我给它:
((5 + 3)* 8)
将输出:
中缀:((5 + 3)* 8)
后缀(RPN):5 3 + 8 *
结果:64
但是,我正在努力实现一元运算符,所以我可以做类似的事情:
(( -5 +3)* 8)
实施一元运算符(否定等)的最佳方法是什么?另外,有没有人有任何处理浮点数的建议?
最后一件事,如果有人看到我在JavaScript中做任何奇怪的事情让我知道。这是我的第一个JavaScript程序,我还不习惯它。
答案 0 :(得分:11)
最简单的方法是使isNumber
匹配/-?[0-9]+(\.[0-9]+)?/
,同时处理浮点和负数。
如果你真的需要处理一元运算符(比如,对括号表达式进行一元否定),那么你必须:
EvaluateExpression
中单独处理它们(创建一个单独的PerformUnaryExpression
函数,只需要一个操作数)。InfixToPostfix
中的一元和二元减号。在此Python example中查看'-'
如何变为'-u'
。我写了一个关于在another SO question上处理一元减号的更全面的解释。
答案 1 :(得分:4)
我的建议是这样的。不要将' - '作为算术运算符处理。将其视为“标志”运营商。或者把它看作是整个操作数的一部分(即它的符号)。我的意思是每当你遇到' - '时,你只需要将操作数乘以-1,然后继续读取下一个标记。 :)我希望有所帮助。只是一个简单的想法...
答案 2 :(得分:2)
我可以通过修改一元运算符(&#39; +&#39;和&#39; - &#39;)来区分二元运算符来解决这个问题。
例如,我打电话给一元减去&#39; m&#39;和一元加上&#39; +&#39;,使它们成为正对称的,它们的优先级等于指数运算符(&#39; ^&#39;)。
要检测操作员是否是一元的,我只需要检查操作员之前的令牌是操作员还是左括号。
这是我在C ++中的实现:
if (isOperator(*token))
{
if (!isdigit(*(token - 1)) && *(token - 1) != ')') // Unary check
{
if (*token == '+')
*token = 'p'; // To distinguish from the binary ones
else if (*token == '-')
*token = 'm';
else
throw;
}
short prec = precedence(*token);
bool rightAssociative = (*token == '^' || *token == 'm' || *token == 'p');
if (!operators.empty())
{
while (prec < precedence(operators.top()) || (prec == precedence(operators.top()) && !rightAssociative))
{
rpn += operators.top();
operators.pop();
if (operators.empty())
break;
}
}
operators.push(*token);
}
这里的运算符是一个堆栈,而令牌是中缀表达式字符串
的迭代器(这只是操作员处理部分)
答案 3 :(得分:1)
当我需要支持这个时,我在中间阶段做了这个。我开始生成所有表达式词条的列表,然后使用辅助函数来提取运算符和操作数,并且“get operand”函数只要看到一元运算符就会消耗两个lexemes。
但是,如果你使用另一个角色来表示“一元减号”,这确实很有帮助。
答案 4 :(得分:1)
在我的Java实现中,我是以下一种方式完成的:
expression = expression.replace(" ", "").replace("(-", "(0-")
.replace(",-", ",0-");
if (expression.charAt(0) == '-') {
expression = "0" + expression;
}
答案 5 :(得分:0)
要处理浮点数,可以将正则表达式的(部分数字)更改为:
/([0-9]+\.?[0-9]*)/
因此最终的正则表达式为:
/([0-9]+\.?[0-9]*|[*+-\/()])/
要处理一元减号运算符,可以将其更改为另一个字符,例如“ u”。 (As it is explained here--TGO)
我根据给定的链接编写的用于处理一元减号的javascript代码是:
// expr is the given infix expression from which, all the white spaces has been
// removed.(trailing and leading and in between white space characters)
const operators = ['+', '*', '-', '/', '^'];
const openingBrackets = ['(', '[', '{'];
let exprArr = Array.from(expr);
// Since strings are immutable in js, I am converting it to an array for changing
// unary minus to 'u'
for (let i = 0; i < expr.length; i++) {
if (expr[i] === '-') {
if (i === 0) {
exprArr[i] = 'u';
} else if (operators.includes(expr[i - 1])) {
exprArr[i] = 'u';
} else if (openingBrackets.includes(expr[i - 1])) {
exprArr[i] = 'u';
} else {
// '-' is not a unary operator
// it is a binary operator or we have the wrong expression, so...
if (!openingBrackets.includes(expr[i + 1]) && !/[0-9]/.test(expr[i + 1])) {
throw new Error("Invalid Expression...");
}
}
}
}
// And finally converting back to a string.
let expr2 = exprArr.join('');