编写一个“求解”方程的函数

问题描述 投票:0回答:7

我想编写一个函数,它可以让我在 js 中“求解”方程。

我想要什么(不是用编程语言):

function f(x) { 1 + x * x }
var z = 2
var y = f(z)  //y will be 5 as a number

我用JS写的:

function P(cfg) { ....
this.equation = "1 + x";
....};
P.prototype.eqn = function(x) {
    var tmp = eval(this.equation);
    return tmp;
};
....
P.prototype.draw = function() {....
for(var i = 0; i < z; i++)
    ctx.lineTo(i, this.eqn(i));
....};

我还读到,在循环中使用 eval 可能不是一个好主意,但我还没有找到另一种方法(JS初学者)...

这段代码的问题是,至少在 FF 中 var tmp 仍然包含 this.equation 中的字符串而不是计算值。

非常感谢任何进一步的见解!

感谢您的宝贵时间:)

编辑:因为我的问题表述得不太好: 执行该行后 var tmp = eval(this.equation); var tmp 将保存一个等于字符串 this.equation 的 STRING,而不是所需的解 y 值。 另外,我的意思不是解决而是评估,谢谢你的提示:)

javascript math
7个回答
5
投票

根据你的例子,我想说你想要“评估一个表达式”,而不是“求解一个方程”。为了评估表达式,您可能可以找到许多教程。不过我会简单地分解一下。您需要执行几个步骤。

从字符串“1 + x * x”开始,您需要将其分解为标记。具体来说,分解为:

"1", "+", "x", "*", "x"
。此时,您可以将变量(“x”)替换为其文字值(“2”),从而获得
"1", "+", "2", "*", "2"

现在您需要解析表达式。基于 PEMDAS 操作顺序,您需要创建一个树形数据结构,其中首先执行括号子句(括号包围的内容),然后执行乘法和除法,最后执行加法和减法。解析通常不是一件容易的任务,您可能想要组合一个更简单的 BNF 语法(尽管您可以通过一些谷歌搜索找到简单数学表达式的语法)。

接下来,深度优先遍历树,在树上向上评估操作。一旦你到达树顶,你就有了解决方案。

如果你想“解方程”,你将需要更复杂的东西,比如 Sage


3
投票

我之前使用过这个表达式计算器。看起来效果很好。它允许您将表达式传递到解析器中,该解析器返回一个函数对象,然后可以评估输入。

var expr = Parser.parse("2 ^ x");
expr.evaluate({ x: 3 }); // 8

它支持三角函数(sin、cos、ect...)和其他方便的内置函数,例如 abs 和 ciel。

var expr = Parser.parse("sin(x/2) + cos(x/2)")
expr.evaluate({x: Math.PI / 2}); // 1

示例:http://silentmatt.com/javascript-expression-evaluator/

代码:https://github.com/silentmatt/js-expression-eval

请注意,该库不使用 eval()。


2
投票

不确定我完全理解你的问题,但是怎么样:

var makeFunctionOfX = function(src) { 
    return Function('x', 'return ' + src); 
};

然后你可以这样说:

var g = makeFunctionOfX('2 * x')

var y = g(3); // y contains 6

eval
相比,它的最大优点是我们创建的
Function
没有神奇的能力来查看作用域中的变量(因此需要显式地将其
x
作为参数名称传递)。


2
投票
如果您信任用户的输入,那么使用

eval

 是安全的,并且工作得很好。 (我不知道你所说的“var tmp 仍然会有字符串 this.equation”是什么意思。) 
function FuncCreator(eqn){ this.eqn = eqn } FuncCreator.prototype.run = function(x,y,z){ return eval(this.eqn) } var add1 = new FuncCreator('1+x'); var result = add1.run(41); // 42 var complex = new FuncCreator('Math.sin(x*y) / Math.cos(z)'); var result = complex.run(3,4,5); // -1.891591285331882

如果您不信任用户输入,则需要自己实际解析输入并进行处理。这可不是小事。
    

您可以使用

2
投票
库中的表达式解析器并执行以下操作:

