2013-05-08 16 views
6

Desidero scrivere una funzione che mi consenta di "risolvere" un'equazione in js.Scrittura di una funzione che "risolve" un'equazione

quello che voglio (non in un linguaggio di programmazione):

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

quello che ho scritto in 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)); 
....}; 

inoltre ho letto che l'uso di eval in un ciclo non è probabilmente una buona idea, ma non ho ancora trovato un modo (ancora) (principiante JS) ...

Il problema con questo codice è, che almeno in FF il var tmp ANCORA contiene la stringa da this.equation invece di il valore calcolato.

Gradirei qualsiasi ulteriore intuizione!

Grazie per il vostro tempo :)

EDIT: perché la mia domanda non è stata formulata molto bene: dopo l'esecuzione della linea var tmp = eval (this.equation); il var tmp manterrà una STRING che equivale alla stringa this.equation, invece del valore y della soluzione desiderata. Anche io non intendo risolvere, ma di valutare, grazie per questo suggerimento :)

+4

problema non banale. Ti suggerisco di cercare le notazioni infix e postfix e scrivere un parser per questo. Ciò semplificherà notevolmente il tuo problema. –

+0

Quanto è facile ottenere JavaScript per riconoscere letterali e lettere come 'simboli variabili'? come 'sym' in matlab? praticamente dipende da questo. – Shark

+0

Invece di chiamare 'eval' in un ciclo, puoi eseguirlo con' new Function ('x', 'return 1 + x;'); '- questo richiederà solo che la stringa venga valutata una volta, restituendo una funzione che puoi quindi passare valori per 'x' a. –

risposta

4

In base al tuo esempio, direi che vuoi "valutare un'espressione", piuttosto che "risolvere un'equazione". Per valutare un'espressione, probabilmente puoi trovare many tutorials. Lo romperò in breve però. Hai bisogno di fare qualche passo.

A partire dalla stringa "1 + x * x", è necessario suddividerla in token. Nello specifico, scomposizione in: "1", "+", "x", "*", "x". A questo punto, puoi sostituire le tue variabili ("x") per i valori letterali ("2"), dandoti "1", "+", "2", "*", "2"

Ora devi analizzare l'espressione. Sulla base di order of operations PEMDAS è necessario creare una struttura dati ad albero, in cui le clausole parentetiche (elementi circondati da parentesi) vengono eseguite per prime, moltiplicazione e divisione successiva, quindi le aggiunte e la sottrazione per ultime. L'analisi non è spesso un compito facile, e potresti voler mettere insieme una grammatica BNF più semplice (anche se probabilmente puoi trovare una grammatica per semplici espressioni matematiche con alcuni googling).

Successivamente, cammina l'albero, prima la profondità, valutando le operazioni mentre sali sull'albero. Una volta arrivati ​​in cima all'albero, hai la soluzione.

Se invece si vuole "risolvere un'equazione", si sta andando ad avere bisogno di qualcosa di molto più sofisticato, come Sage

2

Non sono sicuro io del tutto capito la tua domanda, ma come circa:

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

Allora si può dire le cose come:

var g = makeFunctionOfX('2 * x') 

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

Il grande vantaggio di questo oltre eval è che il Function che creiamo non ha alcuna capacità magica di vedere le variabili nell'ambito (da qui la necessità di passarlo esplicitamente a x come nome parametro).

+0

Si noti che questo non è più sicuro dell'uso di 'eval'. – Phrogz

+0

@Phrogz - guarda la parte che ho appena aggiunto. –

+1

Potrebbe non avere accesso a determinati valori di chiusura, ma ha ancora accesso all'intero ambito globale. Tutti i problemi associati alla chiamata di "eval" sono presenti con questa soluzione. (Ma, +1, perché è più elegante del mio.) – Phrogz

2

L'utilizzo di eval è sicuro se ci si fida dell'input dell'utente e funziona perfettamente. (Non ho idea di cosa si intende per "il var tmp avrà ancora la this.equation stringa".)

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 

Se non vi fidate l'input dell'utente, avrete bisogno di analizzare in realtà l'input e processalo tu stesso. Questo non è banale.

+0

Grazie per la tua risposta, cosa direi, se impostassi i breakpoint in FF e guardassi il valore che var tmp tiene dopo che è stato asserito per valutare (...) STILL mantiene il valore della stringa this.equation come in tmp === this.equation // true E non ho idea del perché ... – replax

3

Ho usato prima this expression evaluator. Sembrava funzionare molto bene. Permette di passare le espressioni in un parser che restituisce un oggetto funzione che può quindi valutare gli input.

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

Supporta le funzioni trigonometriche (sin, cos, ect ...) e altri a portata di mano costruito nelle funzioni come ABS & ciel.

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

Esempi: http://silentmatt.com/javascript-expression-evaluator/

Codice: https://github.com/silentmatt/js-expression-eval

Si noti che questo lib non fa uso di eval().

2

È possibile utilizzare il parser di espressioni dalla libreria math.js e fare qualcosa di simile:

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 

La creazione di funzioni in math.js è al momento limitata, i loop e i blocchi necessari per definire funzioni più estese non sono ancora supportati.

0

Questo è un thread vecchio, ma ho scritto questo calcolatore di equazioni, questo però non risolve le equazioni algebriche. Esiste comunque una funzione che ti consentirà di fornire un array contenente variabili assegnate. Ma questo non risolve le variabili che non hanno un valore assegnato.

Probabilmente non ho permutato ogni scenario di test case, ma sembra funzionare abbastanza bene.

Modifica: Questo dovrebbe essere modificato per gestire i numeri negativi. A parte questo ... funziona bene.

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

Alcuni commenti farebbero molto per renderlo più comprensibile. Il mio istinto mi dice che l'hai reso eccessivamente complicato. – Teepeemm

+0

Elaborerò i commenti, ed è probabilmente eccessivamente complicato, ma poi di nuovo, per me questo è stato un tentativo di base ... Non ho visto nessun esempio semplice là fuori. Sono sicuro che ci sia un po 'di bitshifting o qualcosa che avrei potuto implementare. –

Problemi correlati