给定一个字符串:
var str1 = "25*5+5*7";
如果不使用JavaScript中的eval
或构造函数,如何编写一个名为“output”的函数来接受字符串并输出该字符串的算术值,即在本例中为160?
给定一个字符串:
var str1 = "25*5+5*7";
如果不使用JavaScript中的eval
或构造函数,如何编写一个名为“output”的函数来接受字符串并输出该字符串的算术值,即在本例中为160?
这是一个完整的优先级表达式求值器,遵循我在对题目的评论中链接到的递归解析思想。
为了实现这个功能,首先我为我想要处理的表达式编写了一个简单的BNF语法:
sum = product | sum "+" product | sum "-" product ;
product = term | product "*" term | product "/" term ;
term = "-" term | "(" sum ")" | number ;
这本身需要一些经验,才能简单明了地完成。如果您没有BNF的经验,您会发现它非常有用,可以描述复杂的项目流,例如表达式、消息、编程语言等。
使用该语法,我按照其他消息中概述的过程编写了以下代码。显然它是由语法驱动的愚蠢机械方式,因此如果您具有该语法,则编写起来相当容易。
(未经测试。我不是JavaScript编码器。这肯定会包含一些语法/语义小问题。编码大约花费了我15分钟。)
var SE="Syntax Error";
function parse(str) { // returns integer expression result or SE
var text=str;
var scan=1;
return parse_sum();
function parse_sum() {
var number, number2;
if (number=parse_product()==SE) return SE;
while (true) {
skip_blanks();
if (match("+") {
number2=parse_product();
if (number2==SE) return SE;
number+=number2;
}
else if (match('-')) {
{ number2=parse_product();
if (number2==SE) return SE;
number-=number2;
}
else return number;
}
}
function parse_product() {
var number, number2;
if (number=parse_number()==SE) return SE;
while (true) {
if (match("*") {
number2=parse_term();
if (number2==SE) return SE;
number*=number2;
}
else if (match('/')) {
number2=parse_term();
if (number2==SE) return SE;
number/=number2;
}
else return number;
}
}
function parse_term() {
var number;
skip_blanks();
if (match("(")) {
number=parse_sum();
if (number=SE) return SE;
skip_blanks();
if (!match(")") return SE;
}
else if match("-") {
number= - parse_term();
}
else if (number=parse_number()==SE) return SE;
return number;
}
function skip_blanks() {
while (match(" ")) { };
return;
}
function parse_number() {
number=0;
if (is_digit()) {
while (is_digit()) {}
return number;
}
else return SE;
}
var number;
function is_digit() { // following 2 lines are likely wrong in detail but not intent
if (text[scan]>="0" && text[scan]<="9") {
number=number*10+text[scan].toInt();
return true;
}
else return false;
}
function match(c) {
if (text[scan]==c)
{ scan++; return true }
else return false;
}
}
编写此类解析器/评估器非常简单。请参见我的SO回答,介绍如何构建解析器(它链接到如何构建评估器的内容)。
这是一个具有*优先级高于+的简单解析器。我尽可能地使它教育性强。如果您特别雄心勃勃,可以自行添加除法和减法,或者括号。
function parse(str) {
var signs = ["*", "+"]; // signs in the order in which they should be evaluated
var funcs = [multiply, add]; // the functions associated with the signs
var tokens = str.split(/\b/); // split the string into "tokens" (numbers or signs)
for (var round = 0; round < signs.length; round++) { // do this for every sign
document.write("tokens at this point: " + tokens.join(" ") + "<BR>");
for (var place = 0; place < tokens.length; place++) { // do this for every token
if (tokens[place] == signs[round]) { // a sign is found
var a = parseInt(tokens[place - 1]); // convert previous token to number
var b = parseInt(tokens[place + 1]); // convert next token to number
var result = funcs[round](a, b); // call the appropriate function
document.write("calculating: " + a + signs[round] + b + "=" + result + "<BR>");
tokens[place - 1] = result.toString(); // store the result as a string
tokens.splice(place--, 2); // delete obsolete tokens and back up one place
}
}
}
return tokens[0]; // at the end tokens[] has only one item: the result
function multiply(x, y) { // the functions which actually do the math
return x * y;
}
function add(x, y) { // the functions which actually do the math
return x + y;
}
}
var str = "25*5+5*7";
document.write("result: " + str + " = " + parse(str));
var str1= "25*5+5*7"
document.write(str1 + ' = ' + math.eval(str1));
// output: "25*5+5*7 = 160"
<script src="http://cdnjs.cloudflare.com/ajax/libs/mathjs/2.1.1/math.min.js"></script>
eval
,而是使用 math.js 的表达式解析器,math.eval(expr)
。 - Jos de Jongvar output = math.eval; var res = output("25*5+5*7")
。只是开个玩笑。无论如何,我的答案完全回答了问题。虽然问题本身有歧义。如果@user3145336的意思是“我怎样才能自己编写表达式解析器?”,那么你的答案是正确的。但是大多数情况下,这些问题的意思是“我怎样才能安全地评估表达式而不必使用不安全的eval
函数?”。那就是我的解释。在这种情况下,math.js的表达式解析器是完美的选择。 - Jos de Jong您可以创建新脚本:
function parse(str) {
var s = document.createElement('script');
s.text = "window.result = " + str;
document.body.appendChild(s); // Run script
document.body.removeChild(s); // Clean up
return result; // Return the result
}
document.body.innerHTML = parse("5*5+5*5");
或者使用事件处理程序内容属性:
function parse(str) {
var el = document.createElement('div');
el.setAttribute('onclick', "this.result = " + str);
el.onclick(); // Run script
return el.result; // Return the result
}
document.body.innerHTML = parse("5*5+5*5");
eval
一样邪恶,但更丑陋。因此,我不建议使用它们。eval
本身更严重,因为它具有与原始 eval
相同的潜在风险(可能会导致注入),而且还修改了DOM :)。 eval
并不邪恶,只是被误解了。 - Tiberiu C.eval
更糟糕,但它不是eval
,因此它满足问题的要求 :) - Oriol0.1 + 0.2 = 0.3
而不是 0.30000000000000004
)。import Decimal from 'decimal.js';
import { evaluate } from 'decimal.js-extensions-evaluate';
evaluate.extend(Decimal);
let result = Decimal.evaluate('0.1 + 0.2');
console.log(result); // '0.3'