var parser = math.parser(); var f = parser.eval('function f(x) = 1 + x * x'); // use the created function f in expressions: parser.eval('z = 2'); // 2 parser.eval('y = f(z)'); // 5 // or use the created function f in JavaScript: var z = 2; // 2 var y = f(z); // 5

目前在 math.js 中创建函数非常有限,尚不支持定义更广泛函数所需的循环和块。
    

这是一个旧线程,但我写了这个方程计算器,但这并不能解决代数方程。然而,有一个函数可以让您提供一个包含指定变量的数组。但这并不能解决没有分配值的变量。

0
投票
我可能没有排列每个测试用例场景,但它似乎工作得相当不错。

编辑:必须修改它才能处理负数。除此之外...工作正常。

这是一把小提琴

<!doctype html> <html> <head> <title>Javascript Equation Calculator</title> </head> <body> <input type="button" onclick="main()" value="calculate"><br> <input type="text" id="userinput"><br> <span id="result">Ready.</span><br> <script> function Calculator(){} String.prototype.replaceLast = function (what, replacement) { var pcs = this.split(what); var lastPc = pcs.pop(); return pcs.join(what) + replacement + lastPc; }; function inS(substr, str){return (str.indexOf(substr) > -1);} function arrayValueOrToken(arr, key, token) { if(key in arr) { return arr[key]; } return token; } function reduceEquation(inputStr) { console.log("reduceEquation Executed-----"); while(hasNest(inputStr)) { if(hasNest(inputStr)) { inputStr = inputStr.replace(")(",')*('); for(var i=0;i<=9;i++) { inputStr = inputStr.replace(i+"(",i+'*('); inputStr = inputStr.replace(")"+i,')*'+i); } var s = inputStr.lastIndexOf("("); var e = 0; for(i=s;i,inputStr.length;i++){if(inputStr[i]==")"){e=i+1;break;}} var eq = inputStr.substring(s,e); var replace = eq; eq = eq.replace(/[()]/g, ''); var substitution = solveEquation(eq); inputStr = inputStr.replaceLast(replace,substitution); } } return inputStr; } function solveEquation(eq) { console.log("solveEquation Executed-----"); eq = doFirstOrder(eq); eq = doLastOrder(eq); return eq; } function doFirstOrder(eq) { console.log("doFirstOrder Executed-----"); for(var i=0;i<eq.length;i++) { if(eq[i]=="*"){eq = solve(eq,"*");return doFirstOrder(eq);} if(eq[i]=='/'){eq = solve(eq,'/');return doFirstOrder(eq);} } return eq; } function doLastOrder(eq) { console.log("doLastOrder Executed-----"); for(var i=0;i<eq.length;i++) { if(eq[i]=="+"){eq = solve(eq,"+");return doLastOrder(eq);} if(eq[i]=="-"){eq = solve(eq,"-");return doLastOrder(eq);} } return eq; } function solve(eq, operator) { var setOp = operator; console.log("solve Executed-----"); var buildEq = "",var1 = true,done = false,char=""; var operators = "+-/*"; var ops = operators.replace(operator, '').split(''); var a=ops[0]; var b=ops[1]; var c=ops[2]; for(var i=0;i<eq.length;i++) { char = eq[i]; switch(true) { case(char==operator):if(var1===true){var1 = false;}else{done = true;}break; case(char==a): case(char==b): case(char==c):if(var1){char = ""; buildEq = "";}else{done = true;} } if(done){break;} buildEq = buildEq + char; } var parts = parts = buildEq.split(operator); var solution = null; if(operator=="+"){solution = parseFloat(parts[0]) + parseFloat(parts[1]);} if(operator=="-"){solution = parseFloat(parts[0]) - parseFloat(parts[1]);} if(operator=="*"){solution = parseFloat(parts[0]) * parseFloat(parts[1]);} if(operator=="/"){solution = parseFloat(parts[0]) / parseFloat(parts[1]);} return eq.replace(buildEq, solution); } function hasNest(inputStr){return inS("(",inputStr);} function allNestsComplete(inputStr) { var oC = 0, cC = 0,char=""; for(var i=0;i<inputStr.length;i++){char = inputStr[i];if(char=="("){oC+=1;}if(char==")"){cC+=1;}} return (oC==cC); } Calculator.prototype.calc = function(inputStr) { console.log("Calc Executed-----"); inputStr = inputStr.replace(/ /g, ""); inputStr = inputStr.replace(/\\/g, '/'); inputStr = inputStr.replace(/x/g, "*") inputStr = inputStr.replace(/X/g, "*") if(!allNestsComplete(inputStr)){return "Nested operations not opened/closed properly.";} inputStr=reduceEquation(inputStr); inputStr = solveEquation(inputStr); return inputStr; }; Calculator.prototype.calcWithVars = function(inputList) { if(inputList.length < 2){return "One or more missing arguments!";} var vars = []; var assocVars = []; var lastVarIndex = inputList.length - 2; var i = 0; var inputStr = inputList[inputList.length-1]; for(i=0;i<=lastVarIndex;i++) { vars.push(inputList[i].replace(/ /g, "")); } for(i=0;i<=vars.length-1;i++) { var vParts = vars[i].split("="); var vName = vParts[0]; var vValue = vParts[1]; assocVars[vName] = vValue; } inputStr = inputStr.replace(/ /g, ""); var eqVars = inputStr.replace(/\s+/g, ' ').replace(/[^a-zA-Z-]/g, ' ').replace(/\s\s+/g, ' '); if(inS(" ", eqVars)) { eqVars = eqVars.split(" "); } else{eqVars = [eqVars];} eqVars.sort(function(a, b){return a.length - a.length;}); var tempTokens = []; var tempCount = 1; for(i=0;i<eqVars.length;i++) { var eqVname = eqVars[i]; var substitution = arrayValueOrToken(assocVars, eqVname, "<unknown>"); if(substitution != "<unknown>") { inputStr = inputStr.replace(eqVname,substitution); } else { var tempToken = "#______#"+tempCount+"#______#"; tempCount++; tempTokens.push(tempToken + "?" + eqVname); inputStr = inputStr.replace(eqVname,tempToken); } } for(i=0;i<tempTokens.length;i++) { var tokenSet = tempTokens[i]; var tokenParts = tokenSet.split("?"); var token = tokenParts[0]; var variableName = tokenParts[1]; inputStr = inputStr.replace(token,variableName); } var answerName = "<unknown>"; var eq = inputStr; if(inS("=", inputStr)) { var eqParts = inputStr.split("="); answerName = eqParts[0]; eq = eqParts[1]; } eq = this.calc(eq); var result = []; for(i=0;i<eqVars.length;i++) { var v = arrayValueOrToken(assocVars, eqVars[i], "<unknown>"); if(v != "<unknown>") { result.push(assocVars[eqVars[i]]); } } result.push(eq); return result; }; function main() { var calculator = new Calculator(); elUserInput = document.getElementById('userinput'); console.log("input: "+ elUserInput.value); elResult = document.getElementById('result'); equation = elUserInput.value; result = calculator.calc(equation); console.log("result: "+ result); elResult.innerHTML = result; } </script> </body> </html>

