2013-05-08 11 views
6

我想寫一個函數讓我「解」一個方程在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值。 此外,我的意思並不是解決,但評價,感謝您的提示:)

+4

非瑣碎的問題。我建議你查找infix和postfix符號併爲它編寫一個解析器。這將大大簡化您的問題。 – 2013-05-08 19:37:28

+0

如何讓JavaScript將文字和字母識別爲「變量符號」?像matlab中的'sym'?你幾乎要依靠這個。 – Shark 2013-05-08 19:38:45

+0

而不是在一個循環中調用'eval',你可以通過'new Function('x','return 1 + x;')來運行它;' - 這隻需要字符串被評估一次,返回一個函數然後您可以將'x'的值傳遞給。 – 2016-08-11 19:49:52

回答

4

根據你的例子,我會說你想「評估一個表達式」,而不是「解決方程式」。爲了評估一個表達式,你可能會發現many tutorials。儘管我會簡短地分解它。你需要做幾個步驟。

從字符串「1 + x * x」開始,您需要將其分解爲令牌。具體來說,將其分解爲:"1", "+", "x", "*", "x"。在這一點上,你可以用你的變量(「x」)替換它們的文字值(「2」),給你"1", "+", "2", "*", "2"

現在你需要解析表達式。根據order of operations PEMDAS,您需要創建一個樹型數據結構,其中括號內的子句(括號內包圍的東西)首先被執行,接下來是乘法和除法,然後是加法和減法。解析通常不是一件容易的事,你可能想要把一個更簡單的BNF語法放在一起(儘管你可能會發現一些簡單的數學表達式的語法和一些谷歌搜索)。

接下來,走樹,先深入,然後評估樹上的操作。一旦你到達樹頂,你就有了解決方案。

相反,如果你想「解決一個公式」,你會需要一些更復雜的,像Sage

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

+0

請注意,這不比使用'eval'更安全。 – Phrogz 2013-05-08 19:44:49

+0

@Progro - 請參閱我剛添加的部分。 – 2013-05-08 19:45:48

+1

它可能無法訪問某些閉包值,但仍可訪問全局全局範圍。所有與調用'eval'相關的問題都出現在這個解決方案中。 (但是,+ 1,因爲它比我的更優雅) – Phrogz 2013-05-08 19:47:18

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 

如果你不信任的用戶輸入,你需要真正分析輸入並自行處理。這是不平凡的。

+0

謝謝你的回答,我的看法是,如果我在FF中設置斷點並查看var tmp被賦予eval後所保留的值(...)它仍然保持字符串this.equation的值在tmp中=== this.equation // true 而且我不知道爲什麼... – replax 2013-05-08 19:56:06

3

我以前用過this expression evaluator。它似乎工作得很好。它允許您將表達式傳遞到解析器,該解析器返回一個可以評估輸入的函數對象。

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

它支持三角函數(正弦,餘弦,等...)及其他方便的內置這種功能的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

請注意,此lib中不使用eval()。

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

這是一個古老的線程,但我寫了這個方程計算器,但這並不解決代數方程。然而,有一個函數可以讓你提供一個包含分配變量的數組。但是這並不能解決沒有指定值的變量。

我可能沒有對每個測試用例場景進行置換,但它似乎工作得很體面。編輯:這將不得不被修改來處理負數。除此之外...工作正常。

Here is a fiddle

<!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> 
+0

有些評論會讓這個更容易理解。我的直覺告訴我你已經讓它變得過於複雜。 – Teepeemm 2016-08-12 18:02:06

+0

我會研究評論,這可能過於複雜,但對我來說,這是一個草根嘗試......沒有看到任何簡單的例子。我敢肯定,他們有些偏移或者我可以實施的東西。 – 2016-08-13 01:20:56

相關問題