我想编写一个函数,它可以让我在 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 值。 另外,我的意思不是解决而是评估,谢谢你的提示:)
根据你的例子,我想说你想要“评估一个表达式”,而不是“求解一个方程”。为了评估表达式,您可能可以找到许多教程。不过我会简单地分解一下。您需要执行几个步骤。
从字符串“1 + x * x”开始,您需要将其分解为标记。具体来说,分解为:
"1", "+", "x", "*", "x"
。此时,您可以将变量(“x”)替换为其文字值(“2”),从而获得 "1", "+", "2", "*", "2"
现在您需要解析表达式。基于 PEMDAS 操作顺序,您需要创建一个树形数据结构,其中首先执行括号子句(括号包围的内容),然后执行乘法和除法,最后执行加法和减法。解析通常不是一件容易的任务,您可能想要组合一个更简单的 BNF 语法(尽管您可以通过一些谷歌搜索找到简单数学表达式的语法)。
接下来,深度优先遍历树,在树上向上评估操作。一旦你到达树顶,你就有了解决方案。
如果你想“解方程”,你将需要更复杂的东西,比如 Sage
我之前使用过这个表达式计算器。看起来效果很好。它允许您将表达式传递到解析器中,该解析器返回一个函数对象,然后可以评估输入。
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()。
不确定我完全理解你的问题,但是怎么样:
var makeFunctionOfX = function(src) {
return Function('x', 'return ' + src);
};
然后你可以这样说:
var g = makeFunctionOfX('2 * x')
var y = g(3); // y contains 6
与
eval
相比,它的最大优点是我们创建的 Function
没有神奇的能力来查看作用域中的变量(因此需要显式地将其 x
作为参数名称传递)。
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
如果您不信任用户输入,则需要自己实际解析输入并进行处理。这可不是小事。您可以使用
这是一个旧线程,但我写了这个方程计算器,但这并不能解决代数方程。然而,有一个函数可以让您提供一个包含指定变量的数组。但这并不能解决没有分配值的变量。
/**
*
* @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