¿Cómo analizo y evalúo una expresión matemática en una cadena (por ejemplo '1+1'
) sin invocar eval(string)
para obtener su valor numérico?
Con ese ejemplo, quiero que la función acepte '1+1'
y regrese 2
.
javascript
string
math
numbers
whereresrhys
fuente
fuente
(Function("return 1+1;"))()
.Respuestas:
Puede usar la biblioteca JavaScript Expression Evaluator , que le permite hacer cosas como:
Parser.evaluate("2 ^ x", { x: 3 });
O mathjs , que permite cosas como:
math.eval('sin(45 deg) ^ 2');
Terminé eligiendo mathjs para uno de mis proyectos.
fuente
Puedes hacer + o - fácilmente:
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) )
Las matemáticas más complicadas hacen que eval sea más atractivo y, ciertamente, más simple de escribir.
fuente
Alguien tiene que analizar esa cadena. Si no es el intérprete (a través de
eval
), entonces deberá ser usted, escribiendo una rutina de análisis para extraer números, operadores y cualquier otra cosa que desee admitir en una expresión matemática.Entonces, no, no hay ninguna forma (simple) sin
eval
. Si le preocupa la seguridad (porque la entrada que está analizando no es de una fuente que controla), tal vez pueda verificar el formato de la entrada (a través de un filtro de expresiones regulares de la lista blanca) antes de pasarlaeval
.fuente
Una alternativa a la excelente respuesta de @kennebec, usando una expresión regular más corta y permitiendo espacios entre operadores
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; }
Úselo como
addbits('5 + 30 - 25.1 + 11');
Actualizar
Aquí hay una versión más optimizada
function addbits(s) { return (s.replace(/\s/g, '').match(/[+\-]?([0-9\.]+)/g) || []) .reduce(function(sum, value) { return parseFloat(sum) + parseFloat(value); }); }
fuente
He creado BigEval para el mismo propósito.
Al resolver expresiones, funciona exactamente igual que
Eval()
y admite operadores como%, ^, &, ** (potencia) y! (factorial). También se le permite usar funciones y constantes (o decir variables) dentro de la expresión. La expresión se resuelve en el orden PEMDAS, que es común en los lenguajes de programación, incluido 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
También se puede utilizar esas bibliotecas de números grandes para aritmética en caso de que se trate de números con precisión arbitraria.
fuente
Busqué bibliotecas de JavaScript para evaluar expresiones matemáticas y encontré estos dos candidatos prometedores:
Evaluador de expresiones de JavaScript : más pequeño y, con suerte, más ligero. Permite expresiones algebraicas, sustituciones y una serie de funciones.
mathjs : también permite números complejos, matrices y unidades. Diseñado para ser utilizado tanto por JavaScript en el navegador como por Node.js.
fuente
Recientemente hice esto en C # (no
Eval()
para nosotros ...) al evaluar la expresión en notación polaca inversa (eso es lo fácil). La parte difícil es analizar la cadena y convertirla en notación polaca inversa. He utilizado el algoritmo de Estación de clasificación , ya que hay un gran ejemplo de Wikipedia y pseudocódigo. Me pareció realmente sencillo implementar ambos y lo recomendaría si aún no ha encontrado una solución o está buscando alternativas.fuente
Esta es una pequeña función que reuní hace un momento para resolver este problema: construye la expresión analizando la cadena un carácter a la vez (aunque en realidad es bastante rápido). Esto tomará cualquier expresión matemática (limitada a operadores +, -, *, / solamente) y devolverá el resultado. También puede manejar valores negativos y operaciones de números ilimitados.
Lo único que queda por hacer es asegurarse de que calcula * & / antes de + & -. Agregaré esa funcionalidad más adelante, pero por ahora esto hace lo que necesito ...
/** * 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; }
fuente
Simple y elegante con
Function()
function parse(str) { return Function(`'use strict'; return (${str})`)() } parse("1+2+3");
fuente
) -----
() `este paréntesis al fin?parse('process.exit()')
.Puede usar un bucle for para verificar si la cadena contiene caracteres no válidos y luego usar un try ... catch con eval para verificar si el cálculo arroja un error como lo
eval("2++")
haría.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'))
o en lugar de una función, puede configurar
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'))
fuente
Finalmente, opté por esta solución, que funciona para sumar enteros positivos y negativos (y con una pequeña modificación en la expresión regular también funcionará para decimales):
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; }
No estoy seguro de si es más rápido que eval (), pero como tengo que realizar la operación muchas veces, me siento mucho más cómodo ejecutando este script que creando un montón de instancias del compilador javascript.
fuente
return
no se puede usar dentro de una expresión,sum("+1")
devuelve NaN .Prueba 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>
fuente
Creo que
parseInt
y ES6 puede ser útil en esta situación.let func = (str) => { let arr = str.split(""); return `${Number(arr[0]) + parseInt(arr[1] + Number(arr[2]))}`}; console.log(func("1+1"));
Lo principal aquí es que
parseInt
analiza el número con el operador. El código se puede modificar a las necesidades correspondientes.fuente
Puede usar esta biblioteca bien mantenida de Github que funciona tanto en Nodejs como en el navegador que es más rápido que otras bibliotecas alternativas proporcionadas aquí
Uso
mexp = require('math-expression-evaluator') var value = mexp.eval(exp);
Documentación completa
fuente
Pruebe AutoCalculator https://github.com/JavscriptLab/autocalculate Calcular el valor de las entradas y la salida mediante el uso de expresiones de selector
Simplemente agregue un atributo para su entrada de salida como data-ac = "(# firstinput + # secondinput)"
No se necesita ninguna inicialización, solo agregue el atributo data-ac. Descubrirá elementos agregados dinámicamente automáticamente
Para agregar 'Rs' con Salida, simplemente agregue dentro de los corchetes data-ac = "{Rs} (# firstinput + # secondinput)"
fuente
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
fuente
num
por 1?Aquí hay una solución algorítmica similar a la de jMichael que recorre la expresión carácter por carácter y rastrea progresivamente izquierda / operador / derecha. La función acumula el resultado después de cada turno que encuentra un carácter operador. Esta versión solo admite los operadores '+' y '-', pero está escrita para ampliarse con otros operadores. Nota: establecemos 'currOp' en '+' antes del bucle porque asumimos que la expresión comienza con un flotante positivo. De hecho, en general, estoy asumiendo que la entrada es similar a lo que vendría de una calculadora.
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); }
fuente