现在这是一个更旧的线程,但知道它仍然受到搜索引擎的关注(我是通过这种方式被带到这里的),这是我对答案的看法,因为我最近需要类似的东西,但在纯 JavaScript 中,没有任何额外的依赖项。我的要求是能够执行简单的公式评估(例如,我们在数据库中有一条记录,表示一个具有尺寸的矩形,我们想要计算它的面积)。

0
投票
/** * * @param {string} formula * @param {function(placeholder: string): number} [placeholderResolver] * @return number */ const evaluateFormula = (formula, placeholderResolver) => { const validOperandRegex = /\{[^}]+}|[a-zA-Z][a-zA-Z0-9._]*|\d+\.?\d*/; // Enclosed in {}, starting with a letter, or a number const multiplicationDivisionRegex = new RegExp('(' + validOperandRegex.source + ')' + String.raw`\s*(\*|\/)\s*` + '(' + validOperandRegex.source + ')'); const additionSubtractionRegex = new RegExp('(' + validOperandRegex.source + ')' + String.raw`\s*(\+|\-)\s*` + '(' + validOperandRegex.source + ')'); /** * * @param {string} formula * @param {function(placeholder: string): number} [placeholderResolver] * @return number */ const evaluateFormulaInner = (formula, placeholderResolver) => { if (!placeholderResolver) { placeholderResolver = x => Number(x); } // Solve parenthesised expressions first const originalFormula = formula.trim(); formula = formula.replace(/\(([^)]+)\)/g, (substring, subFormula) => { console.log(`Solving parens: ${subFormula}`); return evaluateFormulaInner(subFormula, placeholderResolver).toString(); }); if (formula !== originalFormula) { // There were parenthesis in the formula console.log(`Parens stripped: ${formula}`); return evaluateFormulaInner(formula, placeholderResolver); } // Solve multiplications and divisions // Note - only the first encountered expression is evaluated to avoid breaking the sequence of consecutive operations // e.g., 2 * 2 / 4 * 4 equals 4 / 4 * 4 (=4), not 4 / 16 (=1/4) formula = formula.replace(multiplicationDivisionRegex, (substring, operandA, operator, operandB) => { return evaluateSimpleEquation(operandA, operator, operandB, placeholderResolver); }); if (formula !== originalFormula) { // There were divisions or multiplications console.log(`Replaced divisions or multiplications: ${formula}`); return evaluateFormulaInner(formula, placeholderResolver); } // Solve additions and subtractions // Note - only the first encountered expression is evaluated to avoid breaking the sequence of consecutive operations // e.g., 2 + 2 - 4 + 4 equals 4 - 4 + 4 (=4), not 4 - 8 (=-4) formula = formula.replace(additionSubtractionRegex, (substring, operandA, operator, operandB) => { return evaluateSimpleEquation(operandA, operator, operandB, placeholderResolver); }); if (formula !== originalFormula) { // There were additions or subtractions console.log(`Replaced additions or subtractions: ${formula}`); return evaluateFormulaInner(formula, placeholderResolver); } // Finally, we can convert the answer to a number return evaluateOperandValue(formula, placeholderResolver); }; /** * * @param {string} operand * @param {function(placeholder: string): number} placeholderResolver */ const evaluateOperandValue = (operand, placeholderResolver) => { operand = operand.trim(); if (operand.match(/^\{[^}]+}$/)) { return placeholderResolver(operand.slice(1, -1)); } if (operand.match(/^[a-z][a-z0-9._]*$/)) { return placeholderResolver(operand); } const result = Number(operand); if (isNaN(result)) { throw new Error('Invalid operand: ' + operand); } return result; } /** * * @param {string} operandA * @param {'*'|'/'|'+'|'-'} operator * @param {string} operandB * @param {function(placeholder: string): number} placeholderResolver */ const evaluateSimpleEquation = (operandA, operator, operandB, placeholderResolver) => { const operandANumber = evaluateOperandValue(operandA, placeholderResolver); const operandBNumber = evaluateOperandValue(operandB, placeholderResolver); switch (operator) { case "*": return operandANumber * operandBNumber; case "+": return operandANumber + operandBNumber; case "-": return operandANumber - operandBNumber; case "/": return operandANumber / operandBNumber; } throw new Error('Invalid operator: ' + operator); }; return evaluateFormulaInner(formula, placeholderResolver) };

该函数可以解决像OP这样的简单问题,但也可以解决需要一定灵活性的问题,比如我的问题:
const rectangles = [
    {width: 10, height: 8},
    {width: 4, height: 4},
    {width: 3, height: 8},
];

rectangles.forEach(rectangle => {
    console.log(`Rectangle area (${rectangle.width} x ${rectangle.height}) is ${evaluateFormula('width * height', (dimension) => rectangle[dimension])}`);
});

结果:
Rectangle area (10 x 8) is 80
Rectangle area (4 x 4) is 16
Rectangle area (3 x 8) is 24


© www.soinside.com 2019 - 2024. All rights reserved.