¿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
.
¿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
.
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.
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.
Alguien tiene que analizar esa cadena. Si no es el intérprete (a través deeval
), 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 pasarla eval
.
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);
});
}
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.
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.
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.
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;
}
Simple y elegante con Function()
function parse(str) {
return Function(`'use strict'; return (${str})`)()
}
parse("1+2+3");
) -----
() `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'))
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.
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>
Creo que parseInt
y ES6 puede ser útil en esta situación.
==> de esta manera:
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.
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);
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)"
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
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);
}
(Function("return 1+1;"))()
.