Jak przeanalizować i ocenić wyrażenie matematyczne w ciągu (np. '1+1'
) Bez wywoływania w eval(string)
celu uzyskania jego wartości liczbowej?
W tym przykładzie chcę, aby funkcja akceptowała '1+1'
i zwracała 2
.
javascript
string
math
numbers
wheresrhys
źródło
źródło
(Function("return 1+1;"))()
.Odpowiedzi:
Możesz użyć biblioteki JavaScript Expression Evaluator , która umożliwia wykonywanie takich czynności jak:
Parser.evaluate("2 ^ x", { x: 3 });
Lub matematyka , która pozwala na takie rzeczy, jak:
math.eval('sin(45 deg) ^ 2');
W końcu wybrałem matematykę do jednego z moich projektów.
źródło
Możesz łatwo + lub -:
function addbits(s) { var total = 0, s = s.match(/[+\-]*(\.\d+|\d+(\.\d+)?)/g) || []; while (s.length) { total += parseFloat(s.shift()); } return total; } var string = '1+23+4+5-30'; console.log( addbits(string) )
Bardziej skomplikowana matematyka sprawia, że eval jest bardziej atrakcyjny - i na pewno łatwiejszy do napisania.
źródło
Ktoś musi przeanalizować ten ciąg. Jeśli nie jest to interpreter (via
eval
), to będziesz musiał to być ty, pisząc procedurę parsowania w celu wyodrębnienia liczb, operatorów i wszystkiego, co chcesz obsługiwać w wyrażeniu matematycznym.Więc nie, nie ma żadnego (prostego) sposobu bez
eval
. Jeśli obawiasz się o bezpieczeństwo (ponieważ dane wejściowe, które analizujesz, nie pochodzą ze źródła, które kontrolujesz), może możesz sprawdzić format danych wejściowych (za pomocą filtru regex z białej listy) przed przekazaniem go doeval
?źródło
Alternatywa dla doskonałej odpowiedzi @kennebec, wykorzystująca krótsze wyrażenie regularne i dopuszczająca spacje między operatorami
function addbits(s) { var total = 0; s = s.replace(/\s/g, '').match(/[+\-]?([0-9\.\s]+)/g) || []; while(s.length) total += parseFloat(s.shift()); return total; }
Użyj tego jak
addbits('5 + 30 - 25.1 + 11');
Aktualizacja
Oto bardziej zoptymalizowana wersja
function addbits(s) { return (s.replace(/\s/g, '').match(/[+\-]?([0-9\.]+)/g) || []) .reduce(function(sum, value) { return parseFloat(sum) + parseFloat(value); }); }
źródło
Stworzyłem BigEval w tym samym celu.
W rozwiązywaniu wyrażeń działa dokładnie tak samo, jak
Eval()
i obsługuje operatory takie jak%, ^, &, ** (potęga) i! (Factorial). Możesz także używać funkcji i stałych (lub powiedzmy zmiennych) wewnątrz wyrażenia. Wyrażenie jest rozwiązywane w kolejności PEMDAS, która jest powszechna w językach programowania, w tym JavaScript.var Obj = new BigEval(); var result = Obj.exec("5! + 6.6e3 * (PI + E)"); // 38795.17158152233 var result2 = Obj.exec("sin(45 * deg)**2 + cos(pi / 4)**2"); // 1 var result3 = Obj.exec("0 & -7 ^ -7 - 0%1 + 6%2"); //-7
Można również wykorzystać te biblioteki Big Number do arytmetyki, jeśli masz do czynienia z liczbami z dowolną dokładnością.
źródło
Poszukałem bibliotek JavaScript do oceny wyrażeń matematycznych i znalazłem tych dwóch obiecujących kandydatów:
JavaScript Expression Evaluator : Mniejszy i, miejmy nadzieję, lżejszy. Umożliwia wyrażenia algebraiczne, podstawienia i szereg funkcji.
mathjs : umożliwia również liczby zespolone, macierze i jednostki. Zbudowany do użytku zarówno przez JavaScript w przeglądarce, jak i Node.js.
źródło
Niedawno zrobiłem to w C # (nie
Eval()
dla nas ...), oceniając wyrażenie w odwrotnej notacji polskiej (to łatwy kawałek). Najtrudniejsza część polega na parsowaniu struny i zamianie jej na odwrotną notację polską. Użyłem algorytmu manewrowania , ponieważ jest świetny przykład na Wikipedii i pseudokodzie. Zauważyłem, że wdrożenie obu jest naprawdę proste i polecam to, jeśli nie znalazłeś jeszcze rozwiązania lub szukasz alternatyw.źródło
To jest mała funkcja, którą właśnie teraz dodałem, aby rozwiązać ten problem - buduje wyrażenie, analizując ciąg znaków po jednym znaku na raz (chociaż jest to dość szybkie). Spowoduje to pobranie dowolnego wyrażenia matematycznego (ograniczone tylko do operatorów +, -, *, /) i zwrócenie wyniku. Obsługuje również wartości ujemne i nieograniczoną liczbę operacji.
Jedyne „do zrobienia” to upewnienie się, że oblicza * & / przed + & -. Dodam tę funkcjonalność później, ale na razie robi to, czego potrzebuję ...
/** * Evaluate a mathematical expression (as a string) and return the result * @param {String} expr A mathematical expression * @returns {Decimal} Result of the mathematical expression * @example * // Returns -81.4600 * expr("10.04+9.5-1+-100"); */ function expr (expr) { var chars = expr.split(""); var n = [], op = [], index = 0, oplast = true; n[index] = ""; // Parse the expression for (var c = 0; c < chars.length; c++) { if (isNaN(parseInt(chars[c])) && chars[c] !== "." && !oplast) { op[index] = chars[c]; index++; n[index] = ""; oplast = true; } else { n[index] += chars[c]; oplast = false; } } // Calculate the expression expr = parseFloat(n[0]); for (var o = 0; o < op.length; o++) { var num = parseFloat(n[o + 1]); switch (op[o]) { case "+": expr = expr + num; break; case "-": expr = expr - num; break; case "*": expr = expr * num; break; case "/": expr = expr / num; break; } } return expr; }
źródło
Prosty i elegancki z
Function()
function parse(str) { return Function(`'use strict'; return (${str})`)() } parse("1+2+3");
źródło
) -----
() `ten nawias w końcu?parse('process.exit()')
.Możesz użyć pętli for, aby sprawdzić, czy ciąg zawiera jakiekolwiek nieprawidłowe znaki, a następnie użyć try ... catch z eval, aby sprawdzić, czy obliczenia generują błąd, taki jak
eval("2++")
by.function evaluateMath(str) { for (var i = 0; i < str.length; i++) { if (isNaN(str[i]) && !['+', '-', '/', '*', '%', '**'].includes(str[i])) { return NaN; } } try { return eval(str) } catch (e) { if (e.name !== 'SyntaxError') throw e return NaN; } } console.log(evaluateMath('2 + 6'))
lub zamiast funkcji możesz ustawić
Math.eval
Math.eval = function(str) { for (var i = 0; i < str.length; i++) { if (isNaN(str[i]) && !['+', '-', '/', '*', '%', '**'].includes(str[i])) { return NaN; } } try { return eval(str) } catch (e) { if (e.name !== 'SyntaxError') throw e return NaN; } } console.log(Math.eval('2 + 6'))
źródło
Ostatecznie zdecydowałem się na to rozwiązanie, które działa na sumowanie dodatnich i ujemnych liczb całkowitych (i po niewielkiej modyfikacji wyrażenia regularnego będzie działać również dla liczb dziesiętnych):
function sum(string) { return (string.match(/^(-?\d+)(\+-?\d+)*$/)) ? string.split('+').stringSum() : NaN; } Array.prototype.stringSum = function() { var sum = 0; for(var k=0, kl=this.length;k<kl;k++) { sum += +this[k]; } return sum; }
Nie jestem pewien, czy jest szybszy niż eval (), ale ponieważ muszę wykonywać tę operację wiele razy, jestem o wiele wygodniejszy z uruchamianiem tego skryptu niż tworzenie wielu instancji kompilatora javascript
źródło
return
nie można go użyć wewnątrz wyrażenia,sum("+1")
zwraca NaN .Spróbuj nerdamer
var result = nerdamer('12+2+PI').evaluate(); document.getElementById('text').innerHTML = result.text();
<script src="http://nerdamer.com/js/nerdamer.core.js"></script> <div id="text"></div>
źródło
Wierzę, że
parseInt
i ES6 mogą być pomocne w tej sytuacjilet func = (str) => { let arr = str.split(""); return `${Number(arr[0]) + parseInt(arr[1] + Number(arr[2]))}` }; console.log(func("1+1"));
Najważniejsze jest to, że
parseInt
analizuje liczbę z operatorem. Kod można modyfikować do odpowiednich potrzeb.źródło
Wypróbuj AutoCalculator https://github.com/JavscriptLab/autocalculate Oblicz wartość wejściową i wyjściową za pomocą wyrażeń selektora
Po prostu dodaj atrybut do danych wyjściowych, taki jak data-ac = "(# firstinput + # secondinput)"
Nie ma potrzeby żadnej inicjalizacji, wystarczy dodać tylko atrybut data-ac. Automatycznie wykryje dynamicznie dodane elementy
W przypadku dodania „Rs” z wyjściem po prostu dodaj w nawiasach klamrowych data-ac = "{Rs} (# firstinput + # secondinput)"
źródło
const operatorToFunction = { "+": (num1, num2) => +num1 + +num2, "-": (num1, num2) => +num1 - +num2, "*": (num1, num2) => +num1 * +num2, "/": (num1, num2) => +num1 / +num2 } const findOperator = (str) => { const [operator] = str.split("").filter((ch) => ["+", "-", "*", "/"].includes(ch)) return operator; } const executeOperation = (str) => { const operationStr = str.replace(/[ ]/g, ""); const operator = findOperator(operationStr); const [num1, num2] = operationStr.split(operator) return operatorToFunction[operator](num1, num2); }; const addition = executeOperation('1 + 1'); // ans is 2 const subtraction = executeOperation('4 - 1'); // ans is 3 const multiplication = executeOperation('2 * 5'); // ans is 10 const division = executeOperation('16 / 4'); // ans is 4
źródło
num
przez 1?Oto rozwiązanie algorytmiczne podobne do rozwiązania jMichaela, które przechodzi przez wyrażenie znak po znaku i stopniowo śledzi lewo / operator / prawo. Funkcja kumuluje wynik po każdej turze, w której znajduje znak operatora. Ta wersja obsługuje tylko operatory „+” i „-”, ale została napisana w celu rozszerzenia o inne operatory. Uwaga: przed zapętleniem ustawiamy „currOp” na „+”, ponieważ zakładamy, że wyrażenie zaczyna się od dodatniego pływaka. W rzeczywistości ogólnie zakładam, że dane wejściowe są podobne do tego, co pochodzi z kalkulatora.
function calculate(exp) { const opMap = { '+': (a, b) => { return parseFloat(a) + parseFloat(b) }, '-': (a, b) => { return parseFloat(a) - parseFloat(b) }, }; const opList = Object.keys(opMap); let acc = 0; let next = ''; let currOp = '+'; for (let char of exp) { if (opList.includes(char)) { acc = opMap[currOp](acc, next); currOp = char; next = ''; } else { next += char; } } return currOp === '+' ? acc + parseFloat(next) : acc - parseFloat(next); }
źródło