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,而不是所需的解 y 值。另外,我的意思不是解决而是评估,感谢您的提示:)

4

6 回答 6

4

根据您的示例,我想说您要“评估表达式”,而不是“求解方程式”。对于评估表达式,您可能可以找到许多教程。不过,我会简要介绍一下。你需要做几个步骤。

从您的字符串“1 + x * x”开始,您需要将其分解为标记。具体来说,将其分解为:"1", "+", "x", "*", "x". 此时,您可以将变量(“x”)替换为它们的字面值(“2”),为您提供"1", "+", "2", "*", "2"

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

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

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

于 2013-05-08T19:49:43.800 回答
3

我以前使用过这个表达式求值器。它似乎工作得很好。它允许您将表达式传递给解析器,该解析器返回一个函数对象,然后可以评估输入。

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

它支持三角函数(sin、cos 等)和其他方便的内置函数,如 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()。

于 2013-05-08T19:47:09.090 回答
2

不确定我是否完全理解您的问题,但如何:

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

然后你可以这样说:

var g = makeFunctionOfX('2 * x')

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

这样做的最大优点evalFunction我们创建的没有神奇的能力来查看范围内的变量(因此需要显式地将其x作为参数名称传递)。

于 2013-05-08T19:42:17.217 回答
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

如果您不信任用户输入,则需要实际解析输入并自己处理。这是不平凡的。

于 2013-05-08T19:42:29.837 回答
2

您可以使用math.js库中的表达式解析器并执行以下操作:

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 中创建函数非常有限,尚不支持定义更广泛函数所需的循环和块。

于 2013-05-11T12:44:07.090 回答
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>
于 2016-08-11T19:46:56.180 回答