Як мені проаналізувати та оцінити математичний вираз у рядку (наприклад '1+1'
), не викликаючи його, eval(string)
щоб отримати його числове значення?
У цьому прикладі я хочу, щоб функція приймала '1+1'
і повертала 2
.
Відповіді:
Ви можете скористатися бібліотекою JavaScript Expression Evaluator , яка дозволяє робити такі речі:
Parser.evaluate("2 ^ x", { x: 3 });
Або mathjs , який дозволяє такі речі:
math.eval('sin(45 deg) ^ 2');
У підсумку я вибрав математику для одного зі своїх проектів.
Ви можете зробити + або - легко:
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)
)
Більш складна математика робить eval привабливішим і, звичайно, простішим у написанні.
Хтось повинен проаналізувати цей рядок. Якщо це не інтерпретатор (через eval
), то це повинні бути ви, написавши процедуру аналізу для вилучення чисел, операторів та всього іншого, що ви хочете підтримати в математичному виразі.
Отже, ні, не існує жодного (простого) способу без eval
. Якщо ви стурбовані безпекою (оскільки вхід, який ви розбираєте, не надходить із контрольованого вами джерела), можливо, ви можете перевірити формат вводу (за допомогою фільтра регулярних виразів білого списку), перш ніж передати його eval
?
Альтернатива чудовій відповіді @kennebec, використовуючи коротший регулярний вираз і дозволяючи пробіли між операторами
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;
}
Використовуйте це як
addbits('5 + 30 - 25.1 + 11');
Оновлення
Ось більш оптимізована версія
function addbits(s) {
return (s.replace(/\s/g, '').match(/[+\-]?([0-9\.]+)/g) || [])
.reduce(function(sum, value) {
return parseFloat(sum) + parseFloat(value);
});
}
Я створив BigEval з тією ж метою.
При вирішенні виразів він працює точно так само, як Eval()
і підтримує оператори, такі як%, ^, &, ** (потужність) та! (факторіал). Ви також можете використовувати функції та константи (або вимовляти змінні) всередині виразу. Вираз вирішується в порядку PEMDAS, що є загальним для мов програмування, включаючи 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
Можна також використовувати ці бібліотеки великих чисел для арифметики, якщо ви маєте справу з числами з довільною точністю.
Я шукав бібліотеки JavaScript для оцінки математичних виразів і знайшов цих двох перспективних кандидатів:
Оцінювач виразів JavaScript : менший і, сподіваюся, більш легкий. Дозволяє алгебраїчні вирази, підстановки та ряд функцій.
mathjs : Дозволяє також комплексні числа, матриці та одиниці виміру. Створений для використання як в браузері JavaScript, так і Node.js.
Нещодавно я зробив це в C # (ні Eval()
для нас ...), оцінивши вираз у зворотному польському позначенні (це простий біт). Важка частина насправді розбирає рядок і перетворює його на зворотний польський запис. Я використовував алгоритм Shunting Yard , оскільки у Вікіпедії та псевдокоді є чудовий приклад. Я виявив, що реалізувати обидва варіанти просто, і я б рекомендував це, якщо ви ще не знайшли рішення або шукаєте альтернативи.
Це невелика функція, яку я зараз зібрав для вирішення цієї проблеми - вона створює вираз, аналізуючи рядок по одному символу (хоча це насправді досить швидко). Для цього буде використано будь-який математичний вираз (обмежений лише операторами +, -, *, /) і поверне результат. Він також може обробляти негативні значення та необмежену кількість операцій.
Залишилось лише "зробити" - переконатися, що він обчислює * & / перед + & -. Додам цю функціональність пізніше, але наразі це робить те, що мені потрібно ...
/**
* 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;
}
Простий і елегантний Function()
function parse(str) {
return Function(`'use strict'; return (${str})`)()
}
parse("1+2+3");
) -----
() `нарешті ця дужка?
parse('process.exit()')
.
Ви можете використовувати цикл for, щоб перевірити, чи містить рядок якісь неприпустимі символи, а потім скористатися функцією try ... catch with eval, щоб перевірити, чи не обчислює помилка, як eval("2++")
би.
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'))
або замість функції ви можете встановити 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'))
Врешті-решт я вибрав це рішення, яке працює для підсумовування додатних і від’ємних цілих чисел (і з невеликою зміною регулярного виразу також буде працювати для десяткових знаків):
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;
}
Я не впевнений, що це швидше, ніж eval (), але оскільки мені доводиться виконувати операцію багато разів, мені набагато зручніше керувати цим сценарієм, ніж створювати навантаження екземплярів компілятора javascript
return
не може використовуватися всередині виразу, sum("+1")
повертає NaN .
Спробуйте 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>
Я вважаю , що parseInt
і ES6 може бути корисний в цій ситуації
let func = (str) => {
let arr = str.split("");
return `${Number(arr[0]) + parseInt(arr[1] + Number(arr[2]))}`
};
console.log(func("1+1"));
Тут головне, що parseInt
аналізує номер за допомогою оператора. Код може бути змінений відповідно до потреб.
Спробуйте AutoCalculator https://github.com/JavscriptLab/autocalculate Обчислити значення вхідних даних та вихідні дані, використовуючи селекторні вирази
Просто додайте атрибут для вихідного вводу, наприклад data-ac = "(# firstinput + # secondinput)"
Не потрібно ніякої ініціалізації, просто додайте лише атрибут data-ac. Він автоматично виявить динамічно додані елементи
Для того, щоб додати 'Rs' з виходом, просто додайте всередину фігурної дужки 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
на 1?
Ось алгоритмічне рішення, подібне до jMichael, яке перебирає символ виразу за символом і поступово відстежує ліворуч / оператор / праворуч. Функція накопичує результат після кожного повороту, коли вона знаходить символ оператора. Ця версія підтримує лише оператори '+' та '-', але написана для розширення з іншими операторами. Примітка: ми встановлюємо для 'currOp' значення '+' перед циклом, оскільки ми припускаємо, що вираз починається з позитивного плаваючого числа. Насправді, загалом, я роблю припущення, що вхідні дані подібні до тих, що надходять від калькулятора.
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;"))()
.