如何修改我的调车场算法以使其接受一元运算符?
我一直致力于在课堂上用 JavaScript 实现调车场算法。
这是我到目前为止的工作:
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 程序,我还不习惯。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
最简单的事情是使
isNumber
匹配/-?[0-9]+(\.[0-9]+)?/
,处理浮点和负数。如果您确实需要处理一元运算符(例如,括号表达式的一元否定),那么您必须:
EvaluateExpression
中单独处理它们(创建一个单独的PerformUnaryExpression
函数,该函数只接受一个操作数)。我在另一个SO问题上写了更全面的解释来处理一元减号< /a>.
The easiest thing would be to make
isNumber
match/-?[0-9]+(\.[0-9]+)?/
, handling both floating points and negative numbers.If you really need to handle unary operators (say, unary negation of parenthesized expressions), then you have to:
EvaluateExpression
(make a separatePerformUnaryExpression
function which only takes one operand).InfixToPostfix
by keeping track of some kind of state. See how'-'
is turned into'-u'
in this Python example.I wrote up a more thorough explanation of handling unary minus on another SO question.
我的建议是这样的。不要将“-”作为算术运算符来处理。将其视为“符号”运算符。或者将其视为整个操作数的一部分(即其符号)。我的意思是,每次遇到“-”时,只需将其后面的操作数乘以-1,然后继续读取下一个标记。 :) 我希望有帮助。只是一个简单的想法...
my suggestion is this. don't handle the '-' as an arithmetic operator. treat it as a 'sign' operator. or treat it as if it's a part of the whole operand (i.e. its sign). what i mean is that everytime you encounter '-', you just have to multiply the operand after it by -1, then proceed to read the next token. :) i hope that helps. just a simple thought...
我可以通过修改一元运算符(“+”和“-”)以将它们与二进制运算符区分开来解决这个问题。
例如,我将一元减“m”和一元加“p”称为右结合,并且它们的优先级等于指数运算符(“^”)。
为了检测运算符是否是一元运算符,我只需检查运算符之前的标记是运算符还是左括号。
这是我在 C++ 中的实现:
这里运算符是一个堆栈,令牌是中缀表达式字符串的迭代器
(这只是运算符处理部分)
I could solve this problem by modifying unary operators('+' and '-') to distinguish them from the binary ones.
For example, I called the unary minus 'm' and unary plus 'p', making them right-assocative and their precedence equal to the exponent operator('^').
To detect if the operator is unary I simply had to check if the token before the operator was an operator or an opening bracket.
This is my implementation in C++:
Here operators is a stack and token is an iterator to the infix expression string
(This just the operator handling part)
当我需要支持这一点时,我在中间阶段这样做了。我首先生成所有表达式词位的列表,然后使用辅助函数来提取运算符和操作数,而“获取操作数”函数只要看到一元运算符就简单地消耗两个词位。
不过,如果您使用另一个字符来表示“一元减”,这确实很有帮助。
When I needed to support this, I did this in an intermediate stage. I started by generating a list of all expression lexemes, then used helper functions to extract operators and operands and the "get operand" function simply consumed two lexemes whenever it saw a unary operator.
It really helps if you use another character to signify "unary minus", though.
这不是用 Javascript 写的,但这是我在搜索后没有找到任何明确答案后专门为了解决这个问题而编写的一个库。
这可以满足您的所有需求,甚至更多:
https://marginalhacks.com/Hacks/libExpr.rb/< /a>
它是一个 ruby 库(以及用于检查它的测试平台),运行修改后的调车场算法,该算法还支持一元('-a')和三元('a?b:c')操作。它还可以执行 RPN、Prefix 和 AST(抽象语法树)——您可以选择,并且可以计算表达式,包括生成可以处理任何变量计算的块(某种 lambda)的能力。只有 AST 可以完成全套操作,包括处理短路操作的能力(例如“||”和“?:”等),但 RPN 确实支持一元。它还具有灵活的优先级模型,其中包括由 C 表达式或 Ruby 表达式(不相同)完成的优先级预设。测试平台本身很有趣,因为它可以创建随机表达式,然后可以使用 eval() 并通过 libExpr 运行来比较结果。
它有相当多的文档/评论,因此将这些想法转换为 Javascript 或其他语言应该不会太难。
就一元运算符而言,基本思想是您可以根据先前的标记来识别它们。如果前一个标记是运算符或左括号,则“一元可能”运算符(+ 和 -)只是一元,并且只能使用一个操作数进行推送。重要的是,您的 RPN 堆栈必须区分一元运算符和二元运算符,以便它知道在求值时要做什么。
This isn't in Javascript, but here is a library I wrote to specifically solve this problem after searching and not finding any clear answers.
This does all you want and more:
https://marginalhacks.com/Hacks/libExpr.rb/
It is a ruby library (as well as a testbench to check it) that runs a modified shunting yard algorithm that also supports unary ('-a') and ternary ('a?b:c') ops. It also does RPN, Prefix and AST (abstract syntax trees) - your choice, and can evaluate the expression, including the ability to yield to a block (a lambda of sorts) that can handle any variable evaluation. Only AST does the full set of operations, including the ability to handle short-circuit operations (such as '||' and '?:' and so on), but RPN does support unary. It also has a flexible precedence model that includes presets for precedence as done by C expressions or by Ruby expressions (not the same). The testbench itself is interesting as it can create random expressions which it can then eval() and also run through libExpr to compare results.
It's fairly documented/commented, so it shouldn't be too hard to convert the ideas to Javascript or some other language.
The basic idea as far as unary operators is that you can recognize them based on the previous token. If the previous token is either an operator or a left-paren, then the "unary-possible" operators (+ and -) are just unary and can be pushed with only one operand. It's important that your RPN stack distinguishes between the unary operator and the binary operator so it knows what to do on evaluation.
在我的 Java 实现中,我采用了以下方式:
In my Java implementation I did it in next way:
要处理浮点数,您可以将正则表达式(的数字部分)更改为:
因此最终的正则表达式将是:
为了处理一元减号运算符,您可以将其更改为另一个字符,例如“u”。
(正如这里所解释的 -TGO)
我根据给定的链接编写的用于处理一元减运算符的 JavaScript 代码是:
To handle floating point numbers you can change your (number part of) regex to:
so the final regex would be:
And for handling unary minus operator, you can change it to another character like 'u'.
(As it is explained here by -TGO)
The javascript code that i wrote for handling unary minus operator based on the link given is: